From eb753846c8ccf1aa84724bdb5fff3b344ffb2372 Mon Sep 17 00:00:00 2001 From: bnnm Date: Sun, 3 Nov 2019 17:54:01 +0100 Subject: [PATCH] Tweak ADX meta code --- src/meta/adx.c | 265 +++++++++++++++++++++++++++---------------------- 1 file changed, 144 insertions(+), 121 deletions(-) diff --git a/src/meta/adx.c b/src/meta/adx.c index 5252b641..a42c2697 100644 --- a/src/meta/adx.c +++ b/src/meta/adx.c @@ -3,14 +3,15 @@ #endif #include #include - #include "meta.h" #include "adx_keys.h" #include "../coding/coding.h" -#define MAX_TEST_FRAMES (INT_MAX/0x8000) -static int find_adx_key(STREAMFILE *streamFile, uint8_t type, uint16_t *xor_start, uint16_t *xor_mult, uint16_t *xor_add); +#define ADX_KEY_MAX_TEST_FRAMES 32768 +#define ADX_KEY_TEST_BUFFER_SIZE 0x8000 + +static int find_adx_key(STREAMFILE *sf, uint8_t type, uint16_t *xor_start, uint16_t *xor_mult, uint16_t *xor_add); /* ADX - CRI Middleware format */ VGMSTREAM * init_vgmstream_adx(STREAMFILE *streamFile) { @@ -18,7 +19,7 @@ VGMSTREAM * init_vgmstream_adx(STREAMFILE *streamFile) { off_t start_offset, hist_offset = 0; int loop_flag = 0, channel_count; int32_t loop_start_sample = 0, loop_end_sample = 0; - uint16_t version_signature; + uint16_t version; uint8_t encoding_type; uint8_t frame_size; @@ -29,79 +30,77 @@ VGMSTREAM * init_vgmstream_adx(STREAMFILE *streamFile) { /* checks*/ - /* .adx: standard, .adp: Headhunter (DC) */ + /* .adx: standard + * .adp: Headhunter (DC) */ if (!check_extensions(streamFile,"adx,adp")) goto fail; - /* check first 2 bytes */ - if ((uint16_t)read_16bitBE(0x00,streamFile)!=0x8000) goto fail; + if ((uint16_t)read_16bitBE(0x00,streamFile) != 0x8000) + goto fail; - /* get stream offset, check for CRI signature just before */ - start_offset = (uint16_t)read_16bitBE(0x02,streamFile) + 4; - if ((uint16_t)read_16bitBE(start_offset-6,streamFile)!=0x2863 || /* "(c" */ - (uint32_t)read_32bitBE(start_offset-4,streamFile)!=0x29435249 /* ")CRI" */ - ) goto fail; + start_offset = (uint16_t)read_16bitBE(0x02,streamFile) + 0x04; + if ((uint16_t)read_16bitBE(start_offset - 0x06,streamFile) != 0x2863 || /* "(c" */ + (uint32_t)read_32bitBE(start_offset - 0x04,streamFile) != 0x29435249) /* ")CRI" */ + goto fail; - /* check for encoding type */ - /* 0x02 is for some unknown fixed filter, 0x03 is standard ADX, 0x04 is - * ADX with exponential scale, 0x10 is AHX for DC, 0x11 is AHX */ encoding_type = read_8bit(0x04, streamFile); - switch (encoding_type) { - case 2: + case 0x02: coding_type = coding_CRI_ADX_fixed; break; - case 3: + case 0x03: coding_type = coding_CRI_ADX; break; - case 4: + case 0x04: coding_type = coding_CRI_ADX_exp; break; - default: + default: /* 0x10 is AHX for DC, 0x11 is AHX */ goto fail; } + /* ADX encoders can't set this value, but is honored by ADXPlay if changed and multiple of 0x12, + * though output is unusual and may not be fully supported (works in mono so not an interleave) */ frame_size = read_8bit(0x05, streamFile); - /* check for bits per sample? (only 4 makes sense for ADX) */ - if (read_8bit(0x06,streamFile) != 4) goto fail; + if (read_8bit(0x06,streamFile) != 4) /* bits per sample */ + goto fail; /* older ADX (adxencd) up to 2ch, newer ADX (criatomencd) up to 8 */ channel_count = read_8bit(0x07,streamFile); + /* 0x08: sample rate */ + /* 0x0c: samples */ + /* 0x10: high-pass frequency */ - /* check version signature, read loop info */ - version_signature = read_16bitBE(0x12,streamFile); - + version = read_16bitBE(0x12,streamFile); /* encryption */ - if (version_signature == 0x0408) { + if (version == 0x0408) { if (find_adx_key(streamFile, 8, &xor_start, &xor_mult, &xor_add)) { coding_type = coding_CRI_ADX_enc_8; - version_signature = 0x0400; + version = 0x0400; } } - else if (version_signature == 0x0409) { + else if (version == 0x0409) { if (find_adx_key(streamFile, 9, &xor_start, &xor_mult, &xor_add)) { coding_type = coding_CRI_ADX_enc_9; - version_signature = 0x0400; + version = 0x0400; } } - /* version + extra data */ - if (version_signature == 0x0300) { /* early ADX (~2004?) */ + if (version == 0x0300) { /* early ADX (~1998) [Grandia (SAT), Baroque (SAT)] */ size_t base_size = 0x14, loops_size = 0x18; header_type = meta_ADX_03; /* no sample history */ - if (start_offset - 6 >= base_size + loops_size) { /* enough space for loop info? */ + if (start_offset - 0x06 >= base_size + loops_size) { /* enough space for loop info? */ off_t loops_offset = base_size; - /* off+0x00 (2): initial loop padding (the encoder adds a few blank samples so loop start is block-aligned; max 31) + /* 0x00 (2): initial loop padding (the encoder adds a few blank samples so loop start is block-aligned; max 31) * ex. loop_start=12: enc_start=32, padding=20 (32-20=12); loop_start=35: enc_start=64, padding=29 (64-29=35) - * off+0x02 (2): loop sample(?) flag (always 1) */ + * 0x02 (2): loop sample(?) flag (always 1) */ loop_flag = read_32bitBE(loops_offset+0x04,streamFile) != 0; /* loop offset(?) flag (always 1) */ loop_start_sample = read_32bitBE(loops_offset+0x08,streamFile); //loop_start_offset = read_32bitBE(loops_offset+0x0c,streamFile); @@ -109,37 +108,40 @@ VGMSTREAM * init_vgmstream_adx(STREAMFILE *streamFile) { //loop_end_offset = read_32bitBE(loops_offset+0x14,streamFile); } } - else if (version_signature == 0x0400) { /* common */ + else if (version == 0x0400) { /* common */ size_t base_size = 0x18, hist_size, ainf_size = 0, loops_size = 0x18; off_t ainf_offset; header_type = meta_ADX_04; hist_offset = base_size; /* always present but often blank */ - hist_size = (channel_count > 1 ? 4*channel_count : 4+4); /* min is 8, even in 1ch files */ + hist_size = (channel_count > 1 ? 0x04 * channel_count : 0x04 + 0x04); /* min is 8, even in 1ch files */ - ainf_offset = base_size + hist_size + 0x4; /* not seen with >2ch though */ + ainf_offset = base_size + hist_size + 0x04; /* not seen with >2ch though */ if ((uint32_t)read_32bitBE(ainf_offset+0x00,streamFile) == 0x41494E46) /* "AINF" */ ainf_size = read_32bitBE(ainf_offset+0x04,streamFile); - if (start_offset - ainf_size - 6 >= hist_offset + hist_size + loops_size) { /* enough space for loop info? */ + if (start_offset - ainf_size - 0x06 >= hist_offset + hist_size + loops_size) { /* enough space for loop info? */ off_t loops_offset = base_size + hist_size; - /* off+0x00 (2): initial loop padding (the encoder adds a few blank samples so loop start is block-aligned; max 31) + /* 0x00 (2): initial loop padding (the encoder adds a few blank samples so loop start is block-aligned; max 31) * ex. loop_start=12: enc_start=32, padding=20 (32-20=12); loop_start=35: enc_start=64, padding=29 (64-29=35) - * off+0x02 (2): loop sample(?) flag (always 1) */ + * 0x02 (2): loop sample(?) flag (always 1) */ loop_flag = read_32bitBE(loops_offset+0x04,streamFile) != 0; /* loop offset(?) flag (always 1) */ loop_start_sample = read_32bitBE(loops_offset+0x08,streamFile); - //loop_start_offset = read_32bitBE(loops_offset+0x0c,streamFile); + //loop_start_offset = read_32bitBE(loops_offset+0x0c,streamFile); loop_end_sample = read_32bitBE(loops_offset+0x10,streamFile); - //loop_end_offset = read_32bitBE(loops_offset+0x14,streamFile); + //loop_end_offset = read_32bitBE(loops_offset+0x14,streamFile); } /* AINF header info (may be inserted by CRI's tools but is rarely used) * Can also start right after the loop points (base_size + hist_size + loops_size) - * 0x00 (4): "AINF"; 0x04 (4): size; 0x08 (10): str_id + * 0x00 (4): "AINF" + * 0x04 (4): size + * 0x08 (10): str_id * 0x18 (2): volume (0=base/max?, negative=reduce) - * 0x1c (2): pan l; 0x1e (2): pan r (0=base, max +-128) */ + * 0x1c (2): pan l + * 0x1e (2): pan r (0=base, max +-128) */ /* CINF header info (very rare, found after loops) [Sakura Taisen 3 (PS2)] * 0x00 (4): "CINF" @@ -149,7 +151,7 @@ VGMSTREAM * init_vgmstream_adx(STREAMFILE *streamFile) { * 0x48 (-): file name, null terminated */ } - else if (version_signature == 0x0500) { /* found in some SFD: Buggy Heat, appears to have no loop */ + else if (version == 0x0500) { /* found in some SFD: Buggy Heat, appears to have no loop */ header_type = meta_ADX_05; } else { /* not a known/supported version signature */ @@ -161,13 +163,13 @@ VGMSTREAM * init_vgmstream_adx(STREAMFILE *streamFile) { vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; - vgmstream->num_samples = read_32bitBE(0xc,streamFile); - vgmstream->sample_rate = read_32bitBE(0x8,streamFile); + vgmstream->sample_rate = read_32bitBE(0x08,streamFile); + vgmstream->num_samples = read_32bitBE(0x0c,streamFile); vgmstream->loop_start_sample = loop_start_sample; vgmstream->loop_end_sample = loop_end_sample; vgmstream->coding_type = coding_type; - vgmstream->layout_type = channel_count==1 ? layout_none : layout_interleave; + vgmstream->layout_type = layout_interleave; vgmstream->interleave_block_size = frame_size; vgmstream->meta_type = header_type; @@ -175,6 +177,7 @@ VGMSTREAM * init_vgmstream_adx(STREAMFILE *streamFile) { /* calculate filter coefficients */ if (coding_type == coding_CRI_ADX_fixed) { int i; + /* standard XA coefs * (2<<11) */ for (i = 0; i < channel_count; i++) { vgmstream->ch[i].adpcm_coef[0] = 0x0000; vgmstream->ch[i].adpcm_coef[1] = 0x0000; @@ -194,14 +197,14 @@ VGMSTREAM * init_vgmstream_adx(STREAMFILE *streamFile) { x = cutoff; y = vgmstream->sample_rate; - z = cos(2.0*M_PI*x/y); + z = cos(2.0 * M_PI * x / y); - a = M_SQRT2-z; - b = M_SQRT2-1.0; - c = (a-sqrt((a+b)*(a-b)))/b; + a = M_SQRT2 - z; + b = M_SQRT2 - 1.0; + c = (a - sqrt((a + b) * (a - b))) / b; - coef1 = (short)(c*8192); - coef2 = (short)(c*c*-4096); + coef1 = (short)(c * 8192); + coef2 = (short)(c * c * -4096); for (i = 0; i < channel_count; i++) { vgmstream->ch[i].adpcm_coef[0] = coef1; @@ -213,7 +216,7 @@ VGMSTREAM * init_vgmstream_adx(STREAMFILE *streamFile) { { int i; - for (i=0;ich[i].adx_mult = xor_mult; vgmstream->ch[i].adx_add = xor_add; - for (j=0;jch[i]); } } @@ -245,12 +248,14 @@ fail: } -/* return 0 if not found, 1 if found and set parameters */ -static int find_adx_key(STREAMFILE *streamFile, uint8_t type, uint16_t *xor_start, uint16_t *xor_mult, uint16_t *xor_add) { - uint16_t * scales = NULL; - uint16_t * prescales = NULL; - int bruteframe = 0, bruteframe_count = -1; - int startoff, endoff; +/* ADX key detection works by reading XORed ADPCM scales in frames, and un-XORing with keys in + * a list. If resulting values are within the expected range for N scales we accept that key. */ +static int find_adx_key(STREAMFILE *sf, uint8_t type, uint16_t *xor_start, uint16_t *xor_mult, uint16_t *xor_add) { + const int frame_size = 0x12; + uint16_t *scales = NULL; + uint16_t *prescales = NULL; + int bruteframe_start = 0, bruteframe_count = -1; + off_t start_offset; int i, rc = 0; @@ -260,7 +265,7 @@ static int find_adx_key(STREAMFILE *streamFile, uint8_t type, uint16_t *xor_star size_t key_size; /* handle type8 keystrings, key9 keycodes and derived keys too */ - key_size = read_key_file(keybuf,0x20, streamFile); + key_size = read_key_file(keybuf,0x20, sf); if (key_size > 0) { int i, is_ascii = 0; @@ -299,77 +304,90 @@ static int find_adx_key(STREAMFILE *streamFile, uint8_t type, uint16_t *xor_star /* setup totals */ { int frame_count; + int channels = read_8bit(0x07, sf); + int num_samples = read_32bitBE(0x0c, sf); + off_t end_offset; - startoff = read_16bitBE(2, streamFile) + 4; - endoff = (read_32bitBE(12, streamFile) + 31) / 32 * 18 * read_8bit(7, streamFile) + startoff; + start_offset = read_16bitBE(0x02, sf) + 0x4; + end_offset = (num_samples + 31) / 32 * frame_size * channels + start_offset; /* samples-to-bytes */ - frame_count = (endoff - startoff) / 18; + frame_count = (end_offset - start_offset) / frame_size; if (frame_count < bruteframe_count || bruteframe_count < 0) bruteframe_count = frame_count; } - /* find longest run of nonzero frames */ + /* find longest run of non-zero frames (zero frames aren't good for key testing) */ { - int longest = -1, longest_length = -1; - int length = 0; + static const uint8_t zeroes[0x12] = {0}; + uint8_t frame[0x12]; + int longest_start = -1, longest_count = -1; + int count = 0; + for (i = 0; i < bruteframe_count; i++) { - static const unsigned char zeroes[18] = {0}; - unsigned char buf[18]; - read_streamfile(buf, startoff + i * 18, 18, streamFile); - if (memcmp(zeroes, buf, 18)) - length++; + read_streamfile(frame, start_offset + i*frame_size, frame_size, sf); + if (memcmp(zeroes, frame, frame_size) != 0) + count++; else - length = 0; - if (length > longest_length) { - longest_length = length; - longest = i - length + 1; - if (longest_length >= 0x8000) + count = 0; + + /* update new record of non-zero frames */ + if (count > longest_count) { + longest_count = count; + longest_start = i - count + 1; + if (longest_count >= ADX_KEY_MAX_TEST_FRAMES) break; } } - if (longest == -1) { - goto find_key_cleanup; + + /* no non-zero frames */ + if (longest_start == -1) { + goto done; } - bruteframe_count = longest_length; - bruteframe = longest; + + bruteframe_start = longest_start; + bruteframe_count = longest_count; + if (bruteframe_count > ADX_KEY_MAX_TEST_FRAMES) //? + bruteframe_count = ADX_KEY_MAX_TEST_FRAMES; } - /* try to guess key */ + /* pre-load scales in a table, to avoid re-reading them per key */ { - const adxkey_info * keys = NULL; - int keycount = 0, keymask = 0; - int scales_to_do; - int key_id; - /* allocate storage for scales */ - scales_to_do = (bruteframe_count > MAX_TEST_FRAMES ? MAX_TEST_FRAMES : bruteframe_count); - scales = malloc(scales_to_do*sizeof(uint16_t)); - if (!scales) goto find_key_cleanup; + scales = malloc(bruteframe_count * sizeof(uint16_t)); + if (!scales) goto done; - /* prescales are those scales before the first frame we test - * against, we use these to compute the actual start */ - if (bruteframe > 0) { - /* allocate memory for the prescales */ - prescales = malloc(bruteframe*sizeof(uint16_t)); - if (!prescales) goto find_key_cleanup; + /* prescales are scales before the first test frame, with some blank frames no good + * for key testing, but we must read to compute XOR value at bruteframe_start */ + if (bruteframe_start > 0) { + /* allocate storage for prescales */ + prescales = malloc(bruteframe_start * sizeof(uint16_t)); + if (!prescales) goto done; /* read the prescales */ - for (i=0; i