/* Copyright (c) 2017, Mans Rullgard All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include "bits.h" #include "sfbits.h" #include "mqa-common.h" #define MQA_MAGIC 0x11319207d5 struct metadata { unsigned size; uint8_t data[256]; }; static struct bitreader br; static struct metadata *metadata; static int metadata_frags; static int metadata_type = 1; static jmp_buf eofjmp; static unsigned bitnum; static unsigned abspos; static uint32_t csum; static int verbosity = 1; static int packet_type = -1; static int single; static uint32_t update_csum(uint64_t bits, int n) { while (n--) { int x = csum & 1; csum = csum >> 1 | bits << 31; if (x) csum ^= 3; bits >>= 1; } return csum & 15; } static void get_bits_cb(uint64_t bits, int n) { bitnum += n; update_csum(bits, n); } static void eof_cb(void) { longjmp(eofjmp, 1); } static int handle_hole(struct bitreader *b, unsigned ptype) { int size = print_field(b, 4, 0, "size", NULL); if (size == 15) size = print_field(b, 12, 0, "size", NULL); print_data(b, size, 0, "hole", NULL); return 0; } static const struct bitfield datasync_fields[] = { { 36, BF_HEX, "magic" }, { 1, BF_RET, "stream_pos_flag" }, { 1, 0, "pad" }, { 5, BF_HEX, "orig_rate", mqa_rates }, { 5, BF_HEX, "src_rate", mqa_rates }, { 5, 0, "render_filter" }, { 5, BF_HEX, "unknown_1" }, { 1, 0, "unknown_2" }, { 2, 0, "pad" }, { 8, BF_HEX, "mqa_level" }, { 7, BF_RET, "items" }, { 0 } }; static int handle_datasync(struct bitreader *b, unsigned ptype) { uint64_t val[2]; int stream_pos_flag; int n; int size[128]; int type[128]; int i; print_fields(b, datasync_fields, val); stream_pos_flag = val[0]; n = val[1]; for (i = 0; i < n; i++) size[i] = print_field(b, 8, BF_HEX, "size", NULL); for (i = 0; i < n; i++) type[i] = print_field(b, 8, BF_HEX, "type", NULL); if (stream_pos_flag) abspos = print_field(b, 32, BF_HEX, "stream_position", NULL); for (i = 0; i < n; i++) { print_verbose(1, "%10s[type %d]\n", "", type[i]); switch (type[i]) { case 0: print_field(b, 2, 0, "unknown_3", NULL); print_field(b, 4, 0, "unknown_4", NULL); print_field(b, 7, 0, "unknown_5", NULL); print_field(b, 7, 0, "unknown_6", NULL); if (stream_pos_flag) { print_field(b, 27, BF_HEX, "start_offset", NULL); print_field(b, 1, 0, "pad", NULL); print_data(b, size[i] - 48, BF_HEX, "data", NULL); } break; case 3: print_field(b, 64, BF_HEX, "iv", NULL); print_field(b, 32, BF_HEX, "start_pos", NULL); break; default: print_field(b, size[i], BF_HEX | (size[i] > 32 ? BF_DATA : 0), "unknown_7", NULL); break; } } return 0; } static const struct bitfield metadata_fields[] = { { 7, BF_HEX | BF_RET, "type" }, { 1, 0, "is_last" }, { 12, BF_RET, "fragment" }, { 8, BF_RET, "size" }, { 0 } }; static int handle_metadata(struct bitreader *b, unsigned ptype) { uint64_t val[3]; int type, frag, size; uint8_t *buf = NULL; print_fields(b, metadata_fields, val); type = val[0]; frag = val[1]; size = val[2] + 1; if (type == metadata_type) { if (frag >= metadata_frags) { int n = frag + 1; metadata = realloc(metadata, n * sizeof(*metadata)); memset(&metadata[metadata_frags], 0, (n - metadata_frags) * sizeof(*metadata)); metadata_frags = n; } metadata[frag].size = size; buf = metadata[frag].data; } print_data(b, 8 * size, BF_CHAR, "data", buf); return 0; } static const struct bitfield recon_fields[] = { { 12, BF_HEX, "size" }, { 0, BF_DATA | BF_SIZE, "data" }, { 0 } }; static const struct bitfield terminate_fields[] = { { 17, BF_HEX, "bits_to_end" }, { 0 } }; static const struct bitfield signature_fields[] = { { 4, 0, "index" }, { 3072, BF_DATA, "signature" }, { 0 } }; static const struct frame packets[16] = { [0] = { .name = "hole", .handler = handle_hole, }, [1] = { .name = "reconstruction", .fields = recon_fields, }, [3] = { .name = "terminate", .fields = terminate_fields, }, [4] = { .name = "signature", .fields = signature_fields, }, [5] = { .name = "datasync", .handler = handle_datasync, }, [7] = { .name = "metadata", .handler = handle_metadata, }, }; static int find_mqa_sync(struct bitreader *b) { find_sync(b, MQA_MAGIC, 40); if (peek_bits(b, 1, 40)) { int n = peek_bits(b, 7, 73); abspos = peek_bits(b, 32, 80 + 16 * n); } csum = abspos & 15; return 0; } static void scan_mqa(struct bitreader *b) { for (;;) { int startbit = bitnum; int type = get_ubits(b, 4); const struct frame *p = &packets[type]; int pcs; int cs; if (!p->name) { printf("%08x: unknown packet type %d, abort\n", startbit, type); return; } if (packet_type >= 0 && type != packet_type) print_verbosity(0); print_frame(b, p, type, startbit); cs = update_csum(0, -(bitnum - startbit) & 31); pcs = print_field(b, 4, BF_HEX, "checksum", NULL); print_verbosity(verbosity); if (cs != pcs) { printf("%08x: checksum error, resynchronising\n", bitnum); find_mqa_sync(b); continue; } if (single && (packet_type < 0 || type == packet_type)) break; abspos += bitnum - startbit; csum = abspos & 15; } } static void write_metadata(const char *file) { FILE *md = fopen(file, "w"); int i; if (!md) { perror(file); return; } for (i = 0; i < metadata_frags; i++) { if (!metadata[i].size) fprintf(stderr, "%s: fragment %d missing\n", file, i); fwrite(metadata[i].data, 1, metadata[i].size, md); } fclose(md); free(metadata); metadata = NULL; metadata_frags = 0; } static int scan_file(const char *name, int start, int mqabit) { SNDFILE *file; SF_INFO fmt; bitnum = 0; abspos = 0; memset(&fmt, 0, sizeof(fmt)); file = sf_open(name, SFM_READ, &fmt); if (!file) { fprintf(stderr, "%s: %s\n", name, sf_strerror(NULL)); return -1; } if (fmt.channels != 2) { fprintf(stderr, "%s: %d channels not allowed\n", name, fmt.channels); return -1; } if (start) sf_seek(file, start, SEEK_SET); print_verbosity(verbosity); if (!setjmp(eofjmp)) { init_bits_sf(&br, file, mqabit); br.msb = 0; br.get_bits_cb = get_bits_cb; br.eof_cb = eof_cb; find_mqa_sync(&br); scan_mqa(&br); } print_end(); sf_close(file); return 0; } int main(int argc, char **argv) { const char *mdfile = NULL; int mqabit = 8; int start = 0; int opt; int i; while ((opt = getopt(argc, argv, "1b:m:M:p:s:v")) != -1) { switch (opt) { case '1': single = 1; break; case 'b': mqabit = strtol(optarg, NULL, 0); break; case 'm': mdfile = optarg; verbosity = 0; break; case 'M': metadata_type = strtol(optarg, NULL, 0); break; case 'p': packet_type = strtol(optarg, NULL, 0); break; case 's': start = strtol(optarg, NULL, 0); break; case 'v': verbosity++; break; default: return 1; } } argc -= optind; argv += optind; if (argc < 1) return 1; for (i = 0; i < argc; i++) { if (argc > 1) printf("%s\n", argv[i]); scan_file(argv[i], start, mqabit); if (metadata && mdfile) write_metadata(mdfile); } return 0; }