1 #include "mp3_check.h"
2
3 #include <stdio.h>
4
5 static inline int move_to_next_frame(char *possible_mp3_tag,
6 frame_info * mp3_i, gen_info * file_info,
7 command_flags * flags, FILE *);
8 static int get_char_from_file(FILE *fp, unsigned int *header_value,
9 gen_info *file_info, command_flags *flags,
10 char *possible_mp3_tag);
11 static int check_vbr_and_time(frame_info *mp3_i, vbr_data *vbr_info,
12 gen_info *file_info);
13 static int rotate_char_array(char *byte_list, int *new_byte,
14 gen_info * info_file);
15 static int scan_file(FILE *fp, char *filename, meta_options *flag_options,
16 command_flags *flags);
17
18
rotate_char_array(char * byte_list,int * new_byte,gen_info * file_info)19 static int rotate_char_array(char *byte_list, int *new_byte,
20 gen_info *file_info)
21 {
22 int place_holder = 0;
23
24 /* I don't think it'll get any faster than this. */
25 place_holder = file_info->byte_count % 128;
26
27 *(byte_list + place_holder) = *new_byte;
28
29 return (TRUE);
30 }
31
scan_file(FILE * fp,char * filename,meta_options * flag_options,command_flags * flags)32 static int scan_file(FILE *fp, char *filename, meta_options *flag_options,
33 command_flags *flags)
34 {
35 int counter = 0;
36 int END_OF_FILE = FALSE;
37 int found_first_frame = FALSE;
38 char possible_mp3_tag[BUFFER_LENGTH];
39 unsigned int header_value = 0;
40
41 /*
42 * This is set to 'YES' so that I will always get the first 4 bytes
43 * off the stream no matter what.
44 */
45 char found_valid_header = YES;
46 char found_weak_header = NO;
47
48 /* Keep all the structures centralized. */
49 frame_info mp3_i;
50 vbr_data vbr_info;
51 mp3_time song_time;
52 frame_info first_mp3_frame;
53 id3_tag_info id3_tag;
54 gen_info file_info;
55
56 /* Zero these structures out. */
57 init_frame_struct(&first_mp3_frame);
58 init_vbr_tag_struct(&vbr_info);
59 init_gen_info_struct(&file_info);
60
61
62 while (!END_OF_FILE) {
63 if (flags->bflag || flags->aflag) {
64 /*
65 * This is the part where if the current byte count is
66 * greater than the upper limit on the commandline,
67 * it will break out.
68 */
69 if (flags->bflag
70 && (file_info.byte_count >
71 flag_options->byte_limit)) break;
72 }
73
74 /*
75 * The fist run defaults to 'YES', so the 'else' section runs
76 * first
77 *
78 * The logic below is what travels through the mp3 file, and
79 * then quickly does a sync checksum to see if it is a
80 * candidate.
81 */
82 if (!found_valid_header) {
83 /*
84 * Just add one character to the end and keep the
85 * rest. I do not want to grab another 4 bytes unless
86 * I got a success.
87 *
88 * Putting the below action in a function slows it
89 * down quite a bit, but, it makes the code more
90 * readable.
91 */
92 if (get_char_from_file
93 (fp, &header_value, &file_info, flags,
94 possible_mp3_tag)) {
95 if (!flags->ssflag && flags->sflag
96 && !flags->fflag) {
97 printf("%c", (header_value & 0xff));
98 }
99 } else
100 break;
101 } else {
102 /* Clear it out of old values. */
103 header_value = 0;
104
105 /* Grab the complete 4-byte header (if it is one). */
106 for (counter = 0; counter < 4; counter++) {
107 if (get_char_from_file
108 (fp, &header_value, &file_info, flags,
109 possible_mp3_tag)) {
110 if (!flags->ssflag && flags->sflag
111 && !flags->fflag) {
112 printf("%c",
113 (header_value & 0xff));
114 }
115 } else
116 break;
117 }
118 }
119
120
121 /* Check if this is a valid frame sync. */
122 if (((header_value >> 21) & 0x7ff) == 0x7ff) {
123 found_weak_header = YES;
124
125 init_frame_struct(&mp3_i);
126
127 if (check_header_value
128 (&header_value, filename, &mp3_i)) {
129 found_valid_header = YES;
130
131 /*
132 * Was the offset of this current frame known
133 * by the previous frame? It better have...
134 *
135 * This also defines junk at the beginning as
136 * being a bad frame.
137 */
138 if ((file_info.good_frame_count > 0)
139 && (file_info.next_expected_frame !=
140 file_info.byte_count)) {
141 file_info.bad_frame_count++;
142
143 /*
144 * I guess if I get to this section,
145 * I have found an mp3 with some bad
146 * stuff in it, so I will return a
147 * fail when the program exits.
148 */
149 fprintf(stderr,
150 "\nAn expected frame was not found. Expected it at offset 0x%x (BYTE %d), now at offset 0x%x (BYTE %d).",
151 file_info.next_expected_frame,
152 file_info.next_expected_frame,
153 file_info.byte_count,
154 file_info.byte_count);
155
156 /*
157 * Checking to see if a continuous
158 * amount of frames were found. This
159 * check only occurs when a bad frame
160 * was found.
161 */
162 if (flags->qflag
163 && (file_info.
164 frame_sequence_count > 0)
165 && (flag_options->min_frame_seq >
166 file_info.
167 frame_sequence_count)) {
168 fprintf(stderr,
169 "\nMininum contiguous number of frames wasn't reached. Got to %d, needed %d at offset 0x%x (BYTE %d).",
170 file_info.
171 frame_sequence_count,
172 flag_options->
173 min_frame_seq,
174 file_info.
175 next_expected_frame,
176 file_info.
177 next_expected_frame);
178
179 file_info.
180 frame_sequence_count = 0;
181 }
182
183 } else if (file_info.next_expected_frame ==
184 file_info.byte_count) {
185 file_info.good_frame_count++;
186 file_info.frame_sequence_count++;
187 } else
188 printf
189 ("\nSomething happened at byte %d. Next expected frame: %d.\n",
190 file_info.byte_count,
191 file_info.next_expected_frame);
192
193
194 /*
195 * The fflag is only good to use when there
196 * is *no* valid data, and not when there is
197 * good data (it should be implied). !sflag
198 * is set up this way because the data is
199 * already being shown above.
200 *
201 * There are two parts to this header printing.
202 */
203 if (!flags->ssflag &&
204 (flags->sflag && flags->fflag))
205 printf("%c%c%c%c",
206 ((header_value >> 24) & 0xff),
207 ((header_value >> 16) & 0xff),
208 ((header_value >> 8) & 0xff),
209 (header_value & 0xff));
210
211 /*
212 * I'm currently in a known good frame, so I
213 * can safely move forward two characters and
214 * still be in the frame. I will have to
215 * compensate when I move to the next frame.
216 * That is taken care of in move_to_next_frame.
217 */
218 if (mp3_i.PROT_BIT) {
219 /*
220 * Now, to grab the 2 bytes for the
221 * CRC value.
222 */
223 for (counter = 0; counter < 2;
224 counter++) {
225 if (get_char_from_file
226 (fp, &header_value,
227 &file_info, flags,
228 possible_mp3_tag)) {
229 if (!flags->ssflag
230 && flags->sflag
231 && !flags->fflag) {
232 printf("%c",
233 (header_value
234 &
235 0xff));
236 }
237 } else {
238 break;
239 }
240 }
241
242 /* Now, extract it from the header. */
243 mp3_i.CRC16_VALUE =
244 header_value & 0xffff;
245
246 /*
247 * The fflag is only good to use when
248 * there is *no* valid data, and not
249 * when there is good data (it should
250 * be implied). !sflag is set up this
251 * way because the data is already
252 * being shown above.
253 */
254 if (!flags->ssflag
255 && (flags->sflag && flags-> fflag))
256 printf("%c%c",
257 ((header_value >> 8) & 0xff),
258 (header_value & 0xff));
259 }
260
261 /*
262 * If the header is valid, but has bad data,
263 * it will still store this information.
264 * Perhaps it would be better to make sure it
265 * _really_ is a good frame before storing
266 * any VBR data...
267 */
268 check_vbr_and_time(&mp3_i, &vbr_info,
269 &file_info);
270
271 /*
272 * I always want to keep the first frame so I
273 * can print it out at the end consistently.
274 *
275 * I am going to disable the 'if' statement
276 * because for some reason if there's an
277 * anomaly in the frame, it keeps the data,
278 * so I guess for now I'll just have this
279 * information updated at every frame.
280 */
281 if (file_info.good_frame_count > 0) {
282 first_mp3_frame = mp3_i;
283 found_first_frame = TRUE;
284 }
285
286 /* Print out per-frame stats. */
287 if (!flags->sflag && flags->vvflag
288 && (file_info.good_frame_count > 0))
289 print_frame_info(&mp3_i, &file_info);
290
291 /*
292 * This will checkety-check to see if I get
293 * the next frame when I am supposed to.
294 */
295 if (mp3_i.PROT_BIT)
296 file_info.next_expected_frame =
297 file_info.byte_count +
298 mp3_i.FRAME_LENGTH - 2;
299 else
300 file_info.next_expected_frame =
301 file_info.byte_count +
302 mp3_i.FRAME_LENGTH;
303
304 /*
305 * Keep on searching the whole mp3? Ok, lemme
306 * skip though the data to get to the next
307 * header.
308 */
309 if (flags->aflag
310 || (flags->bflag
311 && (flag_options->byte_limit > 0))) {
312 if (!move_to_next_frame
313 (possible_mp3_tag, &mp3_i,
314 &file_info, flags, fp))
315 break;
316 } else
317 /*
318 * I found my first valid header, so
319 * I can quit now.
320 */
321 break;
322
323 } else {
324 /*
325 * I guess if I get to this section, I have
326 * found an mp3 with some bad stuff in it, so
327 * I will return a fail when the program exits.
328 */
329 file_info.bad_frame_count++;
330
331 if (flags->vflag)
332 fprintf(stderr,
333 "\nA possible header 0x%x passed the weak sieve, but failed the strong one at offset 0x%x (BYTE %d).",
334 header_value,
335 file_info.byte_count,
336 file_info.byte_count);
337
338
339 if (flags->qflag
340 && (file_info.frame_sequence_count > 0)
341 && (flag_options->min_frame_seq >
342 file_info.frame_sequence_count)) {
343 fprintf(stderr,
344 "\nMininum contiguous number of frames wasn't reached. Got to %d, needed %d at offset 0x%x (BYTE %d).",
345 file_info.frame_sequence_count,
346 flag_options->min_frame_seq,
347 file_info.next_expected_frame,
348 file_info.next_expected_frame);
349
350 file_info.frame_sequence_count = 0;
351 }
352 }
353
354 } else if (file_info.file_pos == 4
355 && (header_value & 0xffffff00) == 0x49443300) {
356
357
358 /*
359 * Getting to this section means we may have an
360 * ID3V2.x.x header
361 */
362 fprintf(stderr,
363 "Possible ID3v2 frame found, skipping\n");
364
365 /*
366 * Since this section is going to be under serious
367 * development, the flag 'eflag' will allow mp3_check
368 * to record a bad frame when an id3v2 tag is found.
369 * This will allow the user to weed out troublesome
370 * id3v2 mp3s.
371 */
372 mp3_i.ID3V2 = TRUE;
373
374 if (flags->eflag) {
375 if (flags->qflag
376 && (file_info.frame_sequence_count > 0)
377 && (flag_options->min_frame_seq >
378 file_info.frame_sequence_count)) {
379 fprintf(stderr,
380 "\nMininum contiguous number of frames wasn't reached. Got to %d, needed %d at offset 0x%x (BYTE %d).",
381 file_info.frame_sequence_count,
382 flag_options->min_frame_seq,
383 file_info.next_expected_frame,
384 file_info.next_expected_frame);
385
386 file_info.frame_sequence_count = 0;
387 }
388
389 file_info.bad_frame_count++;
390 }
391
392 /*
393 * TODO: Full ID3V2 checking & processing instead of
394 * skipping.
395 *
396 * Throw away the next two bytes - the first one
397 * should always be > 0xff
398 * The next 4 bytes represent the len encoded to
399 * avoid 0x80 bits set
400 */
401 for (counter = 0; counter < 6; counter++) {
402 if (get_char_from_file
403 (fp, &header_value, &file_info, flags,
404 possible_mp3_tag)) {
405 if (!flags->ssflag && flags->sflag
406 && !flags->fflag) {
407 printf("%c",
408 (header_value & 0xff));
409 }
410 } else {
411 break;
412 }
413 }
414
415 /* calculate the proper length */
416 counter = ((header_value >> 3) & 0xfe00000)
417 | ((header_value >> 2) & 0x1fc000)
418 | ((header_value >> 1) & 0x3f80)
419 | (header_value & 0x7f);
420
421 /*
422 * TODO: account for Unsynchronization!
423 * we may won't skip all of it if Unsynchroized is
424 * set (0x80 in byte 5 of file) account for the 10
425 * byte header and the next header this is to avoide
426 * excess messages during sync
427 */
428 file_info.next_expected_frame = counter + 14;
429
430 /* skip the ID3V2 frame */
431 while (counter-- > 0) {
432 if (get_char_from_file
433 (fp, &header_value, &file_info, flags,
434 possible_mp3_tag)) {
435 if (!flags->ssflag && flags->sflag
436 && !flags->fflag) {
437 printf("%c",
438 (header_value & 0xff));
439 }
440 } else {
441 break;
442 }
443 }
444
445 /* and mark as not currently synchronized - force it */
446 found_valid_header = YES;
447 found_weak_header = NO;
448 } else {
449 /*
450 * Getting to this section does not mean we got an
451 * invalid mp3 file...
452 */
453 found_valid_header = NO;
454 found_weak_header = NO;
455 }
456 }
457
458
459 if (!flags->sflag) {
460 if (flags->pflag) {
461 printf("%s %s\t%s %d\t%s %d\t%s %d", "FILE_NAME",
462 filename, "GOOD_FRAMES",
463 file_info.good_frame_count, "BAD_FRAMES",
464 file_info.bad_frame_count, "LAST_BYTE_CHECKED",
465 file_info.byte_count);
466 } else {
467 printf("\n");
468 printf("%-20s%s\n", "FILE_NAME", filename);
469 printf("%-20s%d\n", "GOOD_FRAMES",
470 file_info.good_frame_count);
471 printf("%-20s%d\n", "BAD_FRAMES",
472 file_info.bad_frame_count);
473 printf("%-20s%d", "LAST_BYTE_CHECKED",
474 file_info.byte_count);
475 }
476
477 if ((vbr_info.high_rate != vbr_info.low_rate)
478 && (file_info.good_frame_count > 0)) {
479 /*
480 * I do not want floating values in ave_rate. Seems
481 * silly to have a decimal point for that.
482 */
483 vbr_info.ave_rate =
484 vbr_info.sum_rate / file_info.good_frame_count;
485
486 if (flags->pflag) {
487 /*
488 * I don't want any newlines for the below
489 * line because the pflag option is to print
490 * it out all one one line
491 */
492 printf("\t%s %d\t%s %d\t%s %d", "VBR_HIGH",
493 vbr_info.high_rate, "VBR_LOW",
494 vbr_info.low_rate, "VBR_AVERAGE",
495 vbr_info.ave_rate);
496 } else {
497 printf("\n");
498 printf("%-20s%d\n", "VBR_HIGH",
499 vbr_info.high_rate);
500 printf("%-20s%d\n", "VBR_LOW",
501 vbr_info.low_rate);
502 printf("%-20s%d", "VBR_AVERAGE",
503 vbr_info.ave_rate);
504 }
505 }
506
507 if (found_first_frame) {
508 if (flags->vflag && !flags->pflag) {
509 print_summary(&first_mp3_frame, filename);
510 }
511
512 translate_time(&file_info, &song_time);
513
514 if (flags->pflag)
515 printf("\t%-20s%02u:%02u.%02u\n",
516 "SONG_LENGTH", song_time.minutes,
517 song_time.seconds,
518 song_time.frac_second);
519 else
520 printf("\n%-20s%02u:%02u.%02u\n",
521 "SONG_LENGTH", song_time.minutes,
522 song_time.seconds,
523 song_time.frac_second);
524
525 } else {
526 /*
527 * This line below closes the strings above. notice
528 * that at the end of the last string, there is no
529 * newline...
530 */
531 printf("\n");
532 }
533 }
534
535
536 init_id3_tag_struct(&id3_tag);
537 transform_char_array(possible_mp3_tag, &file_info);
538
539
540 if (flags->iflag && validate_id3_tag(possible_mp3_tag, &id3_tag)) {
541 if (flags->iflag && flags->fflag && flags->sflag)
542 dump_id3_tag(&id3_tag);
543
544 if (!flags->sflag && flags->vflag) {
545 printf("\n%-20s%d\n", "MP3_TAG", 1);
546
547 printf("%-20s%s\n", "TITLE", id3_tag.TITLE);
548 printf("%-20s%s\n", "ARTIST", id3_tag.ARTIST);
549 printf("%-20s%s\n", "ALBUM", id3_tag.ALBUM);
550 printf("%-20s%s\n", "YEAR", id3_tag.YEAR);
551 printf("%-20s%s\n", "COMMENT", id3_tag.COMMENT);
552 printf("%-20s%d\n", "GENRE", id3_tag.GENRE);
553 printf("%-20s%d\n", "TRACK", id3_tag.TRACK_NUMBER);
554 printf("%-20s%d\n", "ID3.11", id3_tag.ID3_311_VERSION);
555 }
556 } else if (!flags->sflag && flags->iflag) {
557 printf("\n%-20s%d\n", "MP3_TAG", 0);
558 }
559
560 if (ferror(fp)) {
561 fprintf(stderr, _("%s: %s %s\n"), PACKAGE, filename,
562 strerror(errno));
563 clearerr(fp);
564 }
565
566 if (ferror(stdout)) {
567 fprintf(stderr, _("%s: stdout %s\n"), PACKAGE,
568 strerror(errno));
569 exit(1);
570 }
571
572 /*
573 * The 'file_info.bad_frame_count' seems the best way to find errors,
574 * so I will use it.
575 */
576 if (file_info.bad_frame_count > 0)
577 return (FAIL);
578 else
579 return (PASS);
580 }
581
parse_args(char * argv[],meta_options * flag_options,command_flags * flags)582 int parse_args(char *argv[], meta_options *flag_options, command_flags *flags)
583 {
584 FILE *fp;
585 char *filename;
586 int error_count = 0;
587
588 fp = stdin;
589 filename = "stdin";
590
591 do {
592 if (*argv) {
593 if (strcmp(*argv, "-") == 0) {
594 fp = stdin;
595 } else if ((fp = fopen(*argv, "r")) == NULL) {
596 fprintf(stderr, "mp3_check: %s %s\n", *argv,
597 strerror(errno));
598 ++argv;
599 filename = *argv++;
600 continue;
601 }
602 filename = *argv++;
603 }
604
605 if (!scan_file(fp, filename, flag_options, flags))
606 error_count++;
607
608 if (fp != stdin)
609 fclose(fp);
610
611 } while (*argv);
612
613 if (error_count > 0)
614 return (FAIL);
615 else
616 return (PASS);
617 }
618
move_to_next_frame(char * possible_mp3_tag,frame_info * mp3_i,gen_info * file_info,command_flags * flags,FILE * fp)619 static inline int move_to_next_frame(char *possible_mp3_tag,
620 frame_info *mp3_i, gen_info *file_info,
621 command_flags *flags, FILE *fp)
622 {
623 int counter = 0;
624 int unk_char = 0;
625
626 const int print_to_stdout = !flags->ssflag && (flags->sflag
627 || flags->fflag);
628 int bytes_to_read = 0;
629
630 /* Here, mp3_check adjusts for the CRC checksum */
631 if (mp3_i->PROT_BIT)
632 /* move past the header (4 bytes) and CRC (2 bytes) = 6 */
633 bytes_to_read = mp3_i->FRAME_LENGTH - 6;
634 else
635 /* move past the header (4 bytes) since no CRC */
636 bytes_to_read = mp3_i->FRAME_LENGTH - 4;
637
638 /*
639 * This for loop cycles though the frame data. All I want to know is
640 * if the frame header is valid or not... basically, I know I got a
641 * good frame, I just want to hurry to the next one.
642 */
643 if (fp != stdin && !print_to_stdout && !flags->iflag) {
644
645 if (!fseek(fp, bytes_to_read, SEEK_CUR)) {
646 file_info->file_pos += bytes_to_read;
647 file_info->byte_count += bytes_to_read;
648
649 return (PASS);
650 }
651 }
652
653 /*
654 * Walk though the contents by hand if we've got to print the
655 * contents, or if we're reading from stdin, or if something went
656 * wrong with fseek().
657 */
658 for (counter = 0; counter < bytes_to_read; counter++) {
659 if ((unk_char = getc(fp)) != EOF) {
660
661 ++file_info->file_pos;
662 ++file_info->byte_count;
663
664 /*
665 * This is to keep track of the id3 tag that is at
666 * the end of mp3s.
667 */
668 if (flags->iflag)
669 rotate_char_array(possible_mp3_tag, &unk_char,
670 file_info);
671
672 /*
673 * Since I know I am in a good frame, print out the
674 * data. When I am in this function, it doesnt matter
675 * if the fflag is set.
676 */
677 if (print_to_stdout)
678 printf("%c", unk_char);
679
680
681 } else {
682 return (FAIL);
683 break;
684 }
685 }
686
687 return (PASS);
688 }
689
get_char_from_file(FILE * fp,unsigned int * header_value,gen_info * file_info,command_flags * flags,char * possible_mp3_tag)690 static int get_char_from_file(FILE *fp, unsigned int *header_value,
691 gen_info *file_info, command_flags *flags,
692 char *possible_mp3_tag)
693 {
694 int step_char = 0;
695
696 if ((step_char = getc(fp)) != EOF) {
697 /*
698 * This keeps the 32 bit header, and rotates out the oldest,
699 * and adds a new one.
700 */
701 *header_value = (*header_value << 8) + step_char;
702
703 ++file_info->file_pos;
704 ++file_info->byte_count;
705
706 /*
707 * This section keeps track of the last 128 Bytes of the stream
708 * to later check for mp3 tags.
709 */
710 if (flags->iflag)
711 rotate_char_array(possible_mp3_tag, &step_char,
712 file_info);
713
714 return (PASS);
715 } else
716 return (FAIL);
717 }
718
check_vbr_and_time(frame_info * mp3_i,vbr_data * vbr_info,gen_info * file_info)719 static int check_vbr_and_time(frame_info *mp3_i, vbr_data *vbr_info,
720 gen_info *file_info)
721 {
722 /*
723 * Lets get the time of this frame, and add it to the total length of
724 * the mp3 file
725 */
726 file_info->time_in_seconds +=
727 (mp3_i->SAMPLES_PER_FRAME * 1.0) / (mp3_i->SAMPLE_FREQ * 1.0);
728
729 /* Time to do some nifty VBR checking. */
730 if (vbr_info->high_rate < mp3_i->BIT_RATE)
731 vbr_info->high_rate = mp3_i->BIT_RATE;
732
733 if ((mp3_i->BIT_RATE < vbr_info->low_rate)
734 || (vbr_info->low_rate == 0)) vbr_info->low_rate = mp3_i->BIT_RATE;
735
736
737 /* Get a running total for division at the end. */
738 vbr_info->sum_rate += mp3_i->BIT_RATE;
739
740 return (TRUE);
741 }
742