1 /*
2  * This file is part of cyanrip.
3  *
4  * cyanrip is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * cyanrip is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with cyanrip; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17  */
18 
19 #include <time.h>
20 #include <getopt.h>
21 #include <sys/stat.h>
22 
23 #include <libavutil/bprint.h>
24 #include <libavutil/avstring.h>
25 
26 #include "cyanrip_main.h"
27 #include "cyanrip_log.h"
28 #include "checksums.h"
29 #include "discid.h"
30 #include "musicbrainz.h"
31 #include "coverart.h"
32 #include "accurip.h"
33 #include "cyanrip_encode.h"
34 #include "os_compat.h"
35 
36 int quit_now = 0;
37 
38 const cyanrip_out_fmt crip_fmt_info[] = {
39     [CYANRIP_FORMAT_FLAC]     = { "flac",     "FLAC", "flac",  "flac",  1, 11, 1, AV_CODEC_ID_FLAC,      },
40     [CYANRIP_FORMAT_MP3]      = { "mp3",      "MP3",  "mp3",   "mp3",   1,  0, 0, AV_CODEC_ID_MP3,       },
41     [CYANRIP_FORMAT_TTA]      = { "tta",      "TTA",  "tta",   "tta",   0,  0, 1, AV_CODEC_ID_TTA,       },
42     [CYANRIP_FORMAT_OPUS]     = { "opus",     "OPUS", "opus",  "ogg",   0, 10, 0, AV_CODEC_ID_OPUS,      },
43     [CYANRIP_FORMAT_AAC]      = { "aac",      "AAC",  "m4a",   "adts",  0,  0, 0, AV_CODEC_ID_AAC,       },
44     [CYANRIP_FORMAT_AAC_MP4]  = { "aac_mp4",  "AAC",  "mp4",   "mp4",   1,  0, 0, AV_CODEC_ID_AAC,       },
45     [CYANRIP_FORMAT_WAVPACK]  = { "wavpack",  "WV",   "wv",    "wv",    0,  8, 1, AV_CODEC_ID_WAVPACK,   },
46     [CYANRIP_FORMAT_VORBIS]   = { "vorbis",   "OGG",  "ogg",   "ogg",   0,  0, 0, AV_CODEC_ID_VORBIS,    },
47     [CYANRIP_FORMAT_ALAC]     = { "alac",     "ALAC", "m4a",   "ipod",  0,  2, 1, AV_CODEC_ID_ALAC,      },
48     [CYANRIP_FORMAT_WAV]      = { "wav",      "WAV",  "wav",   "wav",   0,  0, 1, AV_CODEC_ID_NONE,      },
49     [CYANRIP_FORMAT_OPUS_MP4] = { "opus_mp4", "OPUS", "mp4",   "mp4",   1, 10, 0, AV_CODEC_ID_OPUS,      },
50     [CYANRIP_FORMAT_PCM]      = { "pcm",      "PCM",  "pcm",   "s16le", 0,  0, 1, AV_CODEC_ID_NONE,      },
51 };
52 
cyanrip_ctx_end(cyanrip_ctx ** s)53 static void cyanrip_ctx_end(cyanrip_ctx **s)
54 {
55     cyanrip_ctx *ctx;
56     if (!s || !*s)
57         return;
58     ctx = *s;
59 
60     for (int i = 0; i < ctx->nb_tracks; i++) {
61         crip_free_art(&ctx->tracks[i].art);
62         av_dict_free(&ctx->tracks[i].meta);
63         av_free(ctx->tracks[i].ar_db_entries);
64     }
65 
66     for (int i = 0; i < ctx->nb_cover_arts; i++)
67         crip_free_art(&ctx->cover_arts[i]);
68 
69     av_free(ctx->mb_submission_url);
70 
71     if (ctx->paranoia)
72         cdio_paranoia_free(ctx->paranoia);
73     if (ctx->drive)
74         cdio_cddap_close_no_free_cdio(ctx->drive);
75     if (ctx->settings.eject_on_success_rip && !ctx->total_error_count &&
76         (ctx->mcap & CDIO_DRIVE_CAP_MISC_EJECT) && ctx->cdio && !quit_now)
77         cdio_eject_media(&ctx->cdio);
78     else if (ctx->cdio)
79         cdio_destroy(ctx->cdio);
80 
81     free(ctx->settings.dev_path);
82     av_dict_free(&ctx->meta);
83     av_freep(&ctx->base_dst_folder);
84     av_freep(&ctx);
85 
86     *s = NULL;
87 }
88 
89 static const paranoia_mode_t paranoia_level_map[] = {
90     [0] = PARANOIA_MODE_DISABLE, /* Disable everything */
91     [1] = PARANOIA_MODE_OVERLAP, /* Perform overlapped reads */
92     [2] = PARANOIA_MODE_OVERLAP | PARANOIA_MODE_VERIFY, /* Perform and verify overlapped reads */
93     [3] = PARANOIA_MODE_FULL ^ PARANOIA_MODE_NEVERSKIP, /* Maximum, but do allow skipping sectors */
94 };
95 const int crip_max_paranoia_level = (sizeof(paranoia_level_map) / sizeof(paranoia_level_map[0])) - 1;
96 
cyanrip_ctx_init(cyanrip_ctx ** s,cyanrip_settings * settings)97 static int cyanrip_ctx_init(cyanrip_ctx **s, cyanrip_settings *settings)
98 {
99     cyanrip_ctx *ctx = av_mallocz(sizeof(cyanrip_ctx));
100 
101     memcpy(&ctx->settings, settings, sizeof(cyanrip_settings));
102 
103     if (ctx->settings.print_info_only)
104         ctx->settings.eject_on_success_rip = 0;
105 
106     cdio_init();
107 
108     if (!ctx->settings.dev_path)
109         ctx->settings.dev_path = cdio_get_default_device(NULL);
110 
111     if (!(ctx->cdio = cdio_open(ctx->settings.dev_path, DRIVER_UNKNOWN))) {
112         cyanrip_log(ctx, 0, "Unable to init cdio context\n");
113         return AVERROR(EINVAL);
114     }
115 
116     cdio_get_drive_cap(ctx->cdio, &ctx->rcap, &ctx->wcap, &ctx->mcap);
117 
118     char *msg = NULL;
119     if (!(ctx->drive = cdio_cddap_identify_cdio(ctx->cdio, CDDA_MESSAGE_LOGIT, &msg))) {
120         cyanrip_log(ctx, 0, "Unable to init cddap context!\n");
121         if (msg) {
122             cyanrip_log(ctx, 0, "cdio: \"%s\"\n", msg);
123             cdio_cddap_free_messages(msg);
124         }
125         return AVERROR(EINVAL);
126     }
127 
128     if (msg) {
129         cyanrip_log(ctx, 0, "%s\n", msg);
130         cdio_cddap_free_messages(msg);
131     }
132 
133     cyanrip_log(ctx, 0, "Opening drive...\n");
134     int ret = cdio_cddap_open(ctx->drive);
135     if (ret < 0) {
136         cyanrip_log(ctx, 0, "Unable to open device!\n");
137         cyanrip_ctx_end(&ctx);
138         return AVERROR(EINVAL);
139     }
140 
141     cdio_cddap_verbose_set(ctx->drive, CDDA_MESSAGE_LOGIT, CDDA_MESSAGE_FORGETIT);
142 
143     if (settings->speed) {
144         if (!(ctx->mcap & CDIO_DRIVE_CAP_MISC_SELECT_SPEED)) {
145             cyanrip_log(ctx, 0, "Device does not support changing speeds!\n");
146             cyanrip_ctx_end(&ctx);
147             return AVERROR(EINVAL);
148         }
149 
150         ret = cdio_cddap_speed_set(ctx->drive, settings->speed);
151         msg = cdio_cddap_errors(ctx->drive);
152         if (msg) {
153             cyanrip_log(ctx, 0, "cdio error: %s\n", msg);
154             cdio_cddap_free_messages(msg);
155         }
156         if (ret)
157             return AVERROR(EINVAL);
158     }
159 
160     ctx->paranoia = cdio_paranoia_init(ctx->drive);
161     if (!ctx->paranoia) {
162         cyanrip_log(ctx, 0, "Unable to init paranoia!\n");
163         cyanrip_ctx_end(&ctx);
164         return AVERROR(EINVAL);
165     }
166 
167     cdio_paranoia_modeset(ctx->paranoia, paranoia_level_map[settings->paranoia_level]);
168 
169     ctx->start_lsn = 0;
170 
171     ctx->end_lsn = cdio_get_track_lsn(ctx->cdio, CDIO_CDROM_LEADOUT_TRACK) - 1;
172     ctx->duration_frames = ctx->end_lsn - ctx->start_lsn + 1;
173 
174     ctx->nb_tracks = ctx->nb_cd_tracks = cdio_cddap_tracks(ctx->drive);
175     if ((ctx->nb_tracks < 1) || (ctx->nb_tracks > CDIO_CD_MAX_TRACKS)) {
176         cyanrip_log(ctx, 0, "Invalid number of tracks: %i!\n", ctx->nb_tracks);
177         ctx->nb_tracks = 0;
178         cyanrip_ctx_end(&ctx);
179         return AVERROR(EINVAL);
180     }
181 
182     int first_track_nb = cdio_get_first_track_num(ctx->cdio);
183     for (int i = 0; i < ctx->nb_cd_tracks; i++) {
184         cyanrip_track *t = &ctx->tracks[i];
185 
186         t->number = t->cd_track_number = i + first_track_nb;
187         t->track_is_data = !cdio_cddap_track_audiop(ctx->drive, t->number);
188         t->pregap_lsn = cdio_get_track_pregap_lsn(ctx->cdio, t->number);
189         t->start_lsn = cdio_get_track_lsn(ctx->cdio, t->number);
190         t->end_lsn = cdio_get_track_last_lsn(ctx->cdio, t->number);
191 
192         t->start_lsn_sig = t->start_lsn;
193         t->end_lsn_sig = t->end_lsn;
194 
195         if (t->track_is_data) {
196             if (ctx->settings.pregap_action[t->number - 1] == CYANRIP_PREGAP_DEFAULT)
197                 ctx->settings.pregap_action[t->number - 1] = CYANRIP_PREGAP_DROP;
198             if (ctx->settings.pregap_action[t->number - 0] == CYANRIP_PREGAP_DEFAULT)
199                 ctx->settings.pregap_action[t->number - 0] = CYANRIP_PREGAP_DROP;
200             if (i == (ctx->nb_cd_tracks - 1)) {
201                 ctx->tracks[i - 1].end_lsn -= 11400;
202                 t->pregap_lsn = ctx->tracks[i - 1].end_lsn + 1;
203             }
204         }
205     }
206 
207     for (int i = 0; i < ctx->nb_cd_tracks; i++) {
208         cyanrip_track *t = &ctx->tracks[i];
209         if (t->track_is_data)
210             continue;
211         t->acurip_track_is_first = 1;
212         break;
213     }
214 
215     for (int i = ctx->nb_cd_tracks - 1; i >= 0; i--) {
216         cyanrip_track *t = &ctx->tracks[i];
217         if (t->track_is_data)
218             continue;
219         t->acurip_track_is_last = 1;
220         break;
221     }
222 
223     /* For hot removal detection - init this so we can detect changes */
224     cdio_get_media_changed(ctx->cdio);
225 
226     *s = ctx;
227     return 0;
228 }
229 
crip_fill_mcn(cyanrip_ctx * ctx)230 static void crip_fill_mcn(cyanrip_ctx *ctx)
231 {
232     /* Get disc MCN */
233     if (ctx->rcap & CDIO_DRIVE_CAP_READ_MCN) {
234         const char *mcn = cdio_get_mcn(ctx->cdio);
235         if (mcn) {
236             if (strlen(mcn))
237                 av_dict_set(&ctx->meta, "disc_mcn", mcn, 0);
238             cdio_free((void *)mcn);
239         }
240     }
241 }
242 
track_set_creation_time(cyanrip_ctx * ctx,cyanrip_track * t)243 static void track_set_creation_time(cyanrip_ctx *ctx, cyanrip_track *t)
244 {
245     if (dict_get(t->meta, "creation_time"))
246         return;
247 
248     char t_s[64];
249     time_t t_c = time(NULL);
250     struct tm *t_l = localtime(&t_c);
251     strftime(t_s, sizeof(t_s), "%Y-%m-%dT%H:%M:%S", t_l);
252     av_dict_set(&t->meta, "creation_time", t_s, 0);
253 }
254 
copy_album_to_track_meta(cyanrip_ctx * ctx)255 static void copy_album_to_track_meta(cyanrip_ctx *ctx)
256 {
257     for (int i = 0; i < ctx->nb_tracks; i++) {
258         av_dict_set(&ctx->tracks[i].meta, "comment", "cyanrip "PROJECT_VERSION_STRING, 0);
259         av_dict_set_int(&ctx->tracks[i].meta, "track", ctx->tracks[i].number, 0);
260         av_dict_set_int(&ctx->tracks[i].meta, "tracktotal", ctx->nb_tracks, 0);
261         av_dict_copy(&ctx->tracks[i].meta, ctx->meta, AV_DICT_DONT_OVERWRITE);
262     }
263 }
264 
265 static const uint8_t silent_frame[CDIO_CD_FRAMESIZE_RAW] = { 0 };
266 
267 uint64_t paranoia_status[PARANOIA_CB_FINISHED + 1] = { 0 };
268 
status_cb(long int n,paranoia_cb_mode_t status)269 static void status_cb(long int n, paranoia_cb_mode_t status)
270 {
271     if (status >= PARANOIA_CB_READ && status <= PARANOIA_CB_FINISHED)
272         paranoia_status[status]++;
273 }
274 
cyanrip_read_frame(cyanrip_ctx * ctx)275 static const uint8_t *cyanrip_read_frame(cyanrip_ctx *ctx)
276 {
277     int err = 0;
278     char *msg = NULL;
279 
280     const uint8_t *data;
281     data = (void *)cdio_paranoia_read_limited(ctx->paranoia, &status_cb,
282                                               ctx->settings.frame_max_retries);
283 
284     msg = cdio_cddap_errors(ctx->drive);
285     if (msg) {
286         cyanrip_log(ctx, 0, "\ncdio error: %s\n", msg);
287         cdio_cddap_free_messages(msg);
288         err = 1;
289     }
290 
291     if (!data) {
292         if (!msg) {
293             cyanrip_log(ctx, 0, "\nFrame read failed!\n");
294             err = 1;
295         }
296         data = silent_frame;
297     }
298 
299     ctx->total_error_count += err;
300 
301     return data;
302 }
303 
search_for_offset(cyanrip_track * t,int * offset_found,const uint8_t * mem,int dir,int guess,int bytes)304 static int search_for_offset(cyanrip_track *t, int *offset_found,
305                              const uint8_t *mem, int dir,
306                              int guess, int bytes)
307 {
308     if (guess) {
309         const uint8_t *start_addr = mem + guess*4;
310         uint32_t accurip_v1 = 0x0;
311         for (int j = 0; j < (CDIO_CD_FRAMESIZE_RAW >> 2); j++)
312             accurip_v1 += AV_RL32(&start_addr[j*4]) * (j + 1);
313         if (crip_find_ar(t, accurip_v1, 1) == t->ar_db_max_confidence && accurip_v1) {
314             *offset_found = guess;
315             return 1;
316         }
317     }
318 
319     for (int byte_off = ((dir < 0) * 4); byte_off < bytes; byte_off += 4) {
320         if (quit_now)
321             return 0;
322 
323         int offset = dir * (byte_off >> 2);
324         if (guess == offset)
325             continue;
326 
327         const uint8_t *start_addr = mem + dir * byte_off;
328         uint32_t accurip_v1 = 0x0;
329         for (int j = 0; j < (CDIO_CD_FRAMESIZE_RAW >> 2); j++)
330             accurip_v1 += AV_RL32(&start_addr[j*4]) * (j + 1);
331         if (crip_find_ar(t, accurip_v1, 1) == t->ar_db_max_confidence && accurip_v1) {
332             *offset_found = offset;
333             return 1;
334         }
335     }
336 
337     return 0;
338 }
339 
search_for_drive_offset(cyanrip_ctx * ctx,int range)340 static void search_for_drive_offset(cyanrip_ctx *ctx, int range)
341 {
342     int had_ar = 0, did_check = 0;
343     int offset_found = 0, offset_found_samples = 0;
344     uint8_t *mem = av_malloc(2 * range * CDIO_CD_FRAMESIZE_RAW);
345 
346     if (ctx->ar_db_status != CYANRIP_ACCUDB_FOUND)
347         goto end;
348 
349     for (int t_idx = 0; t_idx < ctx->nb_tracks; t_idx++) {
350         cyanrip_track *t = &ctx->tracks[t_idx];
351         lsn_t start = cdio_get_track_lsn(ctx->cdio, t_idx + 1);
352         lsn_t end = cdio_get_track_last_lsn(ctx->cdio, t_idx + 1);
353 
354         if (ctx->tracks[t_idx].ar_db_status != CYANRIP_ACCUDB_FOUND)
355             continue;
356         else
357             had_ar |= 1;
358 
359         if ((end - start) < (450 + range))
360             continue;
361 
362         did_check |= 1;
363 
364         /* Set start/end ranges */
365         start += 450 - range;
366         end = start + 2*range;
367 
368         size_t bytes = 0;
369 
370         cyanrip_log(ctx, 0, "Loading data for track %i...\n", t_idx + 1);
371         cdio_paranoia_seek(ctx->paranoia, start, SEEK_SET);
372         for (int i = 0; i < 2*range; i++) {
373             const uint8_t *data = cyanrip_read_frame(ctx);
374             memcpy(mem + bytes, data, CDIO_CD_FRAMESIZE_RAW);
375             bytes += CDIO_CD_FRAMESIZE_RAW;
376             if (quit_now) {
377                 cyanrip_log(ctx, 0, "Stopping, offset finding incomplete!\n");
378                 goto end;
379             }
380         }
381 
382         int found, offset;
383         int dir = (offset_found && (offset_found_samples < 0)) ? -1 : +1;
384 
385         cyanrip_log(ctx, 0, "Data loaded, searching for offsets...\n");
386 
387         found = search_for_offset(t, &offset, mem + (bytes >> 1), dir, offset_found_samples,
388                                   range * CDIO_CD_FRAMESIZE_RAW);
389         if (!found)
390             found = search_for_offset(t, &offset, mem + (bytes >> 1), -dir, 0,
391                                       range * CDIO_CD_FRAMESIZE_RAW);
392 
393         if (!found) {
394             cyanrip_log(ctx, 0, "Nothing found for track %i%s\n", t_idx + 1,
395                         t_idx != (ctx->nb_tracks - 1) ? ", trying another track" : "");
396         } else if (!offset_found) {
397             offset_found_samples = offset;
398             offset_found++;
399             cyanrip_log(ctx, 0, "Offset of %c%i found in track %i%s\n",
400                         offset >= 0 ? '+' : '-', abs(offset), t_idx + 1,
401                         t_idx != (ctx->nb_tracks - 1) ? ", trying to confirm with another track" : "");
402         } else if (offset_found_samples == offset) {
403             offset_found++;
404             cyanrip_log(ctx, 0, "Offset of %c%i confirmed (confidence: %i) in track %i%s\n",
405                         offset >= 0 ? '+' : '-', abs(offset), offset_found, t_idx + 1,
406                         t_idx != (ctx->nb_tracks - 1) ? ", trying to confirm with another track" : "");
407         } else {
408             cyanrip_log(ctx, 0, "New offset of %c%i found at track %i, scrapping old offset of %c%i%s\n",
409                         offset >= 0 ? '+' : '-', abs(offset), t_idx + 1,
410                         offset_found_samples >= 0 ? '+' : '-', abs(offset_found_samples),
411                         t_idx != (ctx->nb_tracks - 1) ? ", trying to confirm with another track" : "");
412             offset_found_samples = offset;
413             offset_found = 1;
414         }
415     }
416 
417 end:
418     av_free(mem);
419 
420     if (!offset_found) {
421         if (!had_ar) {
422             cyanrip_log(ctx, 0, "No track had AccuRip entry, cannot find offset!\n");
423         } else if (had_ar && !did_check) {
424             cyanrip_log(ctx, 0, "No track was long enough, unable to find drive offset!\n");
425         } else {
426             cyanrip_log(ctx, 0, "Was not able to find drive offset with a radius of %i frames"
427                         ", trying again with a larger radius...\n", range);
428             search_for_drive_offset(ctx, 2*range);
429         }
430         return;
431     } else {
432         cyanrip_log(ctx, 0, "Drive offset of %c%i found (confidence: %i)!\n",
433                     offset_found_samples >= 0 ? '+' : '-', abs(offset_found_samples), offset_found);
434     }
435 }
436 
track_read_extra(cyanrip_ctx * ctx,cyanrip_track * t)437 static void track_read_extra(cyanrip_ctx *ctx, cyanrip_track *t)
438 {
439     if (!t->track_is_data) {
440         t->preemphasis = cdio_cddap_track_preemp(ctx->drive, t->cd_track_number);
441 
442         if (!ctx->disregard_cd_isrc && (ctx->rcap & CDIO_DRIVE_CAP_READ_ISRC) && !dict_get(t->meta, "isrc")) {
443             const char *isrc_str = cdio_get_track_isrc(ctx->cdio, t->cd_track_number);
444             if (isrc_str) {
445                 if (strlen(isrc_str))
446                     av_dict_set(&t->meta, "isrc", isrc_str, 0);
447                 else
448                     ctx->disregard_cd_isrc = 1;
449                 cdio_free((void *)isrc_str);
450             } else {
451                 ctx->disregard_cd_isrc = 1;
452             }
453         }
454     }
455 }
456 
cyanrip_rip_track(cyanrip_ctx * ctx,cyanrip_track * t)457 static int cyanrip_rip_track(cyanrip_ctx *ctx, cyanrip_track *t)
458 {
459     int ret = 0;
460 
461     const int frames_before_disc_start = t->frames_before_disc_start;
462     const int frames = t->frames;
463     const int frames_after_disc_end = t->frames_after_disc_end;
464     const ptrdiff_t offs = t->partial_frame_byte_offs;
465 
466     if (t->track_is_data) {
467         cyanrip_log(ctx, 0, "Track %i is data:\n", t->number);
468         cyanrip_log_track_end(ctx, t);
469         return 0;
470     }
471 
472     /* Hopefully reduce seeking by reading this here */
473     track_read_extra(ctx, t);
474 
475     cdio_paranoia_seek(ctx->paranoia, t->start_lsn, SEEK_SET);
476 
477     /* Set creation time at the start of ripping */
478     track_set_creation_time(ctx, t);
479 
480     cyanrip_dec_ctx *dec_ctx = { NULL };
481     cyanrip_enc_ctx *enc_ctx[CYANRIP_FORMATS_NB] = { NULL };
482     ret = cyanrip_create_dec_ctx(ctx, &dec_ctx, t);
483     if (ret < 0) {
484         cyanrip_log(ctx, 0, "Error initializing decoder!\n");
485         goto fail;
486     }
487     for (int i = 0; i < ctx->settings.outputs_num; i++) {
488         ret = cyanrip_init_track_encoding(ctx, &enc_ctx[i], dec_ctx, t,
489                                           ctx->settings.outputs[i]);
490         if (ret < 0) {
491             cyanrip_log(ctx, 0, "Error initializing encoder!\n");
492             goto fail;
493         }
494     }
495 
496     int start_err = ctx->total_error_count;
497 
498     /* Checksum */
499     cyanrip_checksum_ctx checksum_ctx;
500     crip_init_checksum_ctx(ctx, &checksum_ctx, t);
501 
502     /* Fill with silence to maintain track length */
503     for (int i = 0; i < frames_before_disc_start; i++) {
504         int bytes = CDIO_CD_FRAMESIZE_RAW;
505         const uint8_t *data = silent_frame;
506 
507         if (!i && offs) {
508             data += CDIO_CD_FRAMESIZE_RAW + offs;
509             bytes = -offs;
510         }
511 
512         crip_process_checksums(&checksum_ctx, data, bytes);
513         ret = cyanrip_send_pcm_to_encoders(ctx, enc_ctx, ctx->settings.outputs_num,
514                                            dec_ctx, data, bytes);
515         if (ret) {
516             cyanrip_log(ctx, 0, "Error in decoding/sending frame!\n");
517             goto fail;
518         }
519     }
520 
521     /* Read the actual CD data */
522     for (int i = 0; i < frames; i++) {
523         /* Detect disc removals */
524         if (cdio_get_media_changed(ctx->cdio)) {
525             cyanrip_log(ctx, 0, "\nDrive media changed, stopping!\n");
526             ret = AVERROR(EINVAL);
527             goto fail;
528         }
529 
530         /* Flush paranoia cache if overreading into lead-out - no idea why */
531         if ((t->start_lsn + i) > ctx->end_lsn)
532             cdio_paranoia_seek(ctx->paranoia, t->start_lsn + i, SEEK_SET);
533 
534         int bytes = CDIO_CD_FRAMESIZE_RAW;
535         const uint8_t *data = cyanrip_read_frame(ctx);
536 
537         /* Account for partial frames caused by the offset */
538         if (offs > 0) {
539             if (!i) {
540                 data  += offs;
541                 bytes -= offs;
542             } else if ((i == (frames - 1)) && !frames_after_disc_end) {
543                 bytes = offs;
544             }
545         } else if (offs < 0) {
546             if (!i && !frames_before_disc_start) {
547                 data += CDIO_CD_FRAMESIZE_RAW + offs;
548                 bytes = -offs;
549             } else if (i == (frames - 1)) {
550                 bytes += offs;
551             }
552         }
553 
554         /* Stop now if requested */
555         if (quit_now) {
556             cyanrip_log(ctx, 0, "\nStopping, ripping incomplete!\n");
557             break;
558         }
559 
560         /* Update checksums */
561         crip_process_checksums(&checksum_ctx, data, bytes);
562 
563         /* Decode and encode */
564         ret = cyanrip_send_pcm_to_encoders(ctx, enc_ctx, ctx->settings.outputs_num,
565                                            dec_ctx, data, bytes);
566         if (ret < 0) {
567             cyanrip_log(ctx, 0, "\nError in decoding/sending frame!\n");
568             goto fail;
569         }
570 
571         /* Report progress */
572         cyanrip_log(NULL, 0, "\rRipping and encoding track %i, progress - %0.2f%%",
573                     t->number, ((double)(i + 1)/frames)*100.0f);
574         if (ctx->total_error_count - start_err)
575             cyanrip_log(NULL, 0, ", errors - %i", ctx->total_error_count - start_err);
576     }
577 
578     /* Fill with silence to maintain track length */
579     for (int i = 0; i < frames_after_disc_end; i++) {
580         int bytes = CDIO_CD_FRAMESIZE_RAW;
581         const uint8_t *data = silent_frame;
582 
583         if ((i == (frames_after_disc_end - 1)) && offs)
584             bytes = offs;
585 
586         crip_process_checksums(&checksum_ctx, data, bytes);
587         ret = cyanrip_send_pcm_to_encoders(ctx, enc_ctx, ctx->settings.outputs_num,
588                                            dec_ctx, data, bytes);
589         if (ret) {
590             cyanrip_log(ctx, 0, "Error in decoding/sending frame!\n");
591             goto fail;
592         }
593     }
594 
595     crip_finalize_checksums(&checksum_ctx, t);
596 
597     cyanrip_log(NULL, 0, "\nFlushing encoders...\n");
598 
599     /* Flush encoders */
600     ret = cyanrip_send_pcm_to_encoders(ctx, enc_ctx, ctx->settings.outputs_num,
601                                        dec_ctx, NULL, 0);
602     if (ret)
603         cyanrip_log(ctx, 0, "Error sending flush signal to encoders!\n");
604 
605 fail:
606     for (int i = 0; i < ctx->settings.outputs_num; i++) {
607         int err = cyanrip_end_track_encoding(&enc_ctx[i]);
608         if (err) {
609             cyanrip_log(ctx, 0, "Error in encoding!\n");
610             ret = err;
611             break;
612         }
613     }
614 
615     if (!ret && !quit_now)
616         cyanrip_log(ctx, 0, "Track %i ripped and encoded successfully!\n", t->number);
617 
618     cyanrip_free_dec_ctx(ctx, &dec_ctx);
619 
620     if (!ret)
621         cyanrip_log_track_end(ctx, t);
622     else
623         ctx->total_error_count++;
624 
625     return ret;
626 }
627 
on_quit_signal(int signo)628 static void on_quit_signal(int signo)
629 {
630     if (quit_now) {
631         cyanrip_log(NULL, 0, "Force quitting\n");
632         exit(1);
633     }
634     cyanrip_log(NULL, 0, "\r\nTrying to quit\n");
635     quit_now = 1;
636 }
637 
setup_track_lsn(cyanrip_ctx * ctx,cyanrip_track * t)638 static void setup_track_lsn(cyanrip_ctx *ctx, cyanrip_track *t)
639 {
640     lsn_t first_frame = t->start_lsn;
641     lsn_t last_frame  = t->end_lsn;
642 
643     /* Duration doesn't depend on adjustments we make to frames */
644     int frames = last_frame - first_frame + 1;
645 
646     t->nb_samples = frames*(CDIO_CD_FRAMESIZE_RAW >> 2);
647 
648     /* Move the seek position coarsely */
649     const int extra_frames = ctx->settings.over_under_read_frames;
650     int sign = (extra_frames < 0) ? -1 : ((extra_frames > 0) ? +1 : 0);
651     first_frame += sign*FFMAX(FFABS(extra_frames) - 1, 0);
652     last_frame += sign*FFMAX(FFABS(extra_frames) - 1, 0);
653 
654     /* Bump the lower/higher frame in the offset direction */
655     first_frame -= sign < 0;
656     last_frame  += sign > 0;
657 
658     /* Don't read into the lead in/out */
659     if (!ctx->settings.overread_leadinout) {
660         t->frames_before_disc_start = FFMAX(ctx->start_lsn - first_frame, 0);
661         t->frames_after_disc_end = FFMAX(last_frame - ctx->end_lsn, 0);
662 
663         first_frame += t->frames_before_disc_start;
664         last_frame  -= t->frames_after_disc_end;
665     } else {
666         t->frames_before_disc_start = 0;
667         t->frames_after_disc_end = 0;
668     }
669 
670     /* Offset accounted start/end sectors */
671     t->start_lsn = first_frame;
672     t->end_lsn = last_frame;
673     t->frames = last_frame - first_frame + 1;
674 
675     /* Last/first frame partial offset */
676     ptrdiff_t offs = ctx->settings.offset*4;
677     offs -= sign*FFMAX(FFABS(extra_frames) - 1, 0)*CDIO_CD_FRAMESIZE_RAW;
678 
679     t->partial_frame_byte_offs = offs;
680 }
681 
setup_track_offsets_and_report(cyanrip_ctx * ctx)682 static void setup_track_offsets_and_report(cyanrip_ctx *ctx)
683 {
684     int gaps = 0;
685 
686     cyanrip_log(ctx, 0, "Gaps:\n");
687 
688     /* Before pregap */
689     if (ctx->tracks[0].pregap_lsn != CDIO_INVALID_LSN &&
690         ctx->tracks[0].pregap_lsn > ctx->start_lsn) {
691         cyanrip_log(ctx, 0, "    %i frame gap between lead-in and track 1 pregap, merging into pregap\n",
692                     ctx->tracks[0].pregap_lsn - ctx->start_lsn);
693         gaps++;
694 
695         ctx->tracks[0].pregap_lsn = ctx->start_lsn;
696     } else if (ctx->tracks[0].pregap_lsn == CDIO_INVALID_LSN &&
697                ctx->tracks[0].start_lsn > ctx->start_lsn) {
698         cyanrip_log(ctx, 0, "    %i frame unmarked gap between lead-in and track 1, marking as a pregap\n",
699                     ctx->tracks[0].start_lsn - ctx->start_lsn);
700         gaps++;
701 
702         ctx->tracks[0].pregap_lsn = ctx->start_lsn;
703     }
704 
705     /* Pregaps */
706     for (int i = 0; i < ctx->nb_tracks; i++) {
707         cyanrip_track *ct = &ctx->tracks[i - 0];
708         cyanrip_track *lt = ct->number > 1 ? &ctx->tracks[i - 1] : NULL;
709 
710         if (ct->pregap_lsn == CDIO_INVALID_LSN)
711             continue;
712 
713         cyanrip_log(ctx, 0, "    %i frame pregap in track %i, ",
714                     ct->start_lsn - ct->pregap_lsn, ct->number);
715         gaps++;
716 
717         switch (ctx->settings.pregap_action[ct->number - 1]) {
718         case CYANRIP_PREGAP_DEFAULT:
719             if (!lt)
720                 cyanrip_log(ctx, 0, "unmerged\n");
721             else
722                 cyanrip_log(ctx, 0, "merging into track %i\n", lt->number);
723             break;
724         case CYANRIP_PREGAP_DROP:
725             cyanrip_log(ctx, 0, "dropping\n");
726             if (lt)
727                 lt->end_lsn = ct->pregap_lsn - 1;
728             break;
729         case CYANRIP_PREGAP_MERGE:
730             cyanrip_log(ctx, 0, "merging\n");
731             ct->start_lsn = ct->pregap_lsn;
732             if (lt)
733                 lt->end_lsn = ct->pregap_lsn - 1;
734             break;
735         case CYANRIP_PREGAP_TRACK:
736             cyanrip_log(ctx, 0, "splitting off into a new track, number %i\n", ct->number > 1 ? ct->number : 0);
737 
738             if (lt)
739                 lt->end_lsn = ct->pregap_lsn - 1;
740 
741             if (ct->number > 1) /* Push all track numbers up if needed */
742                 for (int j = i; j < ctx->nb_tracks; j++)
743                     ctx->tracks[j].number++;
744 
745             memmove(&ctx->tracks[i + 1], &ctx->tracks[i],
746                     sizeof(cyanrip_track)*(ctx->nb_tracks - i));
747 
748             ct = &ctx->tracks[i + 1];
749 
750             ctx->nb_tracks++;
751             cyanrip_track *nt = &ctx->tracks[i];
752 
753             memset(nt, 0, sizeof(*nt));
754 
755             nt->number = ct->number - 1;
756             nt->pregap_lsn = CDIO_INVALID_LSN;
757             nt->start_lsn = ct->pregap_lsn;
758             nt->end_lsn = ct->start_lsn - 1;
759             nt->cd_track_number = ct->cd_track_number;
760 
761             ct->pregap_lsn = CDIO_INVALID_LSN;
762         }
763     }
764 
765     /* Between tracks */
766     for (int i = 1; i < ctx->nb_tracks; i++) {
767         cyanrip_track *ct = &ctx->tracks[i - 0];
768         cyanrip_track *lt = &ctx->tracks[i - 1];
769 
770         if (ct->start_lsn == (lt->end_lsn + 1))
771             continue;
772 
773         int discont_frames = ct->start_lsn - lt->end_lsn;
774 
775         cyanrip_log(ctx, 0, "    %i frame discontinuity between tracks %i and %i, ",
776                     discont_frames, ct->number, lt->number);
777         gaps++;
778 
779         if (ctx->settings.pregap_action[ct->number - 1] != CYANRIP_PREGAP_DROP) {
780             cyanrip_log(ctx, 0, "padding track %i\n", lt->number);
781             lt->end_lsn = ct->start_lsn - 1;
782         } else {
783             cyanrip_log(ctx, 0, "ignoring\n");
784         }
785     }
786 
787     /* After last track */
788     cyanrip_track *lt = &ctx->tracks[ctx->nb_tracks - 1];
789     if (ctx->end_lsn > lt->end_lsn && !lt->track_is_data) {
790         int discont_frames = ctx->end_lsn - lt->end_lsn;
791         cyanrip_log(ctx, 0, "    %i frame gap between last track and lead-out, padding track\n",
792                     discont_frames);
793         gaps++;
794 
795         lt->end_lsn = ctx->end_lsn;
796     }
797 
798     /* Finally set up the internals with the set start_lsn/end_lsn */
799     for (int i = 0; i < ctx->nb_tracks; i++) {
800         cyanrip_track *t = &ctx->tracks[i];
801 
802         if (t->track_is_data)
803             t->frames = t->end_lsn - t->start_lsn + 1;
804         else
805             setup_track_lsn(ctx, t);
806     }
807 
808     cyanrip_log(ctx, 0, "%s\n", gaps ? "" : "    None signalled\n");
809 }
810 
811 /* Key 1 and 2 must be set, and src will be modified */
append_missing_keys(char * src,const char * key1,const char * key2)812 static char *append_missing_keys(char *src, const char *key1, const char *key2)
813 {
814     /* Copy string with enough space to append extra */
815     char *copy = av_mallocz(strlen(src) + strlen(key1) + strlen(key2) + 1);
816     memcpy(copy, src, strlen(src));
817 
818     int add_key1_offset = -1;
819     int add_key2_offset = -1;
820 
821     /* Look for keyless entries */
822     int count = 0;
823     char *p_save, *p = av_strtok(src, ":", &p_save);
824     while (p) {
825         if (!strstr(p, "=")) {
826             if (count == 0)
827                 add_key1_offset = p - src;
828             else if (count == 1)
829                 add_key2_offset = p - src;
830         }
831         p = av_strtok(NULL, ":", &p_save);
832         if (++count >= 2)
833             break;
834     }
835 
836     /* Prepend key1 if missing */
837     if (add_key1_offset >= 0) {
838         memmove(&copy[add_key1_offset + strlen(key1)], &copy[add_key1_offset], strlen(copy) - add_key1_offset);
839         memcpy(&copy[add_key1_offset], key1, strlen(key1));
840         if (add_key2_offset >= 0)
841             add_key2_offset += strlen(key1);
842     }
843 
844     /* Prepend key2 if missing */
845     if (add_key2_offset >= 0) {
846         memmove(&copy[add_key2_offset + strlen(key2)], &copy[add_key2_offset], strlen(copy) - add_key2_offset);
847         memcpy(&copy[add_key2_offset], key2, strlen(key2));
848     }
849 
850     return copy;
851 }
852 
crip_is_integer(const char * src)853 static inline int crip_is_integer(const char *src)
854 {
855     for (int i = 0; i < strlen(src); i++)
856         if (!av_isdigit(src[i]))
857             return 0;
858     return 1;
859 }
860 
add_to_dir_list(char *** dir_list,int * dir_list_nb,const char * src)861 static int add_to_dir_list(char ***dir_list, int *dir_list_nb, const char *src)
862 {
863     int nb = *dir_list_nb;
864     char **new_ptr = av_realloc(*dir_list, (nb + 1) * sizeof(*new_ptr));
865     if (!new_ptr)
866         return AVERROR(ENOMEM);
867 
868     new_ptr[nb] = av_strdup(src);
869     nb++;
870 
871     *dir_list = new_ptr;
872     *dir_list_nb = nb;
873 
874     return 0;
875 }
876 
877 struct CRIPCharReplacement {
878     const char from;
879     const char to;
880     const char to_u[5];
881     int is_avail_locally;
882 } crip_char_replacement[] = {
883     { '<', '_', "‹", HAS_CH_LESS },
884     { '>', '_', "›", HAS_CH_MORE },
885     { ':', '_', "∶", HAS_CH_COLUMN },
886     { '|', '_', "│", HAS_CH_OR },
887     { '?', '_', "?", HAS_CH_Q },
888     { '*', '_', "∗", HAS_CH_ANY },
889     { '/', '_', "∕", HAS_CH_FWDSLASH },
890     { '\'', '_', "⧹", HAS_CH_BWDSLASH },
891     { '"', '\'', "“", HAS_CH_QUOTES },
892     { '"', '\'', "”", HAS_CH_QUOTES },
893     { 0 },
894 };
895 
crip_bprint_sanitize(cyanrip_ctx * ctx,AVBPrint * buf,const char * str,char *** dir_list,int * dir_list_nb,int sanitize_fwdslash)896 static int crip_bprint_sanitize(cyanrip_ctx *ctx, AVBPrint *buf, const char *str,
897                                 char ***dir_list, int *dir_list_nb,
898                                 int sanitize_fwdslash)
899 {
900     int32_t cp, ret, quote_match = 0;
901     const char *pos = str, *end = str + strlen(str);
902 
903     int os_sanitize = (ctx->settings.sanitize_method == CRIP_SANITIZE_OS_SIMPLE) ||
904                       (ctx->settings.sanitize_method == CRIP_SANITIZE_OS_UNICODE);
905 
906     while (str < end) {
907         ret = av_utf8_decode(&cp, (const uint8_t **)&str, end, AV_UTF8_FLAG_ACCEPT_ALL);
908         if (ret < 0) {
909             cyanrip_log(ctx, 0, "Error parsing string: %s!\n", av_err2str(ret));
910             return ret;
911         }
912 
913         struct CRIPCharReplacement *rep = NULL;
914         for (int i = 0; crip_char_replacement[i].from; i++) {
915             if (cp == crip_char_replacement[i].from) {
916                 int is_quote = crip_char_replacement[i].from == '"';
917                 rep = &crip_char_replacement[i + (is_quote && quote_match)];
918                 quote_match = (quote_match + 1) & 1;
919                 break;
920             }
921         }
922 
923         int skip = !rep;
924         int skip_sanitation = rep && (os_sanitize && rep->is_avail_locally);
925         int passthrough_slash = rep && !skip_sanitation && (rep->from == OS_DIR_CHAR && !sanitize_fwdslash);
926 
927         if (skip || skip_sanitation || passthrough_slash) {
928             if (passthrough_slash)
929                 add_to_dir_list(dir_list, dir_list_nb, buf->str);
930             av_bprint_append_data(buf, pos, str - pos);
931             pos = str;
932             continue;
933         } else if (ctx->settings.sanitize_method == CRIP_SANITIZE_SIMPLE ||
934                    ctx->settings.sanitize_method == CRIP_SANITIZE_OS_SIMPLE) {
935             av_bprint_chars(buf, rep->to, 1);
936         } else if (ctx->settings.sanitize_method == CRIP_SANITIZE_UNICODE ||
937                    ctx->settings.sanitize_method == CRIP_SANITIZE_OS_UNICODE) {
938             av_bprint_append_data(buf, rep->to_u, strlen(rep->to_u));
939         }
940 
941         pos = str;
942     }
943 
944     return 0;
945 }
946 
get_dir_tag_val(cyanrip_ctx * ctx,AVDictionary * meta,const char * ofmt,const char * key)947 static char *get_dir_tag_val(cyanrip_ctx *ctx, AVDictionary *meta,
948                              const char *ofmt, const char *key)
949 {
950     char *val = NULL;
951     if (!strcmp(key, "year")) {
952         const char *date = dict_get(meta, "date");
953         if (date) {
954             char *save_year, *date_dup = av_strdup(date);
955             val = av_strdup(av_strtok(date_dup, ":-", &save_year));
956             av_free(date_dup);
957         }
958     } else if (!strcmp(key, "format")) {
959         val = av_strdup(ofmt);
960     } else if (!strcmp(key, "track")) {
961         const char *track = dict_get(meta, "track");
962         if (crip_is_integer(track)) {
963             int pad = 0, digits = strlen(track);
964             if (((digits + pad) < 2) && ctx->nb_tracks >  9) pad++;
965             if (((digits + pad) < 3) && ctx->nb_tracks > 99) pad++;
966             val = av_mallocz(pad + digits + 1);
967             for (int i = 0; i < pad; i++)
968                 val[i] = '0';
969             memcpy(&val[pad], track, digits);
970         } else {
971             val = av_strdup(track);
972         }
973     } else {
974         val = av_strdup(dict_get(meta, key));
975     }
976 
977     return val;
978 }
979 
process_cond(cyanrip_ctx * ctx,AVBPrint * buf,AVDictionary * meta,const char * ofmt,char *** dir_list,int * dir_list_nb,const char * scheme)980 static int process_cond(cyanrip_ctx *ctx, AVBPrint *buf, AVDictionary *meta,
981                         const char *ofmt, char ***dir_list, int *dir_list_nb,
982                         const char *scheme)
983 {
984     char *scheme_copy = av_strdup(scheme);
985 
986     char *save, *tok = av_strtok(scheme_copy, "{}", &save);
987     while (tok) {
988         if (!strncmp(tok, "if", strlen("if"))) {
989             char *cond = av_strdup(tok);
990             char *cond_save, *cond_tok = av_strtok(cond, "#", &cond_save);
991 
992             cond_tok = av_strtok(NULL, "#", &cond_save);
993             if (!cond_tok) {
994                 cyanrip_log(ctx, 0, "Invalid scheme syntax, no \"#\"!\n");
995                 av_free(cond);
996                 goto fail;
997             }
998 
999             int val1_origin_is_tag = 1;
1000             char *val1 = get_dir_tag_val(ctx, meta, ofmt, cond_tok);
1001             if (!val1) {
1002                 val1 = av_strdup(tok);
1003                 val1_origin_is_tag = 0;
1004             }
1005 
1006             cond_tok = av_strtok(NULL, "#", &cond_save);
1007             if (!cond_tok) {
1008                 cyanrip_log(ctx, 0, "Invalid scheme syntax, no terminating \"#\"!\n");
1009                 av_free(cond);
1010                 av_free(val1);
1011                 goto fail;
1012             }
1013 
1014             int cond_is_eq = 0, cond_is_not_eq = 0, cond_is_more = 0, cond_is_less = 0;
1015             if (strstr(cond_tok, "==")) {
1016                 cond_is_eq = 1;
1017             } else if (strstr(cond_tok, "!=")) {
1018                 cond_is_not_eq = 1;
1019             } else if (strstr(cond_tok, ">")) {
1020                 cond_is_more = 1;
1021             } else if (strstr(cond_tok, "<")) {
1022                 cond_is_less = 1;
1023             } else {
1024                 cyanrip_log(ctx, 0, "Invalid condition syntax!\n");
1025                 av_free(cond);
1026                 av_free(val1);
1027                 goto fail;
1028             }
1029 
1030             cond_tok = av_strtok(NULL, "#", &cond_save);
1031             if (!cond_tok) {
1032                 cyanrip_log(ctx, 0, "Invalid scheme syntax, no terminating \"#\"!\n");
1033                 goto fail;
1034             }
1035 
1036             int val2_origin_is_tag = 1;
1037             char *val2 = get_dir_tag_val(ctx, meta, ofmt, cond_tok);
1038             if (!val2) {
1039                 val2 = av_strdup(cond_tok);
1040                 val2_origin_is_tag = 0;
1041             }
1042 
1043             cond_tok = av_strtok(NULL, "#", &cond_save);
1044             if (!cond_tok) {
1045                 cyanrip_log(ctx, 0, "Invalid scheme syntax, no terminating \"#\"!\n");
1046                 goto fail;
1047             }
1048 
1049             int cond_true = 0;
1050             cond_true |= cond_is_eq && !strcmp(val1, val2);
1051             cond_true |= cond_is_not_eq && strcmp(val1, val2);
1052 
1053             if (cond_is_less || cond_is_more) {
1054                 int val1_is_int = crip_is_integer(val1), val2_is_int = crip_is_integer(val2);
1055                 if (!val1_is_int && (val1_is_int == val2_is_int)) { /* None are int */
1056                     cond_true = cond_is_less ? (strcmp(val1, val2) < 0) : (cond_is_more ? strcmp(val1, val2) > 0 : 0);
1057                 } else if (val1_is_int && (val1_is_int == val2_is_int)) { /* Both are int */
1058                     int64_t val1_dec = strtol(val1, NULL, 10);
1059                     int64_t val2_dec = strtol(val2, NULL, 10);
1060                     cond_true |= cond_is_less && val1_dec < val2_dec;
1061                     cond_true |= cond_is_more && val1_dec > val2_dec;
1062                 } else {
1063                     ptrdiff_t val1_dec = val1_is_int ? strtol(val1, NULL, 10) : (!val1_origin_is_tag ? 0 : (ptrdiff_t)val1);
1064                     ptrdiff_t val2_dec = val2_is_int ? strtol(val2, NULL, 10) : (!val2_origin_is_tag ? 0 : (ptrdiff_t)val2);
1065                     cond_true |= cond_is_less && val1_dec < val2_dec;
1066                     cond_true |= cond_is_more && val1_dec > val2_dec;
1067                 }
1068             }
1069 
1070             if (cond_true) {
1071                 char *true_save, *true_tok = av_strtok(cond_tok, "|", &true_save);
1072                 while (true_tok) {
1073                     int origin_is_tag = 1;
1074                     char *true_val = get_dir_tag_val(ctx, meta, ofmt, true_tok);
1075                     if (!true_val) {
1076                         true_val = av_strdup(true_tok);
1077                         origin_is_tag = 0;
1078                     }
1079 
1080                     crip_bprint_sanitize(ctx, buf, true_val, dir_list, dir_list_nb, origin_is_tag);
1081                     av_free(true_val);
1082 
1083                     true_tok = av_strtok(NULL, "|", &true_save);
1084                 }
1085             }
1086 
1087             av_free(val2);
1088             av_free(val1);
1089             av_free(cond);
1090 
1091             tok = av_strtok(NULL, "{}", &save);
1092             continue;
1093         }
1094 
1095         int origin_is_tag = 1;
1096         char *val = get_dir_tag_val(ctx, meta, ofmt, tok);
1097         if (!val) {
1098             val = av_strdup(tok);
1099             origin_is_tag = 0;
1100         }
1101 
1102         crip_bprint_sanitize(ctx, buf, val, dir_list, dir_list_nb, origin_is_tag);
1103         av_free(val);
1104 
1105         tok = av_strtok(NULL, "{}", &save);
1106     }
1107 
1108     av_free(scheme_copy);
1109     return 0;
1110 
1111 fail:
1112     av_free(scheme_copy);
1113     return AVERROR(EINVAL);
1114 }
1115 
crip_get_path(cyanrip_ctx * ctx,enum CRIPPathType type,int create_dirs,const cyanrip_out_fmt * fmt,void * arg)1116 char *crip_get_path(cyanrip_ctx *ctx, enum CRIPPathType type, int create_dirs,
1117                     const cyanrip_out_fmt *fmt, void *arg)
1118 {
1119     char *ret = NULL, **ret_p = NULL;
1120     AVBPrint buf;
1121     char **dir_list = NULL;
1122     int dir_list_nb = 0;
1123     av_bprint_init(&buf, 0, AV_BPRINT_SIZE_AUTOMATIC);
1124 
1125     if (process_cond(ctx, &buf, ctx->meta, fmt->folder_suffix, &dir_list, &dir_list_nb,
1126                      ctx->settings.folder_name_scheme))
1127         goto end;
1128 
1129     add_to_dir_list(&dir_list, &dir_list_nb, buf.str);
1130     av_bprint_chars(&buf, OS_DIR_CHAR, 1);
1131 
1132     char *ext = NULL;
1133     if (type == CRIP_PATH_COVERART) {
1134         CRIPArt *art = arg;
1135         crip_bprint_sanitize(ctx, &buf, dict_get(art->meta, "title"), &dir_list, &dir_list_nb, 0);
1136         ext = art->extension ? av_strdup(art->extension) : av_strdup("<extension>");
1137     } else if (type == CRIP_PATH_LOG) {
1138         if (process_cond(ctx, &buf, ctx->meta, fmt->name, &dir_list, &dir_list_nb,
1139                          ctx->settings.log_name_scheme))
1140             goto end;
1141         ext = av_strdup("log");
1142     } else {
1143         cyanrip_track *t = arg;
1144         if (process_cond(ctx, &buf, t->meta, fmt->name, &dir_list, &dir_list_nb,
1145                          ctx->settings.track_name_scheme))
1146             goto end;
1147         ext = av_strdup(fmt->ext);
1148     }
1149 
1150     if (ext)
1151         av_bprintf(&buf, ".%s", ext);
1152     av_free(ext);
1153 
1154     ret_p = &ret;
1155     for (int i = 0; i < dir_list_nb; i++) {
1156         if (create_dirs) {
1157             struct stat st_req = { 0 };
1158             if (stat(dir_list[i], &st_req) == -1)
1159                 mkdir(dir_list[i], 0700);
1160         }
1161         av_free(dir_list[i]);
1162     }
1163     av_free(dir_list);
1164 
1165 end:
1166     av_bprint_finalize(&buf, ret_p);
1167     return ret;
1168 }
1169 
main(int argc,char ** argv)1170 int main(int argc, char **argv)
1171 {
1172     cyanrip_ctx *ctx = NULL;
1173     cyanrip_settings settings;
1174 
1175     if (signal(SIGINT, on_quit_signal) == SIG_ERR)
1176         cyanrip_log(ctx, 0, "Can't init signal handler!\n");
1177 
1178     /* Default settings */
1179     settings.dev_path = NULL;
1180     settings.folder_name_scheme = "{album}{if #releasecomment# > #0# (|releasecomment|)} [{format}]";
1181     settings.track_name_scheme = "{if #totaldiscs# > #1#|disc|.}{track} - {title}";
1182     settings.log_name_scheme = "{album}{if #totaldiscs# > #1# CD|disc|}";
1183     settings.sanitize_method = CRIP_SANITIZE_UNICODE;
1184     settings.speed = 0;
1185     settings.frame_max_retries = 25;
1186     settings.over_under_read_frames = 0;
1187     settings.offset = 0;
1188     settings.print_info_only = 0;
1189     settings.disable_mb = 0;
1190     settings.disable_coverart_db = 0;
1191     settings.decode_hdcd = 0;
1192     settings.bitrate = 128.0f;
1193     settings.overread_leadinout = 0;
1194     settings.rip_indices_count = -1;
1195     settings.disable_accurip = 0;
1196     settings.eject_on_success_rip = 0;
1197     settings.outputs[0] = CYANRIP_FORMAT_FLAC;
1198     settings.outputs_num = 1;
1199     settings.paranoia_level = FF_ARRAY_ELEMS(paranoia_level_map) - 1;
1200 
1201     memset(settings.pregap_action, CYANRIP_PREGAP_DEFAULT, sizeof(settings.pregap_action));
1202 
1203     int c, idx;
1204     char *p_save, *p;
1205     int mb_release_idx = -1;
1206     char *mb_release_str = NULL;
1207     int discnumber = 0, totaldiscs = 0;
1208     char *album_metadata_ptr = NULL;
1209     char *track_metadata_ptr[198] = { NULL };
1210     int track_metadata_ptr_cnt = 0;
1211     int find_drive_offset_range = 0;
1212 
1213     CRIPArt cover_arts[32] = { 0 };
1214     int nb_cover_arts = 0;
1215 
1216     CRIPArt track_cover_arts[198] = { 0 };
1217     int track_cover_arts_map[198] = { 0 };
1218     int nb_track_cover_arts = 0;
1219 
1220     while ((c = getopt(argc, argv, "hNAUfHIVEOl:a:t:b:c:r:d:o:s:S:D:p:C:R:P:F:L:T:")) != -1) {
1221         switch (c) {
1222         case 'h':
1223             cyanrip_log(ctx, 0, "cyanrip %s (%s) help:\n", PROJECT_VERSION_STRING, vcstag);
1224             cyanrip_log(ctx, 0, "\n  Ripping options:\n");
1225             cyanrip_log(ctx, 0, "    -d <path>             Set device path\n");
1226             cyanrip_log(ctx, 0, "    -s <int>              CD Drive offset in samples (default: 0)\n");
1227             cyanrip_log(ctx, 0, "    -r <int>              Maximum number of retries to read a frame (default: 25)\n");
1228             cyanrip_log(ctx, 0, "    -S <int>              Set drive speed (default: unset)\n");
1229             cyanrip_log(ctx, 0, "    -p <number>=<string>  Track pregap handling (default: default)\n");
1230             cyanrip_log(ctx, 0, "    -P <int>              Paranoia level, %i to 0 inclusive, default: %i\n", crip_max_paranoia_level, settings.paranoia_level);
1231             cyanrip_log(ctx, 0, "    -O                    Enable overreading into lead-in and lead-out\n");
1232             cyanrip_log(ctx, 0, "    -H                    Enable HDCD decoding. Do this if you're sure disc is HDCD\n");
1233             cyanrip_log(ctx, 0, "\n  Metadata options:\n");
1234             cyanrip_log(ctx, 0, "    -I                    Only print CD and track info\n");
1235             cyanrip_log(ctx, 0, "    -a <string>           Album metadata, key=value:key=value\n");
1236             cyanrip_log(ctx, 0, "    -t <number>=<string>  Track metadata, can be specified multiple times\n");
1237             cyanrip_log(ctx, 0, "    -R <int>/<string>     Sets the MusicBrainz release to use, either as an index starting from 1 or an ID string\n");
1238             cyanrip_log(ctx, 0, "    -c <int>/<int>        Tag multi-disc albums, syntax is disc/totaldiscs\n");
1239             cyanrip_log(ctx, 0, "    -C <title>=<path>     Set cover image path, type may be \"Name\" or a \"track_number\"\n");
1240             cyanrip_log(ctx, 0, "    -N                    Disables MusicBrainz lookup and ignores lack of manual metadata\n");
1241             cyanrip_log(ctx, 0, "    -A                    Disables AccurateRip database query and validation\n");
1242             cyanrip_log(ctx, 0, "    -U                    Disables Cover art DB database query and retrieval\n");
1243             cyanrip_log(ctx, 0, "\n  Output options:\n");
1244             cyanrip_log(ctx, 0, "    -l <list>             Select which tracks to rip (default: all)\n");
1245             cyanrip_log(ctx, 0, "    -D <string>           Directory naming scheme, by default its \"%s\"\n", settings.folder_name_scheme);
1246             cyanrip_log(ctx, 0, "    -F <string>           Track naming scheme, by default its \"%s\"\n", settings.track_name_scheme);
1247             cyanrip_log(ctx, 0, "    -L <string>           Log file name scheme, by default its \"%s\"\n", settings.log_name_scheme);
1248             cyanrip_log(ctx, 0, "    -T <string>           Filename sanitation: simple, os_simple, unicode (default), os_unicode\n");
1249             cyanrip_log(ctx, 0, "    -o <string>           Comma separated list of outputs\n");
1250             cyanrip_log(ctx, 0, "    -b <kbps>             Bitrate of lossy files in kbps\n");
1251             cyanrip_log(ctx, 0, "\n  Misc. options:\n");
1252             cyanrip_log(ctx, 0, "    -E                    Eject tray once successfully done\n");
1253             cyanrip_log(ctx, 0, "    -V                    Print program version\n");
1254             cyanrip_log(ctx, 0, "    -h                    Print options help\n");
1255             cyanrip_log(ctx, 0, "    -f                    Find drive offset (requires a disc with an AccuRip DB entry)\n");
1256             return 0;
1257             break;
1258         case 'S':
1259             settings.speed = (int)strtol(optarg, NULL, 10);
1260             if (settings.speed < 0) {
1261                 cyanrip_log(ctx, 0, "Invalid drive speed!\n");
1262                 return 1;
1263             }
1264             break;
1265         case 'P':
1266             if (!strcmp(optarg, "none"))
1267                 settings.paranoia_level = 0;
1268             else if (!strcmp(optarg, "max"))
1269                 settings.paranoia_level = crip_max_paranoia_level;
1270             else
1271                 settings.paranoia_level = (int)strtol(optarg, NULL, 10);
1272             if (settings.paranoia_level < 0 || settings.paranoia_level > crip_max_paranoia_level) {
1273                 cyanrip_log(ctx, 0, "Invalid paranoia level %i must be between 0 and %i!\n",
1274                             settings.paranoia_level, crip_max_paranoia_level);
1275                 return 1;
1276             }
1277             break;
1278         case 'r':
1279             settings.frame_max_retries = strtol(optarg, NULL, 10);
1280             if (settings.frame_max_retries < 0) {
1281                 cyanrip_log(ctx, 0, "Invalid retries amount!\n");
1282                 return 1;
1283             }
1284             break;
1285         case 'R':
1286             p = NULL;
1287             mb_release_idx = strtol(optarg, &p, 10);
1288             if (p != NULL && p[0] != ' ' && p[0] != '\0') {
1289                 mb_release_str = optarg;
1290                 mb_release_idx = -1;
1291             } else if (mb_release_idx <= 0) {
1292                 cyanrip_log(ctx, 0, "Invalid release index %i!\n", mb_release_idx);
1293                 return 1;
1294             }
1295             break;
1296         case 's':
1297             settings.offset = strtol(optarg, NULL, 10);
1298             int sign = settings.offset < 0 ? -1 : +1;
1299             int frames = ceilf(abs(settings.offset)/(float)(CDIO_CD_FRAMESIZE_RAW >> 2));
1300             settings.over_under_read_frames = sign*frames;
1301             break;
1302         case 'N':
1303             settings.disable_mb = 1;
1304             break;
1305         case 'A':
1306             settings.disable_accurip = 1;
1307             break;
1308         case 'U':
1309             settings.disable_coverart_db = 1;
1310             break;
1311         case 'b':
1312             settings.bitrate = strtof(optarg, NULL);
1313             break;
1314         case 'l':
1315             settings.rip_indices_count = 0;
1316             p = av_strtok(optarg, ",", &p_save);
1317             while (p) {
1318                 idx = strtol(p, NULL, 10);
1319                 for (int i = 0; i < settings.rip_indices_count; i++) {
1320                     if (settings.rip_indices[i] == idx) {
1321                         cyanrip_log(ctx, 0, "Duplicated rip idx %i\n", idx);
1322                         return 1;
1323                     }
1324                 }
1325                 settings.rip_indices[settings.rip_indices_count++] = idx;
1326                 p = av_strtok(NULL, ",", &p_save);
1327             }
1328             qsort(settings.rip_indices, settings.rip_indices_count,
1329                   sizeof(int), cmp_numbers);
1330             break;
1331         case 'o':
1332             settings.outputs_num = 0;
1333             if (!strncmp("help", optarg, strlen("help"))) {
1334                 cyanrip_log(ctx, 0, "Supported output codecs:\n");
1335                 cyanrip_print_codecs();
1336                 return 0;
1337             }
1338             p = av_strtok(optarg, ",", &p_save);
1339             while (p) {
1340                 int res = cyanrip_validate_fmt(p);
1341                 for (int i = 0; i < settings.outputs_num; i++) {
1342                     if (settings.outputs[i] == res) {
1343                         cyanrip_log(ctx, 0, "Duplicated format \"%s\"\n", p);
1344                         return 1;
1345                     }
1346                 }
1347                 if (res != -1) {
1348                     settings.outputs[settings.outputs_num++] = res;
1349                 } else {
1350                     cyanrip_log(ctx, 0, "Invalid format \"%s\"\n", p);
1351                     return 1;
1352                 }
1353                 p = av_strtok(NULL, ",", &p_save);
1354             }
1355             break;
1356         case 'I':
1357             settings.print_info_only = 1;
1358             break;
1359         case 'H':
1360             settings.decode_hdcd = 1;
1361             break;
1362         case 'O':
1363             settings.overread_leadinout = 1;
1364             break;
1365         case 'f':
1366             find_drive_offset_range = 6;
1367             break;
1368         case 'c':
1369             p = av_strtok(optarg, "/", &p_save);
1370             discnumber = strtol(p, NULL, 10);
1371             if (discnumber <= 0) {
1372                 cyanrip_log(ctx, 0, "Invalid discnumber %i\n", discnumber);
1373                 return 1;
1374             }
1375             p = av_strtok(NULL, "/", &p_save);
1376             if (!p)
1377                 break;
1378             totaldiscs = strtol(p, NULL, 10);
1379             if (totaldiscs <= 0) {
1380                 cyanrip_log(ctx, 0, "Invalid totaldiscs %i\n", totaldiscs);
1381                 return 1;
1382             }
1383             if (discnumber > totaldiscs) {
1384                 cyanrip_log(ctx, 0, "discnumber %i is larger than totaldiscs %i\n", discnumber, totaldiscs);
1385                 return 1;
1386             }
1387             break;
1388         case 'p':
1389             p = av_strtok(optarg, "=", &p_save);
1390             idx = strtol(p, NULL, 10);
1391             if (idx < 1 || idx > 197) {
1392                 cyanrip_log(ctx, 0, "Invalid track idx for pregap: %i\n", idx);
1393                 return 1;
1394             }
1395             enum cyanrip_pregap_action act = CYANRIP_PREGAP_DEFAULT;
1396             p = av_strtok(NULL, "=", &p_save);
1397             if (!p) {
1398                 cyanrip_log(ctx, 0, "Missing pregap action\n");
1399                 return 1;
1400             }
1401             if (!strncmp(p, "default", strlen("default"))) {
1402                 act = CYANRIP_PREGAP_DEFAULT;
1403             } else if (!strncmp(p, "drop", strlen("drop"))) {
1404                 act = CYANRIP_PREGAP_DROP;
1405             } else if (!strncmp(p, "merge", strlen("merge"))) {
1406                 act = CYANRIP_PREGAP_MERGE;
1407             } else if (!strncmp(p, "track", strlen("track"))) {
1408                 act = CYANRIP_PREGAP_TRACK;
1409             } else {
1410                 cyanrip_log(ctx, 0, "Invalid pregap action %s\n", p);
1411                 return 1;
1412             }
1413             settings.pregap_action[idx - 1] = act;
1414             break;
1415         case 'C':
1416             p = av_strtok(optarg, "=", &p_save);
1417             char *next = av_strtok(NULL, "=", &p_save);
1418             CRIPArt *dst = NULL;
1419 
1420             if (!next) {
1421                 int have_front = 0;
1422                 int have_back = 0;
1423                 for (int i = 0; i < nb_cover_arts; i++) {
1424                     if (!strcmp(cover_arts[i].title, "Front"))
1425                         have_front = 1;
1426                     if (!strcmp(cover_arts[i].title, "Back"))
1427                         have_back = 1;
1428                 }
1429                 if (!have_front) {
1430                     next = p;
1431                     p = "Front";
1432                 } else if (!have_back) {
1433                     next = p;
1434                     p = "Back";
1435                 } else {
1436                     cyanrip_log(ctx, 0, "No cover art location specified for \"%s\"\n", p);
1437                     return 1;
1438                 }
1439             }
1440 
1441             if (crip_is_integer(p)) {
1442                 idx = strtol(p, NULL, 10);
1443                 if (idx < 0 || idx > 198) {
1444                     cyanrip_log(ctx, 0, "Invalid track idx for cover art: %i\n", idx);
1445                     return 1;
1446                 }
1447                 for (int i = 0; i < nb_track_cover_arts; i++) {
1448                     if (track_cover_arts_map[i] == idx) {
1449                         cyanrip_log(ctx, 0, "Cover art already specified for track idx %i!\n", idx);
1450                         return 1;
1451                     }
1452                 }
1453                 track_cover_arts_map[nb_track_cover_arts] = idx;
1454                 dst = &track_cover_arts[nb_track_cover_arts++];
1455                 p = "title";
1456             } else {
1457                 for (int i = 0; i < nb_cover_arts; i++) {
1458                     if (!strcmp(cover_arts[i].title, p)) {
1459                         cyanrip_log(ctx, 0, "Cover art \"%s\" already specified!\n", p);
1460                         return 1;
1461                     }
1462                 }
1463 
1464                 dst = &cover_arts[nb_cover_arts++];
1465                 if (nb_cover_arts > 31) {
1466                     cyanrip_log(ctx, 0, "Too many cover arts specified!\n");
1467                     return 1;
1468                 }
1469             }
1470 
1471             dst->source_url = next;
1472             dst->title = p;
1473             break;
1474         case 'E':
1475             settings.eject_on_success_rip = 1;
1476             break;
1477         case 'D':
1478             settings.folder_name_scheme = optarg;
1479             break;
1480         case 'F':
1481             settings.track_name_scheme = optarg;
1482             break;
1483         case 'L':
1484             settings.log_name_scheme = optarg;
1485             break;
1486         case 'T':
1487             if (!strncmp(optarg, "simple", strlen("simple"))) {
1488                 settings.sanitize_method = CRIP_SANITIZE_SIMPLE;
1489             } else if (!strncmp(optarg, "os_simple", strlen("os_simple"))) {
1490                 settings.sanitize_method = CRIP_SANITIZE_OS_SIMPLE;
1491             } else if (!strncmp(optarg, "unicode", strlen("unicode"))) {
1492                 settings.sanitize_method = CRIP_SANITIZE_UNICODE;
1493             } else if (!strncmp(optarg, "os_unicode", strlen("os_unicode"))) {
1494                 settings.sanitize_method = CRIP_SANITIZE_OS_UNICODE;
1495             } else {
1496                 cyanrip_log(ctx, 0, "Invalid sanitation method %s\n", optarg);
1497                 return 1;
1498             }
1499             break;
1500         case 'V':
1501             cyanrip_log(ctx, 0, "cyanrip %s (%s)\n", PROJECT_VERSION_STRING, vcstag);
1502             return 0;
1503         case 'd':
1504             settings.dev_path = strdup(optarg);
1505             break;
1506         case '?':
1507             return 1;
1508             break;
1509         case 'a':
1510             album_metadata_ptr = optarg;
1511             break;
1512         case 't':
1513             track_metadata_ptr[track_metadata_ptr_cnt++] = optarg;
1514             break;
1515         default:
1516             abort();
1517             break;
1518         }
1519     }
1520 
1521     if (settings.outputs_num > 1 && !strstr(settings.folder_name_scheme, "{format}")) {
1522         cyanrip_log(ctx, 0, "Directory name scheme must contain {format} with multiple output formats!\n");
1523         return 1;
1524     }
1525 
1526     if (find_drive_offset_range) {
1527         settings.disable_accurip = 0;
1528         settings.disable_mb = 1;
1529         settings.disable_coverart_db = 1;
1530         settings.offset = 0;
1531         settings.eject_on_success_rip = 0;
1532         cyanrip_log(ctx, 0, "Searching for drive offset, enabling AccuRip and disabling MusicBrainz and Cover art fetching...\n");
1533     }
1534 
1535     if (cyanrip_ctx_init(&ctx, &settings))
1536         return 1;
1537 
1538     /* Fill disc MCN */
1539     crip_fill_mcn(ctx);
1540 
1541     /* Fill discid */
1542     if (crip_fill_discid(ctx)) {
1543         ctx->total_error_count++;
1544         goto end;
1545     }
1546 
1547     /* Fill musicbrainz metadata */
1548     if (crip_fill_metadata(ctx,
1549                            !!album_metadata_ptr || track_metadata_ptr_cnt,
1550                            mb_release_idx, mb_release_str, discnumber)) {
1551         ctx->total_error_count++;
1552         goto end;
1553     }
1554 
1555     /* Print this for easy access */
1556     if (ctx->settings.print_info_only)
1557         cyanrip_log(ctx, 0, "MusicBrainz URL:\n%s\n", ctx->mb_submission_url);
1558 
1559     /* Copy album cover arts */
1560     ctx->nb_cover_arts = nb_cover_arts;
1561     for (int i = 0; i < nb_cover_arts; i++) {
1562         ctx->cover_arts[i].source_url = av_strdup(cover_arts[i].source_url);
1563         av_dict_set(&ctx->cover_arts[i].meta, "title", cover_arts[i].title, 0);
1564     }
1565 
1566     /* Album cover art (down)loading, and DB quering */
1567     if (crip_fill_coverart(ctx, ctx->settings.print_info_only) < 0) {
1568         ctx->total_error_count++;
1569         goto end;
1570     }
1571 
1572     /* Fill in accurip data */
1573     if (crip_fill_accurip(ctx)) {
1574         ctx->total_error_count++;
1575         goto end;
1576     }
1577 
1578     if (find_drive_offset_range) {
1579         search_for_drive_offset(ctx, find_drive_offset_range);
1580         goto end;
1581     }
1582 
1583     if (mb_release_str && !dict_get(ctx->meta, "release_id"))
1584         av_dict_set(&ctx->meta, "release_id", mb_release_str, 0);
1585 
1586     if (discnumber)
1587         av_dict_set_int(&ctx->meta, "disc", discnumber, 0);
1588 
1589     if (totaldiscs)
1590         av_dict_set_int(&ctx->meta, "totaldiscs", totaldiscs, 0);
1591 
1592     /* Read user album metadata */
1593     if (album_metadata_ptr) {
1594         /* Fixup */
1595         char *copy = append_missing_keys(album_metadata_ptr, "album=", "album_artist=");
1596 
1597         /* Parse */
1598         int err = av_dict_parse_string(&ctx->meta, copy, "=", ":", 0);
1599         av_free(copy);
1600         if (err) {
1601             cyanrip_log(ctx, 0, "Error reading album tags: %s\n",
1602                         av_err2str(err));
1603             ctx->total_error_count++;
1604             goto end;
1605         }
1606 
1607         /* Fixup title tag mistake */
1608         const char *title = dict_get(ctx->meta, "title");
1609         const char *album = dict_get(ctx->meta, "album");
1610         if (title && !album) {
1611             av_dict_set(&ctx->meta, "album", title, 0);
1612             av_dict_set(&ctx->meta, "title", "", 0);
1613         }
1614 
1615         /* Populate artist tag if missing/unspecified */
1616         const char *album_artist = dict_get(ctx->meta, "album_artist");
1617         const char *artist = dict_get(ctx->meta, "artist");
1618         if (album_artist && !artist)
1619             av_dict_set(&ctx->meta, "artist", album_artist, 0);
1620         else if (artist && !album_artist)
1621             av_dict_set(&ctx->meta, "album_artist", artist, 0);
1622     }
1623 
1624     /* Create log file */
1625     if (!ctx->settings.print_info_only) {
1626         cyanrip_log_init(ctx);
1627     } else {
1628         cyanrip_log(ctx, 0, "Log(s) will be written to:\n");
1629         for (int f = 0; f < ctx->settings.outputs_num; f++) {
1630             char *logfile = crip_get_path(ctx, CRIP_PATH_LOG, 0,
1631                                           &crip_fmt_info[ctx->settings.outputs[f]],
1632                                           NULL);
1633             cyanrip_log(ctx, 0, "    %s\n", logfile);
1634             av_free(logfile);
1635         }
1636     }
1637 
1638     cyanrip_log_start_report(ctx);
1639     setup_track_offsets_and_report(ctx);
1640 
1641     copy_album_to_track_meta(ctx);
1642 
1643     /* Read user track metadata */
1644     for (int i = 0; i < track_metadata_ptr_cnt; i++) {
1645         if (!track_metadata_ptr[i])
1646             continue;
1647 
1648         char *end = NULL;
1649         int u_nb = strtol(track_metadata_ptr[i], &end, 10);
1650 
1651         /* Verify all indices */
1652         int track_idx = 0;
1653         for (; track_idx < ctx->nb_tracks; track_idx++) {
1654             if (ctx->tracks[track_idx].number == u_nb)
1655                 break;
1656         }
1657         if (track_idx >= ctx->nb_tracks) {
1658             cyanrip_log(ctx, 0, "Invalid track number %i, list has %i tracks!\n",
1659                         u_nb, ctx->nb_tracks);
1660             ctx->total_error_count++;
1661             goto end;
1662         }
1663 
1664         end += 1; /* Move past equal sign */
1665 
1666         /* Fixup */
1667         char *copy = append_missing_keys(end, "title=", "artist=");
1668 
1669         /* Parse */
1670         int err = av_dict_parse_string(&ctx->tracks[track_idx].meta,
1671                                        copy, "=", ":", 0);
1672         av_free(copy);
1673         if (err) {
1674             cyanrip_log(ctx, 0, "Error reading track tags: %s\n",
1675                         av_err2str(err));
1676             ctx->total_error_count++;
1677             goto end;
1678         }
1679     }
1680 
1681     /* Copy track cover arts */
1682     for (int i = 0; i < nb_track_cover_arts; i++) {
1683         idx = track_cover_arts_map[i];
1684         int track_idx = 0;
1685         for (; track_idx < ctx->nb_tracks; track_idx++) {
1686             if (ctx->tracks[track_idx].number == idx)
1687                 break;
1688         }
1689         if (track_idx >= ctx->nb_tracks) {
1690             cyanrip_log(ctx, 0, "Invalid track number %i, list has %i tracks!\n",
1691                         idx, ctx->nb_tracks);
1692             ctx->total_error_count++;
1693             goto end;
1694         }
1695         ctx->tracks[track_idx].art.source_url = av_strdup(track_cover_arts[i].source_url);
1696         av_dict_set(&ctx->tracks[track_idx].art.meta, "title", "Front", 0);
1697     }
1698 
1699     /* Track cover art (down)loading */
1700     if (crip_fill_track_coverart(ctx, ctx->settings.print_info_only) < 0) {
1701         ctx->total_error_count++;
1702         goto end;
1703     }
1704 
1705     /* Write non-track cover arts */
1706     if (ctx->nb_cover_arts) {
1707         cyanrip_log(ctx, 0, "Cover art destination(s):\n");
1708         for (int f = 0; f < ctx->settings.outputs_num; f++) {
1709             for (int i = 0; i < ctx->nb_cover_arts; i++) {
1710                 char *file = crip_get_path(ctx, CRIP_PATH_COVERART, 0,
1711                                            &crip_fmt_info[ctx->settings.outputs[f]],
1712                                            &ctx->cover_arts[i]);
1713                 cyanrip_log(ctx, 0, "    %s\n", file);
1714                 av_free(file);
1715 
1716                 if (!ctx->settings.print_info_only) {
1717                     int err = crip_save_art(ctx, &ctx->cover_arts[i],
1718                                             &crip_fmt_info[ctx->settings.outputs[f]]);
1719                     if (err) {
1720                         ctx->total_error_count++;
1721                         goto end;
1722                     }
1723                 }
1724             }
1725         }
1726         cyanrip_log(ctx, 0, "\n");
1727     }
1728 
1729     cyanrip_log(ctx, 0, "Tracks:\n");
1730     if (ctx->settings.rip_indices_count == -1) {
1731         for (int i = 0; i < ctx->nb_tracks; i++) {
1732             cyanrip_track *t = &ctx->tracks[i];
1733             if (ctx->settings.print_info_only) {
1734                 cyanrip_log(ctx, 0, "Track %i info:\n", t->number);
1735                 track_read_extra(ctx, t);
1736                 cyanrip_log_track_end(ctx, t);
1737 
1738                 if (cdio_get_media_changed(ctx->cdio)) {
1739                     cyanrip_log(ctx, 0, "Drive media changed, stopping!\n");
1740                     break;
1741                 }
1742             } else {
1743                 if (cyanrip_rip_track(ctx, t))
1744                     break;
1745             }
1746 
1747             if (quit_now)
1748                 break;
1749         }
1750     } else {
1751         for (int i = 0; i < ctx->settings.rip_indices_count; i++) {
1752             idx = ctx->settings.rip_indices[i];
1753 
1754             /* Verify all indices */
1755             int j = 0;
1756             for (; j < ctx->nb_tracks; j++) {
1757                 if (ctx->tracks[j].number == idx)
1758                     break;
1759             }
1760             if (j >= ctx->nb_tracks) {
1761                 cyanrip_log(ctx, 0, "Invalid rip index %i, list has %i tracks!\n",
1762                             idx, ctx->nb_tracks);
1763                 ctx->total_error_count++;
1764                 goto end;
1765             }
1766         }
1767 
1768         for (int i = 0; i < ctx->settings.rip_indices_count; i++) {
1769             idx = ctx->settings.rip_indices[i];
1770 
1771             int j = 0;
1772             for (; j < ctx->nb_tracks; j++) {
1773                 if (ctx->tracks[j].number == idx)
1774                     break;
1775             }
1776 
1777             if (ctx->settings.print_info_only) {
1778                 cyanrip_log(ctx, 0, "Track %i info:\n", ctx->tracks[j].number);
1779                 track_read_extra(ctx, &ctx->tracks[j]);
1780                 cyanrip_log_track_end(ctx, &ctx->tracks[j]);
1781 
1782                 if (cdio_get_media_changed(ctx->cdio)) {
1783                     cyanrip_log(ctx, 0, "Drive media changed, stopping!\n");
1784                     break;
1785                 }
1786             } else {
1787                 if (cyanrip_rip_track(ctx, &ctx->tracks[j]))
1788                     break;
1789             }
1790 
1791             if (quit_now)
1792                 break;
1793         }
1794     }
1795 
1796     if (!ctx->settings.print_info_only)
1797         cyanrip_log_finish_report(ctx);
1798 end:
1799     cyanrip_log_end(ctx);
1800 
1801     int err_cnt = ctx->total_error_count;
1802 
1803     cyanrip_ctx_end(&ctx);
1804 
1805     return !!err_cnt;
1806 }
1807 
1808 #ifdef HAVE_WMAIN
wmain(int argc,wchar_t * argv[])1809 int wmain(int argc, wchar_t *argv[])
1810 {
1811     char *argstr_flat, **win32_argv_utf8 = NULL;
1812     int i, ret, buffsize = 0, offset = 0;
1813 
1814     /* determine the UTF-8 buffer size (including NULL-termination symbols) */
1815     for (i = 0; i < argc; i++)
1816         buffsize += WideCharToMultiByte(CP_UTF8, 0, argv[i], -1,
1817                                         NULL, 0, NULL, NULL);
1818 
1819     win32_argv_utf8 = av_mallocz(sizeof(char *) * (argc + 1) + buffsize);
1820     argstr_flat     = (char *)win32_argv_utf8 + sizeof(char *) * (argc + 1);
1821 
1822     for (i = 0; i < argc; i++) {
1823         win32_argv_utf8[i] = &argstr_flat[offset];
1824         offset += WideCharToMultiByte(CP_UTF8, 0, argv[i], -1,
1825                                       &argstr_flat[offset],
1826                                       buffsize - offset, NULL, NULL);
1827     }
1828     win32_argv_utf8[i] = NULL;
1829 
1830     ret = main(argc, win32_argv_utf8);
1831 
1832     av_free(win32_argv_utf8);
1833     return ret;
1834 }
1835 #endif
1836