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(©[add_key1_offset + strlen(key1)], ©[add_key1_offset], strlen(copy) - add_key1_offset);
839 memcpy(©[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(©[add_key2_offset + strlen(key2)], ©[add_key2_offset], strlen(copy) - add_key2_offset);
847 memcpy(©[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