diff --git a/src/coding/coding.h b/src/coding/coding.h index d3a350f8..b54c7631 100644 --- a/src/coding/coding.h +++ b/src/coding/coding.h @@ -136,6 +136,9 @@ void decode_mta2(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, /* mc3_decoder */ void decode_mc3(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); +/* fadpcm_decoder */ +void decode_fadpcm(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); + /* ea_mt_decoder*/ ea_mt_codec_data *init_ea_mt(int channel_count, int type); void decode_ea_mt(VGMSTREAM * vgmstream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); diff --git a/src/coding/fadpcm_decoder.c b/src/coding/fadpcm_decoder.c new file mode 100644 index 00000000..c8de0883 --- /dev/null +++ b/src/coding/fadpcm_decoder.c @@ -0,0 +1,85 @@ +#include "coding.h" +#include "../util.h" + +/* FADPCM table */ +static const int8_t fadpcm_coefs[8][2] = { + { 0 , 0 }, + { 60 , 0 }, + { 122 , 60 }, + { 115 , 52 }, + { 98 , 55 }, + { 0 , 0 }, + { 0 , 0 }, + { 0 , 0 }, +}; + +/* FMOD's FADPCM, basically XA/PSX ADPCM with a fancy header layout. + * Code/layout could be simplified but tries to emulate FMOD's code. + * Algorithm and tables debugged from their PC DLLs. */ +void decode_fadpcm(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) { + off_t frame_offset; + int i, j, k; + int block_samples, num_frame, samples_done = 0, sample_count = 0; + int coef_index[8], shift_factor[8]; + int32_t hist1; //= stream->adpcm_history1_32; + int32_t hist2; //= stream->adpcm_history2_32; + + /* external interleave (fixed size), mono */ + block_samples = (0x8c - 0xc) * 2; + num_frame = first_sample / block_samples; + first_sample = first_sample % block_samples; + + frame_offset = stream->offset + 0x8c*num_frame; + + + /* parse 0xc header */ + { + uint32_t coefs, shifts; + coefs = read_32bitLE(frame_offset + 0x00, stream->streamfile); + shifts = read_32bitLE(frame_offset + 0x04, stream->streamfile); + hist1 = read_16bitLE(frame_offset + 0x08, stream->streamfile); + hist2 = read_16bitLE(frame_offset + 0x0a, stream->streamfile); + + for (i = 0; i < 8; i++) { + coef_index[i] = (coefs >> i*4) & 0x0f; + shift_factor[i] = (shifts >> i*4) & 0x0f; + } + + /* header samples are not written to outbuf */ + } + + + /* decode nibbles, grouped in 0x10 * 0x04 * 2 */ + for (i = 0; i < 8; i++) { + off_t group_offset = frame_offset + 0x0c + 0x10*i; + int32_t coef1 = fadpcm_coefs[(coef_index[i] % 0x07)][0]; /* indexes > 7 are repeats (ex. 0x9 is 0x2) */ + int32_t coef2 = fadpcm_coefs[(coef_index[i] % 0x07)][1]; + int32_t shift = 0x16 - shift_factor[i]; + + for (j = 0; j < 4; j++) { + uint32_t nibbles = read_32bitLE(group_offset + 0x04*j, stream->streamfile); + + for (k = 0; k < 8; k++) { + int32_t new_sample; + + new_sample = (nibbles >> k*4) & 0x0f; + new_sample = (new_sample << 28) >> shift; /* sign extend + scale */ + new_sample = (new_sample - hist2*coef2 + hist1*coef1); + new_sample = new_sample >> 6; /* (new_sample / 64) has minor rounding differences */ + new_sample = clamp16(new_sample); + + if (sample_count >= first_sample && samples_done < samples_to_do) { + outbuf[samples_done * channelspacing] = new_sample; + samples_done++; + } + sample_count++; + + hist2 = hist1; + hist1 = new_sample; + } + } + } + + //stream->adpcm_history1_32 = hist1; + //stream->adpcm_history2_32 = hist2; +} diff --git a/src/coding/ima_decoder.c b/src/coding/ima_decoder.c index bdb09b4e..95590d1e 100644 --- a/src/coding/ima_decoder.c +++ b/src/coding/ima_decoder.c @@ -8,14 +8,7 @@ * All IMAs are mostly the same with these variations: * - interleave: blocks and channels are handled externally (layouts) or internally (mixed channels) * - block header: none (external), normal (4 bytes of history 16b + step 8b + reserved 8b) or others; per channel/global - * - expand type: ms-ima style or others; low or high nibble first - * - * todo: - * MS IMAs have the last sample of the prev block in the block header. In Microsoft's implementation, the header sample - * is written first and last sample is skipped (since they match). vgmstream ignores the header sample and - * writes the last one instead. This means the very first sample in the first header in a stream is incorrectly skipped. - * Header step should be 8 bit. - * Officially defined in "Microsoft Multimedia Standards Update" doc (RIFFNEW.pdf). + * - expand type: IMA style or variations; low or high nibble first */ static const int ADPCMTable[89] = { @@ -39,15 +32,15 @@ static const int IMA_IndexTable[16] = { }; -/* Standard IMA (most common) */ +/* Original IMA expansion, using shift+ADDs to avoid MULs (slow back then) */ static void std_ima_expand_nibble(VGMSTREAMCHANNEL * stream, off_t byte_offset, int nibble_shift, int32_t * hist1, int32_t * step_index) { int sample_nibble, sample_decoded, step, delta; - /* calculate diff = [signed] (step / 8) + (step / 4) + (step / 2) + (step) [when code = 4+2+1] - * simplified through math, using bitwise ops to avoid rounding: - * diff = (code + 1/2) * (step / 4) - * > diff = (step * nibble / 4) + (step / 8) - * > diff = (((step * nibble) + (step / 2)) / 4) */ + /* simplified through math from: + * - diff = (code + 1/2) * (step / 4) + * > diff = ((step * nibble) + (step / 2)) / 4 + * > diff = (step * nibble / 4) + (step / 8) + * final diff = [signed] (step / 8) + (step / 4) + (step / 2) + (step) [when code = 4+2+1] */ sample_nibble = (read_8bit(byte_offset,stream->streamfile) >> nibble_shift)&0xf; /* ADPCM code */ sample_decoded = *hist1; /* predictor value */ @@ -81,7 +74,33 @@ static void std_ima_expand_nibble_16(VGMSTREAMCHANNEL * stream, off_t byte_offse if (sample_nibble & 8) delta = -delta; sample_decoded += delta; - *hist1 = clamp16(sample_decoded); //no need for this actually + *hist1 = clamp16(sample_decoded); /* no need for this, actually */ + *step_index += IMA_IndexTable[sample_nibble]; + if (*step_index < 0) *step_index=0; + if (*step_index > 88) *step_index=88; +} + +/* Original IMA expansion, but using MULs rather than shift+ADDs (faster for newer processors). + * There is minor rounding difference between ADD and MUL expansions, noticeable/propagated in non-headered IMAs. */ +static void std_ima_expand_nibble_mul(VGMSTREAMCHANNEL * stream, off_t byte_offset, int nibble_shift, int32_t * hist1, int32_t * step_index) { + int sample_nibble, sample_decoded, step, delta; + + /* simplified through math from: + * - diff = (code + 1/2) * (step / 4) + * > diff = (code + 1/2) * step) / 4) * (2 / 2) + * > diff = (code + 1/2) * 2 * step / 8 + * final diff = [signed] ((code * 2 + 1) * step) / 8 */ + + sample_nibble = (read_8bit(byte_offset,stream->streamfile) >> nibble_shift)&0xf; + sample_decoded = *hist1; + step = ADPCMTable[*step_index]; + + delta = (sample_nibble & 0x7); + delta = ((delta * 2 + 1) * step) >> 3; + if (sample_nibble & 8) delta = -delta; + sample_decoded += delta; + + *hist1 = clamp16(sample_decoded); *step_index += IMA_IndexTable[sample_nibble]; if (*step_index < 0) *step_index=0; if (*step_index > 88) *step_index=88; @@ -96,7 +115,8 @@ static void n3ds_ima_expand_nibble(VGMSTREAMCHANNEL * stream, off_t byte_offset, step = ADPCMTable[*step_index]; sample_decoded = sample_decoded << 3; - delta = step * (sample_nibble & 7) * 2 + step; /* custom */ + delta = (sample_nibble & 0x07); + delta = step * delta * 2 + step; /* custom */ if (sample_nibble & 8) delta = -delta; sample_decoded += delta; sample_decoded = sample_decoded >> 3; @@ -149,24 +169,6 @@ static void otns_ima_expand_nibble(VGMSTREAMCHANNEL * stream, off_t byte_offset, if (*step_index > 88) *step_index=88; } -/* Ubisoft games, algorithm by Zench (https://bitbucket.org/Zenchreal/decubisnd) */ -static void ubi_ima_expand_nibble(VGMSTREAMCHANNEL * stream, off_t byte_offset, int nibble_shift, int32_t * hist1, int32_t * step_index) { - int sample_nibble, sample_decoded, step, delta; - - sample_nibble = (read_8bit(byte_offset,stream->streamfile) >> nibble_shift)&0xf; - sample_decoded = *hist1; - step = ADPCMTable[*step_index]; - - delta = (((sample_nibble & 7) * 2 + 1) * step) >> 3; /* custom */ - if (sample_nibble & 8) delta = -delta; - sample_decoded += delta; - - *hist1 = clamp16(sample_decoded); - *step_index += IMA_IndexTable[sample_nibble]; - if (*step_index < 0) *step_index=0; - if (*step_index > 88) *step_index=88; -} - /* ************************************ */ /* DVI/IMA */ /* ************************************ */ @@ -176,7 +178,6 @@ static void ubi_ima_expand_nibble(VGMSTREAMCHANNEL * stream, off_t byte_offset, * For vgmstream, low nibble is called "IMA ADPCM" and high nibble is "DVI IMA ADPCM" (same thing though). */ void decode_standard_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, int is_stereo, int is_high_first) { int i, sample_count = 0; - int32_t hist1 = stream->adpcm_history1_32; int step_index = stream->adpcm_step_index; @@ -186,7 +187,7 @@ void decode_standard_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channel if (step_index < 0) step_index=0; if (step_index > 88) step_index=88; - /* decode nibbles */ + /* decode nibbles (layout: varies) */ for (i = first_sample; i < first_sample + samples_to_do; i++, sample_count += channelspacing) { off_t byte_offset = is_stereo ? stream->offset + i : /* stereo: one nibble per channel */ @@ -205,7 +206,6 @@ void decode_standard_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channel void decode_3ds_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) { int i, sample_count; - int32_t hist1 = stream->adpcm_history1_32; int step_index = stream->adpcm_step_index; @@ -227,7 +227,6 @@ void decode_3ds_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspaci void decode_snds_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { int i, sample_count; - int32_t hist1 = stream->adpcm_history1_32; int step_index = stream->adpcm_step_index; @@ -249,7 +248,6 @@ void decode_snds_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspac void decode_otns_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { int i, sample_count; - int32_t hist1 = stream->adpcm_history1_32; int step_index = stream->adpcm_step_index; @@ -275,20 +273,20 @@ void decode_otns_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * /* MS-IMA */ /* ************************************ */ -/* IMA with variable-sized frames, header and custom nibble layout (outputs non-aligned number of samples). +/* IMA with custom frame sizes, header and nibble layout. Outputs an odd number of samples per frame, + * so to simplify calcs this decodes full frames, thus hist doesn't need to be mantained. * Officially defined in "Microsoft Multimedia Standards Update" doc (RIFFNEW.pdf). */ void decode_ms_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { int i, samples_read = 0, samples_done = 0, max_samples; - int32_t hist1;// = stream->adpcm_history1_32; int step_index;// = stream->adpcm_step_index; - /* internal interleave (configurable size), mixed channels (4 byte per ch) */ + /* internal interleave (configurable size), mixed channels */ int block_samples = ((vgmstream->interleave_block_size - 0x04*vgmstream->channels) * 2 / vgmstream->channels) + 1; first_sample = first_sample % block_samples; - /* normal header (hist+step+reserved per channel) */ - { + /* normal header (hist+step+reserved), per channel */ + { //if (first_sample == 0) { off_t header_offset = stream->offset + 0x04*channel; hist1 = read_16bitLE(header_offset+0x00,stream->streamfile); @@ -296,7 +294,7 @@ void decode_ms_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * ou if (step_index < 0) step_index = 0; if (step_index > 88) step_index = 88; - /* write header sample */ + /* write header sample (odd samples per block) */ if (samples_read >= first_sample && samples_done < samples_to_do) { outbuf[samples_done * channelspacing] = (short)hist1; samples_done++; @@ -308,12 +306,12 @@ void decode_ms_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * ou if (max_samples > samples_to_do + first_sample - samples_done) max_samples = samples_to_do + first_sample - samples_done; /* for smaller last block */ - /* decode nibbles (layout: alternates 4*2 nibbles per channel) */ + /* decode nibbles (layout: alternates 4 bytes/4*2 nibbles per channel) */ for (i = 0; i < max_samples; i++) { off_t byte_offset = stream->offset + 0x04*vgmstream->channels + 0x04*channel + 0x04*vgmstream->channels*(i/8) + (i%8)/2; int nibble_shift = (i&1?4:0); /* low nibble first */ - std_ima_expand_nibble(stream, byte_offset,nibble_shift, &hist1, &step_index); + std_ima_expand_nibble(stream, byte_offset,nibble_shift, &hist1, &step_index); /* original expand */ if (samples_read >= first_sample && samples_done < samples_to_do) { outbuf[samples_done * channelspacing] = (short)(hist1); @@ -331,24 +329,23 @@ void decode_ms_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * ou //stream->adpcm_step_index = step_index; } -/* Reflection's MS-IMA (some layout info from XA2WAV by Deniz Oezmen) */ +/* Reflection's MS-IMA with custom nibble layout (some info from XA2WAV by Deniz Oezmen) */ void decode_ref_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { int i, samples_read = 0, samples_done = 0, max_samples; - int32_t hist1;// = stream->adpcm_history1_32; int step_index;// = stream->adpcm_step_index; - /* internal interleave (configurable size), mixed channels (4 byte per ch) */ + /* internal interleave (configurable size), mixed channels */ int block_channel_size = (vgmstream->interleave_block_size - 0x04*vgmstream->channels) / vgmstream->channels; int block_samples = ((vgmstream->interleave_block_size - 0x04*vgmstream->channels) * 2 / vgmstream->channels) + 1; first_sample = first_sample % block_samples; - /* normal header (hist+step+reserved per channel) */ - { + /* normal header (hist+step+reserved), per channel */ + { //if (first_sample == 0) { off_t header_offset = stream->offset + 0x04*channel; hist1 = read_16bitLE(header_offset+0x00,stream->streamfile); - step_index = read_8bit(header_offset+0x02,stream->streamfile); /* 0x03: reserved */ + step_index = read_8bit(header_offset+0x02,stream->streamfile); if (step_index < 0) step_index = 0; if (step_index > 88) step_index = 88; @@ -391,90 +388,92 @@ void decode_ref_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * o /* XBOX-IMA */ /* ************************************ */ -/* MS-IMA with fixed frame size, skips last sample per channel (for aligment) and custom multichannel nibble layout. - * For multichannel the layout is (I think) mixed stereo channels (ex. 6ch: 2ch + 2ch + 2ch) */ -void decode_xbox_ima(VGMSTREAM * vgmstream,VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do,int channel) { - int i, sample_count; - +/* MS-IMA with fixed frame size, and outputs an even number of samples per frame (skips last nibble). + * Defined in Xbox's SDK. Multichannel interleaves 2ch*N/2, or 1ch*N with odd num_channels. */ +void decode_xbox_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { + int i, sample_count = 0; int32_t hist1 = stream->adpcm_history1_32; int step_index = stream->adpcm_step_index; - off_t offset = stream->offset; - - //internal interleave (0x20+4 size), mixed channels (4 byte per ch, mixed stereo) - int block_samples = (vgmstream->channels==1) ? - 32 : - 32*(vgmstream->channels&2);//todo this can be zero in 4/5/8ch = SEGFAULT using % below + /* internal interleave (fixed size), mixed channels */ + int block_samples = (0x24-0x4) * 2; first_sample = first_sample % block_samples; - //normal header (per channel) + /* normal header (hist+step+reserved), per stereo/mono channel in blocks */ if (first_sample == 0) { - off_t header_offset; - header_offset = stream->offset + 4*(channel%2); + off_t header_offset = (channelspacing & 1) ? + stream->offset + 0x24*(channel) + 0x00: + stream->offset + 0x48*(channel/2) + 0x04*(channel%2); - hist1 = read_16bitLE(header_offset,stream->streamfile); - step_index = read_16bitLE(header_offset+2,stream->streamfile); - if (step_index < 0) step_index=0; - if (step_index > 88) step_index=88; - } - - for (i=first_sample,sample_count=0; ioffset + 4*(channel%2) + 4 + i/8*4 + (i%8)/2 : - stream->offset + 4*(channel%2) + 4*2 + i/8*4*2 + (i%8)/2; - nibble_shift = (i&1?4:0); //low nibble first - - std_ima_expand_nibble(stream, offset,nibble_shift, &hist1, &step_index); - outbuf[sample_count] = (short)(hist1); - } - - //internal interleave: increment offset on complete frame - if (channelspacing==1) { /* mono */ - if (offset-stream->offset == 32+3) // ?? - stream->offset += 0x24; - } else { - if (offset-stream->offset == 64+(4*(channel%2))+3) // ?? - stream->offset += 0x24*channelspacing; - } - - stream->adpcm_history1_32 = hist1; - stream->adpcm_step_index = step_index; -} - -/* mono XBOX-IMA ADPCM for interleave */ -void decode_xbox_ima_int(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { - int i, sample_count = 0, num_frame; - int32_t hist1 = stream->adpcm_history1_32; - int step_index = stream->adpcm_step_index; - - //external interleave - int block_samples = (0x24 - 0x4) * 2; /* block size - header, 2 samples per byte */ - num_frame = first_sample / block_samples; - first_sample = first_sample % block_samples; - - //normal header - if (first_sample == 0) { - off_t header_offset = stream->offset + 0x24*num_frame; - - hist1 = read_16bitLE(header_offset,stream->streamfile); - step_index = read_8bit(header_offset+2,stream->streamfile); + hist1 = read_16bitLE(header_offset+0x00,stream->streamfile); + step_index = read_8bit(header_offset+0x02,stream->streamfile); if (step_index < 0) step_index=0; if (step_index > 88) step_index=88; - //must write history from header as last nibble/sample in block is almost always 0 / not encoded + /* write header sample (even samples per block, skips last nibble) */ outbuf[sample_count] = (short)(hist1); sample_count += channelspacing; first_sample += 1; samples_to_do -= 1; } - for (i=first_sample; i < first_sample + samples_to_do; i++) { /* first_sample + samples_to_do should be block_samples at most */ - off_t byte_offset = (stream->offset + 0x24*num_frame + 0x4) + (i-1)/2; - int nibble_shift = ((i-1)&1?4:0); //low nibble first + /* decode nibbles (layout: alternates 4 bytes/4*2 nibbles per channel, in stereo blocks) */ + for (i = first_sample; i < first_sample + samples_to_do; i++) { + off_t byte_offset = (channelspacing & 1) ? + (stream->offset + 0x24*(channel) + 0x04) + (i-1)/2: + (stream->offset + 0x48*(channel/2) + 0x04*2) + 0x04*(channel%2) + 0x04*2*((i-1)/8) + ((i-1)%8)/2; + int nibble_shift = ((i-1)&1?4:0); /* low nibble first */ - //last nibble/sample in block is ignored (next header sample contains it) + /* must skip last nibble per official decoder, probably not needed though */ + if (i < block_samples) { + std_ima_expand_nibble(stream, byte_offset,nibble_shift, &hist1, &step_index); + outbuf[sample_count] = (short)(hist1); + sample_count += channelspacing; + } + } + + /* internal interleave: increment offset on complete frame */ + if (i == block_samples) { + stream->offset += 0x24*vgmstream->channels; + } + + stream->adpcm_history1_32 = hist1; + stream->adpcm_step_index = step_index; +} + +/* Mono XBOX-IMA ADPCM, used for interleave. Also defined in Xbox's SDK. */ +void decode_xbox_ima_int(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { + int i, sample_count = 0, num_frame; + int32_t hist1 = stream->adpcm_history1_32; + int step_index = stream->adpcm_step_index; + + /* external interleave (fixed size), mono */ + int block_samples = (0x24 - 0x4) * 2; + num_frame = first_sample / block_samples; + first_sample = first_sample % block_samples; + + /* normal header (hist+step+reserved), single channel */ + if (first_sample == 0) { + off_t header_offset = stream->offset + 0x24*num_frame; + + hist1 = read_16bitLE(header_offset+0x00,stream->streamfile); + step_index = read_8bit(header_offset+0x02,stream->streamfile); + if (step_index < 0) step_index=0; + if (step_index > 88) step_index=88; + + /* write header sample (even samples per block, skips last nibble) */ + outbuf[sample_count] = (short)(hist1); + sample_count += channelspacing; + first_sample += 1; + samples_to_do -= 1; + } + + /* decode nibbles (layout: all nibbles from one channel) */ + for (i = first_sample; i < first_sample + samples_to_do; i++) { + off_t byte_offset = (stream->offset + 0x24*num_frame + 0x4) + (i-1)/2; + int nibble_shift = ((i-1)&1?4:0); /* low nibble first */ + + /* must skip last nibble per spec, rarely needed though (ex. Gauntlet Dark Legacy) */ if (i < block_samples) { std_ima_expand_nibble(stream, byte_offset,nibble_shift, &hist1, &step_index); outbuf[sample_count] = (short)(hist1); @@ -486,39 +485,42 @@ void decode_xbox_ima_int(VGMSTREAMCHANNEL * stream, sample * outbuf, int channel stream->adpcm_step_index = step_index; } +/* Similar to MS-IMA with even number of samples, header sample is not written (setup only). + * Apparently clamps to -32767 unlike standard's -32768 (probably not noticeable). + * Info here: http://problemkaputt.de/gbatek.htm#dssoundnotes */ void decode_nds_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) { int i, sample_count; - - int32_t hist1 = stream->adpcm_history1_16;//todo unneeded 16? + int32_t hist1 = stream->adpcm_history1_32; int step_index = stream->adpcm_step_index; - //external interleave + /* external interleave (configurable size), mono */ - //normal header + /* normal header (hist+step+reserved), single channel */ if (first_sample == 0) { off_t header_offset = stream->offset; hist1 = read_16bitLE(header_offset,stream->streamfile); step_index = read_16bitLE(header_offset+2,stream->streamfile); - - //todo clip step_index? + if (step_index < 0) step_index=0; /* probably pre-adjusted */ + if (step_index > 88) step_index=88; } + /* decode nibbles (layout: all nibbles from the channel) */ for (i=first_sample,sample_count=0; ioffset + 4 + i/2; - int nibble_shift = (i&1?4:0); //low nibble first + off_t byte_offset = stream->offset + 0x04 + i/2; + int nibble_shift = (i&1?4:0); /* low nibble first */ + //todo waveform has minor deviations using known expands std_ima_expand_nibble(stream, byte_offset,nibble_shift, &hist1, &step_index); outbuf[sample_count] = (short)(hist1); } - stream->adpcm_history1_16 = hist1; + stream->adpcm_history1_32 = hist1; stream->adpcm_step_index = step_index; } void decode_dat4_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) { int i, sample_count; - int32_t hist1 = stream->adpcm_history1_16;//todo unneeded 16? int step_index = stream->adpcm_step_index; @@ -548,7 +550,6 @@ void decode_dat4_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspac void decode_rad_ima(VGMSTREAM * vgmstream,VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do,int channel) { int i, sample_count; - int32_t hist1 = stream->adpcm_history1_32; int step_index = stream->adpcm_step_index; @@ -583,7 +584,6 @@ void decode_rad_ima(VGMSTREAM * vgmstream,VGMSTREAMCHANNEL * stream, sample * ou void decode_rad_ima_mono(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) { int i, sample_count; - int32_t hist1 = stream->adpcm_history1_32; int step_index = stream->adpcm_step_index; @@ -647,91 +647,99 @@ void decode_apple_ima4(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelsp /* XBOX-IMA with modified data layout */ void decode_fsb_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do,int channel) { - int i, sample_count; - + int i, sample_count = 0; int32_t hist1 = stream->adpcm_history1_32; int step_index = stream->adpcm_step_index; - //internal interleave - int block_samples = (0x24 - 4) * 2; /* block size - header, 2 samples per byte */ + /* internal interleave (configurable size), mixed channels */ + int block_samples = (0x24 - 0x4) * 2; first_sample = first_sample % block_samples; - //interleaved header (all hist per channel + all step_index per channel) + /* interleaved header (all hist per channel + all step_index+reserved per channel) */ if (first_sample == 0) { - off_t hist_offset = stream->offset + 2*channel; - off_t step_offset = stream->offset + 2*channel + 2*vgmstream->channels; + off_t hist_offset = stream->offset + 0x02*channel + 0x00; + off_t step_offset = stream->offset + 0x02*channel + 0x02*vgmstream->channels; - hist1 = read_16bitLE(hist_offset,stream->streamfile); + hist1 = read_16bitLE(hist_offset,stream->streamfile); step_index = read_8bit(step_offset,stream->streamfile); if (step_index < 0) step_index=0; if (step_index > 88) step_index=88; - } - for (i=first_sample,sample_count=0; ioffset + 4*vgmstream->channels + 2*channel + i/4*2*vgmstream->channels + (i%4)/2;//2-byte per channel - int nibble_shift = (i&1?4:0); //low nibble first - - std_ima_expand_nibble(stream, byte_offset,nibble_shift, &hist1, &step_index); - outbuf[sample_count] = (short)(hist1); - } - - //internal interleave: increment offset on complete frame - if (i == block_samples) stream->offset += 36*vgmstream->channels; - - stream->adpcm_history1_32 = hist1; - stream->adpcm_step_index = step_index; -} - -/* XBOX-IMA with modified data layout */ -void decode_wwise_ima(VGMSTREAM * vgmstream,VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { - int i, sample_count = 0; - - int32_t hist1 = stream->adpcm_history1_32; - int step_index = stream->adpcm_step_index; - - //internal interleave (configurable size), block-interleave multichannel (ex. if block is 0xD8 in 6ch: 6 blocks of 4+0x20) - int block_samples = (vgmstream->interleave_block_size - 4*vgmstream->channels) * 2 / vgmstream->channels; - first_sample = first_sample % block_samples; - - //block-interleaved header (1 header per channel block); can be LE or BE - if (first_sample == 0) { - int16_t (*read_16bit)(off_t,STREAMFILE*) = vgmstream->codec_endian ? read_16bitBE : read_16bitLE; - off_t header_offset = stream->offset + (vgmstream->interleave_block_size / vgmstream->channels)*channel; - - hist1 = read_16bit(header_offset,stream->streamfile); - step_index = read_8bit(header_offset+2,stream->streamfile); - if (step_index < 0) step_index=0; - if (step_index > 88) step_index=88; - - //must write history from header as last nibble/sample in block is almost always 0 / not encoded + /* write header sample (even samples per block, skips last nibble) */ outbuf[sample_count] = (short)(hist1); sample_count += channelspacing; first_sample += 1; samples_to_do -= 1; } - for (i=first_sample; i < first_sample + samples_to_do; i++) { /* first_sample + samples_to_do should be block_samples at most */ - off_t byte_offset = stream->offset + (vgmstream->interleave_block_size / vgmstream->channels)*channel + 4 + (i-1)/2; - int nibble_shift = ((i-1)&1?4:0); //low nibble first + /* decode nibbles (layout: 2 bytes/2*2 nibbles per channel) */ + for (i = first_sample; i < first_sample + samples_to_do; i++) { + off_t byte_offset = stream->offset + 0x04*vgmstream->channels + 0x02*channel + (i-1)/4*2*vgmstream->channels + ((i-1)%4)/2; + int nibble_shift = ((i-1)&1?4:0); /* low nibble first */ - //last nibble/sample in block is ignored (next header sample contains it) + /* must skip last nibble per official decoder, probably not needed though */ if (i < block_samples) { std_ima_expand_nibble(stream, byte_offset,nibble_shift, &hist1, &step_index); outbuf[sample_count] = (short)(hist1); - sample_count+=channelspacing; + sample_count += channelspacing; } } - //internal interleave: increment offset on complete frame - if (i == block_samples) stream->offset += vgmstream->interleave_block_size; + /* internal interleave: increment offset on complete frame */ + if (i == block_samples) { + stream->offset += 0x24*vgmstream->channels; + } stream->adpcm_history1_32 = hist1; stream->adpcm_step_index = step_index; } -//todo atenuation: apparently from hcs's analysis Wwise IMA expands nibbles slightly different, reducing clipping/dbs -/* -From Wwise_v2015.1.6_Build5553_SDK.Linux -<_ZN13CAkADPCMCodec12DecodeSampleEiii>: + +/* mono XBOX-IMA with header endianness and alt nibble expand (per hcs's decompilation) */ +void decode_wwise_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { + int i, sample_count = 0, num_frame; + int32_t hist1 = stream->adpcm_history1_32; + int step_index = stream->adpcm_step_index; + + /* external interleave (fixed size), mono */ + int block_samples = (0x24 - 0x4) * 2; + num_frame = first_sample / block_samples; + first_sample = first_sample % block_samples; + + /* normal header (hist+step+reserved), single channel */ + if (first_sample == 0) { + int16_t (*read_16bit)(off_t,STREAMFILE*) = vgmstream->codec_endian ? read_16bitBE : read_16bitLE; + off_t header_offset = stream->offset + 0x24*num_frame; + + hist1 = read_16bit(header_offset,stream->streamfile); + step_index = read_8bit(header_offset+2,stream->streamfile); + if (step_index < 0) step_index=0; + if (step_index > 88) step_index=88; + + /* write header sample (even samples per block, skips last nibble) */ + outbuf[sample_count] = (short)(hist1); + sample_count += channelspacing; + first_sample += 1; + samples_to_do -= 1; + } + + /* decode nibbles (layout: all nibbles from one channel) */ + for (i = first_sample; i < first_sample + samples_to_do; i++) { + off_t byte_offset = (stream->offset + 0x24*num_frame + 0x4) + (i-1)/2; + int nibble_shift = ((i-1)&1?4:0); /* low nibble first */ + + /* must skip last nibble like other XBOX-IMAs, often needed (ex. Bayonetta 2 sfx) */ + if (i < block_samples) { + std_ima_expand_nibble_mul(stream, byte_offset,nibble_shift, &hist1, &step_index); + outbuf[sample_count] = (short)(hist1); + sample_count += channelspacing; + } + } + + stream->adpcm_history1_32 = hist1; + stream->adpcm_step_index = step_index; +} +/* from hcs's analysis Wwise IMA expands nibbles slightly different, reducing dbs. Just "MUL" expand? +<_ZN13CAkADPCMCodec12DecodeSampleEiii>: //From Wwise_v2015.1.6_Build5553_SDK.Linux 10: 83 e0 07 and $0x7,%eax ; sample 13: 01 c0 add %eax,%eax ; sample*2 15: 83 c0 01 add $0x1,%eax ; sample*2+1 @@ -740,8 +748,6 @@ From Wwise_v2015.1.6_Build5553_SDK.Linux 1f: 85 c0 test %eax,%eax ; result negative? 21: 0f 48 c2 cmovs %edx,%eax ; adjust if negative to fix rounding for below division 24: c1 f8 03 sar $0x3,%eax ; (sample*2+1)*scale/8 - -Different rounding model vs IMA's shift-and-add (also "adjust" step may be unnecessary). */ /* MS-IMA with possibly the XBOX-IMA model of even number of samples per block (more tests are needed) */ @@ -832,7 +838,7 @@ void decode_ubi_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspaci (!(i%2) ? 4:0) : /* mono mode (high first) */ (channel==0 ? 4:0); /* stereo mode (high=L,low=R) */ - ubi_ima_expand_nibble(stream, byte_offset,nibble_shift, &hist1, &step_index); + std_ima_expand_nibble_mul(stream, byte_offset,nibble_shift, &hist1, &step_index); outbuf[sample_count] = (short)(hist1); /* all samples are written */ } @@ -849,7 +855,7 @@ size_t ima_bytes_to_samples(size_t bytes, int channels) { } size_t ms_ima_bytes_to_samples(size_t bytes, int block_align, int channels) { - /* MS IMA blocks have a 4 byte header per channel; 2 samples per byte (2 nibbles) */ + /* MS-IMA blocks have a 4 byte header per channel; 2 samples per byte (2 nibbles) */ return (bytes / block_align) * ((block_align - 0x04*channels) * 2 / channels + 1) + ((bytes % block_align) ? (((bytes % block_align) - 0x04*channels) * 2 / channels + 1) : 0); } @@ -858,7 +864,7 @@ size_t xbox_ima_bytes_to_samples(size_t bytes, int channels) { int block_align = 0x24 * channels; /* XBOX IMA blocks have a 4 byte header per channel; 2 samples per byte (2 nibbles) */ return (bytes / block_align) * (block_align - 4 * channels) * 2 / channels - + ((bytes % block_align) ? ((bytes % block_align) - 4 * channels) * 2 / channels : 0); //todo probably not possible (aligned) + + ((bytes % block_align) ? ((bytes % block_align) - 4 * channels) * 2 / channels : 0); /* unlikely (encoder aligns) */ } size_t ubi_ima_bytes_to_samples(size_t bytes, int channels, STREAMFILE *streamFile, off_t offset) { diff --git a/src/formats.c b/src/formats.c index 445b64e3..0395c1b1 100644 --- a/src/formats.c +++ b/src/formats.c @@ -105,6 +105,7 @@ static const char* extension_list[] = { "dvi", "dxh", + "e4x", "eam", "emff", "enth", @@ -379,7 +380,7 @@ static const char* extension_list[] = { "xss", "xvag", "xvas", - "xwav", + "xwav",//fake, to be removed "xwb", "xwc", "xwm", //FFmpeg, not parsed (XWMA) @@ -495,6 +496,7 @@ static const coding_info coding_info_list[] = { {coding_MTAF, "Konami MTAF 4-bit ADPCM"}, {coding_MTA2, "Konami MTA2 4-bit ADPCM"}, {coding_MC3, "Paradigm MC3 3-bit ADPCM"}, + {coding_FADPCM, "FMOD FADPCM 4-bit ADCPM"}, {coding_SDX2, "Squareroot-delta-exact (SDX2) 8-bit DPCM"}, {coding_SDX2_int, "Squareroot-delta-exact (SDX2) 8-bit DPCM with 1 byte interleave"}, @@ -565,13 +567,13 @@ static const layout_info layout_info_list[] = { {layout_thp_blocked, "THP Movie Audio blocked"}, {layout_filp_blocked, "FILp blocked"}, {layout_blocked_ea_swvr, "blocked (EA SWVR)"}, - {layout_ps2_adm_blocked, "ADM blocked"}, + {layout_blocked_adm, "blocked (ADM)"}, {layout_dsp_bdsp_blocked, "DSP blocked"}, {layout_blocked_ivaud, "blocked (IVAUD)"}, {layout_ps2_iab_blocked, "IAB blocked"}, {layout_ps2_strlr_blocked, "The Bouncer STR blocked"}, - {layout_rws_blocked, "RWS blocked"}, - {layout_hwas_blocked, "HWAS blocked"}, + {layout_blocked_rws, "blocked (RWS)"}, + {layout_blocked_hwas, "blocked (HWAS)"}, {layout_tra_blocked, "TRA blocked"}, {layout_acm, "ACM blocked"}, {layout_mus_acm, "multiple ACM files, ACM blocked"}, @@ -642,7 +644,6 @@ static const meta_info meta_info_list[] = { {meta_PS2_ILD, "ILD header"}, {meta_PS2_PNB, "assumed PNB (PsychoNauts Bgm File) by .pnb extension"}, {meta_XBOX_WAVM, "Xbox WAVM raw header"}, - {meta_XBOX_RIFF, "Microsoft XWAV RIFF header"}, {meta_DSP_STR, "assumed Conan Gamecube STR File by .str extension"}, {meta_EA_SCHL, "Electronic Arts SCHl header (variable)"}, {meta_EA_SCHL_fixed, "Electronic Arts SCHl header (fixed)"}, @@ -700,6 +701,7 @@ static const meta_info meta_info_list[] = { {meta_PS2_PSH, "Dawn of Mana - Seiken Densetsu 4 PSH Header"}, {meta_RIFF_WAVE_labl, "RIFF WAVE header with loop markers"}, {meta_RIFF_WAVE_smpl, "RIFF WAVE header with sample looping info"}, + {meta_RIFF_WAVE_wsmp, "RIFF WAVE header with wsmp looping info"}, {meta_RIFX_WAVE, "RIFX WAVE header"}, {meta_RIFX_WAVE_smpl, "RIFX WAVE header with sample looping info"}, {meta_XNB, "Microsoft XNA Game Studio 4.0 header"}, @@ -844,6 +846,7 @@ static const meta_info meta_info_list[] = { {meta_DSP_XIII, "XIII dsp header"}, {meta_NGC_DSP_STH_STR, "STH dsp header"}, {meta_DSP_CABELAS, "Cabelas games dsp header"}, + {meta_PS2_ADM, "Dragon Quest V .ADM raw header"}, {meta_PS2_LPCM, "LPCM header"}, {meta_PS2_VMS, "VMS Header"}, {meta_XAU, "XPEC XAU header"}, diff --git a/src/layout/blocked.c b/src/layout/blocked.c index b6b1530f..b910cd9d 100644 --- a/src/layout/blocked.c +++ b/src/layout/blocked.c @@ -131,8 +131,8 @@ void render_vgmstream_blocked(sample * buffer, int32_t sample_count, VGMSTREAM * case layout_blocked_ea_swvr: block_update_ea_swvr(vgmstream->next_block_offset,vgmstream); break; - case layout_ps2_adm_blocked: - ps2_adm_block_update(vgmstream->next_block_offset,vgmstream); + case layout_blocked_adm: + block_update_adm(vgmstream->next_block_offset,vgmstream); break; case layout_dsp_bdsp_blocked: dsp_bdsp_block_update(vgmstream->next_block_offset,vgmstream); @@ -146,11 +146,11 @@ void render_vgmstream_blocked(sample * buffer, int32_t sample_count, VGMSTREAM * case layout_ps2_strlr_blocked: ps2_strlr_block_update(vgmstream->next_block_offset,vgmstream); break; - case layout_rws_blocked: - rws_block_update(vgmstream->next_block_offset,vgmstream); + case layout_blocked_rws: + block_update_rws(vgmstream->next_block_offset,vgmstream); break; - case layout_hwas_blocked: - hwas_block_update(vgmstream->next_block_offset,vgmstream); + case layout_blocked_hwas: + block_update_hwas(vgmstream->next_block_offset,vgmstream); break; case layout_blocked_ea_sns: block_update_ea_sns(vgmstream->next_block_offset,vgmstream); diff --git a/src/layout/blocked_adm.c b/src/layout/blocked_adm.c new file mode 100644 index 00000000..867535a1 --- /dev/null +++ b/src/layout/blocked_adm.c @@ -0,0 +1,48 @@ +#include "layout.h" +#include "../vgmstream.h" + +/* blocks of 0x1000 with interleave 0x400 but also smaller last interleave */ +void block_update_adm(off_t block_offset, VGMSTREAM * vgmstream) { + STREAMFILE* streamFile = vgmstream->ch[0].streamfile; + int i, new_full_block; + size_t block_size, interleave_size, interleave_data; + + /* no header */ + interleave_size = 0x400; + interleave_data = 0x400; + block_size = interleave_size * vgmstream->channels; + + /* every 0x1000 is a full block, signaled by PS-ADPCM flags */ + new_full_block = (read_8bit(block_offset+0x01, streamFile) == 0x06); + + /* try to autodetect usable interleave data size as can be smaller when a discrete block ends (ex. 0x10~0x50, varies with file) */ + if (!new_full_block) { + off_t next_block_offset = block_offset + block_size; + + while (next_block_offset > block_offset) { + next_block_offset -= 0x10; + + /* check if unused line (all blocks should only use flags 0x06/0x03/0x02) */ + if (read_32bitLE(next_block_offset, streamFile) == 0x00000000) { + interleave_data -= 0x10; + next_block_offset -= 0x10; + } + else { + break; + } + } + } + + vgmstream->current_block_offset = block_offset; + vgmstream->next_block_offset = block_offset + block_size; + vgmstream->current_block_size = interleave_data; + + for (i = 0; i < vgmstream->channels; i++) { + vgmstream->ch[i].offset = block_offset + interleave_size*i; + + //if (new_full_block) { /* blocks are not discrete */ + // vgmstream->ch[i].adpcm_history1_32 = 0; + // vgmstream->ch[i].adpcm_step_index = 0; + //} + } +} diff --git a/src/layout/hwas_blocked.c b/src/layout/blocked_hwas.c similarity index 90% rename from src/layout/hwas_blocked.c rename to src/layout/blocked_hwas.c index f55a40fc..dbd4fd70 100644 --- a/src/layout/hwas_blocked.c +++ b/src/layout/blocked_hwas.c @@ -2,7 +2,7 @@ #include "../vgmstream.h" /* a simple headerless block with special adpcm history handling */ -void hwas_block_update(off_t block_offset, VGMSTREAM * vgmstream) { +void block_update_hwas(off_t block_offset, VGMSTREAM * vgmstream) { int i; size_t block_size; diff --git a/src/layout/rws_blocked.c b/src/layout/blocked_rws.c similarity index 87% rename from src/layout/rws_blocked.c rename to src/layout/blocked_rws.c index 23b90c53..7d3fee2a 100644 --- a/src/layout/rws_blocked.c +++ b/src/layout/blocked_rws.c @@ -2,7 +2,7 @@ #include "../vgmstream.h" /* a simple headerless block with padding; configured in the main header */ -void rws_block_update(off_t block_offset, VGMSTREAM * vgmstream) { +void block_update_rws(off_t block_offset, VGMSTREAM * vgmstream) { int i; size_t block_size; size_t interleave; diff --git a/src/layout/layout.h b/src/layout/layout.h index 466281ef..41ba7735 100644 --- a/src/layout/layout.h +++ b/src/layout/layout.h @@ -45,10 +45,8 @@ void thp_block_update(off_t block_offset, VGMSTREAM * vgmstream); void filp_block_update(off_t block_offset, VGMSTREAM * vgmstream); void block_update_ivaud(off_t block_offset, VGMSTREAM * vgmstream); - void block_update_ea_swvr(off_t block_offset, VGMSTREAM * vgmstream); - -void ps2_adm_block_update(off_t block_offset, VGMSTREAM * vgmstream); +void block_update_adm(off_t block_offset, VGMSTREAM * vgmstream); void dsp_bdsp_block_update(off_t block_offset, VGMSTREAM * vgmstream); @@ -58,10 +56,8 @@ void ps2_iab_block_update(off_t block_offset, VGMSTREAM * vgmstream); void ps2_strlr_block_update(off_t block_offset, VGMSTREAM * vgmstream); -void rws_block_update(off_t block_offset, VGMSTREAM * vgmstream); - -void hwas_block_update(off_t block_offset, VGMSTREAM * vgmstream); - +void block_update_rws(off_t block_offset, VGMSTREAM * vgmstream); +void block_update_hwas(off_t block_offset, VGMSTREAM * vgmstream); void block_update_ea_sns(off_t block_offset, VGMSTREAM * vgmstream); void block_update_awc(off_t block_offset, VGMSTREAM * vgmstream); void block_update_vgs(off_t block_offset, VGMSTREAM * vgmstream); diff --git a/src/layout/ps2_adm_blocked.c b/src/layout/ps2_adm_blocked.c deleted file mode 100644 index 87c7c606..00000000 --- a/src/layout/ps2_adm_blocked.c +++ /dev/null @@ -1,18 +0,0 @@ -#include "layout.h" -#include "../vgmstream.h" - -/* set up for the block at the given offset */ -void ps2_adm_block_update(off_t block_offset, VGMSTREAM * vgmstream) { - int i; - - vgmstream->current_block_offset = block_offset; - vgmstream->current_block_size = 0x1000; /*read_32bitLE( - vgmstream->current_block_offset+0x10, - vgmstream->ch[0].streamfile); */ - vgmstream->next_block_offset = vgmstream->current_block_offset + vgmstream->current_block_size; - //vgmstream->current_block_size/=vgmstream->channels; - - for (i=0;ichannels;i++) { - vgmstream->ch[i].offset = vgmstream->current_block_offset+(0x400*i); - } -} diff --git a/src/libvgmstream.vcproj b/src/libvgmstream.vcproj index 246cac1a..94fd9d95 100644 --- a/src/libvgmstream.vcproj +++ b/src/libvgmstream.vcproj @@ -1366,10 +1366,6 @@ RelativePath=".\meta\xbox_xvas.c" > - - @@ -1482,6 +1478,10 @@ RelativePath=".\coding\ea_xas_decoder.c" > + + @@ -1759,7 +1759,7 @@ > - @@ -430,6 +429,7 @@ + @@ -486,10 +486,10 @@ - + - - + + diff --git a/src/libvgmstream.vcxproj.filters b/src/libvgmstream.vcxproj.filters index 0827fb9b..d8543b19 100644 --- a/src/libvgmstream.vcxproj.filters +++ b/src/libvgmstream.vcxproj.filters @@ -832,9 +832,6 @@ meta\Source Files - - meta\Source Files - meta\Source Files @@ -874,6 +871,9 @@ coding\Source Files + + coding\Source Files + coding\Source Files @@ -1021,7 +1021,7 @@ layout\Source Files - + layout\Source Files @@ -1042,13 +1042,13 @@ layout\Source Files - + layout\Source Files layout\Source Files - + layout\Source Files diff --git a/src/meta/fsb5.c b/src/meta/fsb5.c index 99bf7d51..da2c2f41 100644 --- a/src/meta/fsb5.c +++ b/src/meta/fsb5.c @@ -110,7 +110,7 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) { break; case 0x03: /* Loop Info */ LoopStart = read_32bitLE(ExtraFlagStart+0x04,streamFile); - if (ExtraFlagSize > 0x04) /* probably no needed */ + if (ExtraFlagSize > 0x04) /* probably not needed */ LoopEnd = read_32bitLE(ExtraFlagStart+0x08,streamFile); /* when start is 0 seems the song repeats with no real looping (ex. Sonic Boom Fire & Ice jingles) */ @@ -118,9 +118,9 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) { break; case 0x04: /* free comment, or maybe SFX info */ break; - //case 0x05: /* Unknown (32b) */ - // /* found in Tearaway Vita, value 0, first stream only */ - // break; + //case 0x05: /* Unknown (32b) */ + // /* found in Tearaway Vita, value 0, first stream only */ + // break; case 0x06: /* XMA seek table */ /* no need for it */ break; @@ -142,11 +142,11 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) { * (xN entries) */ break; - //case 0x0d: /* Unknown (32b) */ - // /* found in some XMA2 and Vorbis */ - // break; + //case 0x0d: /* Unknown (32b) */ + // /* found in some XMA2/Vorbis/FADPCM */ + // break; default: - VGM_LOG("FSB5: unknown extra flag 0x%x at 0x%04x (size 0x%x)\n", ExtraFlagType, ExtraFlagStart, ExtraFlagSize); + VGM_LOG("FSB5: unknown extra flag 0x%x at 0x%04x + 0x04 (size 0x%x)\n", ExtraFlagType, ExtraFlagStart, ExtraFlagSize); break; } @@ -336,7 +336,10 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) { #endif case 0x10: /* FMOD_SOUND_FORMAT_FADPCM */ - goto fail; + vgmstream->coding_type = coding_FADPCM; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x8c; + break; default: goto fail; diff --git a/src/meta/fsb_keys.h b/src/meta/fsb_keys.h index 1b3a6081..75bf9111 100644 --- a/src/meta/fsb_keys.h +++ b/src/meta/fsb_keys.h @@ -63,6 +63,9 @@ static const uint8_t key_inv[] = { 0x6D,0x69,0x6E,0x74,0x37,0x38,0x72,0x75,0x6E, /* Guitar Hero 3 */ //"5atu6w4zaw" static const uint8_t key_gh3[] = { 0x35,0x61,0x74,0x75,0x36,0x77,0x34,0x7A,0x61,0x77 }; +/* Supreme Commander 2 */ //"B2A7BB00" +static const uint8_t key_sc2[] = { 0x42,0x32,0x41,0x37,0x42,0x42,0x30,0x30 }; + // Unknown: // - Battle: Los Angeles // - Guitar Hero: Warriors of Rock, DJ hero FSB @@ -118,6 +121,10 @@ static const fsbkey_info fsbkey_list[] = { { 0,1, sizeof(key_gh3),key_gh3 },//untested { 1,0, sizeof(key_gh3),key_gh3 },//untested { 1,1, sizeof(key_gh3),key_gh3 },//untested + { 0,0, sizeof(key_sc2),key_sc2 },//untested + { 0,1, sizeof(key_sc2),key_sc2 },//untested + { 1,0, sizeof(key_sc2),key_sc2 },//untested + { 1,1, sizeof(key_sc2),key_sc2 },//untested }; static const int fsbkey_list_count = sizeof(fsbkey_list) / sizeof(fsbkey_list[0]); diff --git a/src/meta/meta.h b/src/meta/meta.h index 89e049d6..80dc0622 100644 --- a/src/meta/meta.h +++ b/src/meta/meta.h @@ -76,8 +76,6 @@ VGMSTREAM * init_vgmstream_ps2_pnb(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_xbox_wavm(STREAMFILE *streamFile); -VGMSTREAM * init_vgmstream_xbox_xwav(STREAMFILE *streamFile); - VGMSTREAM * init_vgmstream_ngc_str(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_caf(STREAMFILE *streamFile); diff --git a/src/meta/mss.c b/src/meta/mss.c index c7a1d15e..e3596dae 100644 --- a/src/meta/mss.c +++ b/src/meta/mss.c @@ -18,7 +18,7 @@ VGMSTREAM * init_vgmstream_mss(STREAMFILE *streamFile) { loop_flag = 0; channel_count = read_16bitLE(0x16,streamFile); - if (read_32bitLE(0x18,streamFile) == 0x4800 && vgmstream->channels > 2) + if (read_32bitLE(0x18,streamFile) == 0x4800 && channel_count > 2) channel_count = 2; //todo add support for interleave stereo streams /* build the VGMSTREAM */ diff --git a/src/meta/nds_hwas.c b/src/meta/nds_hwas.c index e50e614e..9c9449c1 100644 --- a/src/meta/nds_hwas.c +++ b/src/meta/nds_hwas.c @@ -34,14 +34,14 @@ VGMSTREAM * init_vgmstream_nds_hwas(STREAMFILE *streamFile) { vgmstream->meta_type = meta_NDS_HWAS; vgmstream->coding_type = coding_IMA_int; - vgmstream->layout_type = layout_hwas_blocked; + vgmstream->layout_type = layout_blocked_hwas; vgmstream->full_block_size = read_32bitLE(0x04,streamFile); /* usually 0x2000, 0x4000 or 0x8000 */ /* open the file for reading by each channel */ if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) goto fail; - hwas_block_update(start_offset, vgmstream); + block_update_hwas(start_offset, vgmstream); return vgmstream; diff --git a/src/meta/pc_xa30.c b/src/meta/pc_xa30.c index f6662cb1..7b615bcd 100644 --- a/src/meta/pc_xa30.c +++ b/src/meta/pc_xa30.c @@ -1,37 +1,36 @@ #include "meta.h" #include "../coding/coding.h" -/* XA30 - found in Driver: Parallel Lines (PC) */ +/* XA30 - found in Reflections games [Driver: Parallel Lines (PC), Driver 3 (PC)] */ VGMSTREAM * init_vgmstream_pc_xa30(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; off_t start_offset; int loop_flag, channel_count, codec; - size_t file_size, data_size; + size_t stream_size; + int total_subsongs, target_subsong = streamFile->stream_index; /* check extension, case insensitive */ - /* ".xa30" is just the ID, the real filename should be .XA */ - if (!check_extensions(streamFile,"xa,xa30")) + /* ".xa30/e4x" is just the ID, the real filename should be .XA */ + if (!check_extensions(streamFile,"xa,xa30,e4x")) goto fail; /* check header */ - if (read_32bitBE(0x00,streamFile) != 0x58413330) /* "XA30" */ + if (read_32bitBE(0x00,streamFile) != 0x58413330 && /* "XA30" [Driver: Parallel Lines (PC)]*/ + read_32bitBE(0x00,streamFile) != 0x65347892) /* "e4x\92" [Driver 3 (PC)]*/ + goto fail; + if (read_32bitLE(0x04,streamFile) != 2) /* channels?, also extra check to avoid PS2/PC XA30 mixup */ goto fail; - if (read_32bitLE(0x04,streamFile) > 2) goto fail; /* extra check to avoid PS2/PC XA30 mixup */ - - /* reportedly from XA2WAV those are offset+data from a second stream (not seen) */ - if (read_32bitLE(0x14,streamFile) != 0 || read_32bitLE(0x1c,streamFile) != 0) goto fail; + total_subsongs = read_32bitLE(0x14,streamFile) != 0 ? 2 : 1; /* second stream offset (only in Driver 3) */ + if (target_subsong == 0) target_subsong = 1; + if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail; loop_flag = 0; channel_count = 2; /* 0x04: channels? (always 2 in practice) */ - - codec = read_32bitLE(0x0c,streamFile); /* reportedly from XA2WAV (not seen) */ - start_offset = read_32bitLE(0x10,streamFile); - - file_size = get_streamfile_size(streamFile); - data_size = read_32bitLE(0x18,streamFile); - if (data_size+start_offset != file_size) goto fail; + codec = read_32bitLE(0x0c,streamFile); + start_offset = read_32bitLE(0x10 + 0x04*(target_subsong-1),streamFile); + stream_size = read_32bitLE(0x18 + 0x04*(target_subsong-1),streamFile); /* build the VGMSTREAM */ @@ -39,19 +38,26 @@ VGMSTREAM * init_vgmstream_pc_xa30(STREAMFILE *streamFile) { if (!vgmstream) goto fail; vgmstream->sample_rate = read_32bitLE(0x08,streamFile); - /* 0x20: always 00016000?, rest of the header is null */ - + /* 0x20: always IMA=00016000, PCM=00056000 PCM?, rest of the header is null */ + vgmstream->num_streams = total_subsongs; + vgmstream->stream_size = stream_size; vgmstream->meta_type = meta_PC_XA30; switch(codec) { + case 0x00: /* PCM (rare, seen in Driver 3) */ + vgmstream->coding_type = coding_PCM16LE; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = read_32bitLE(0x24,streamFile) / 2; + vgmstream->num_samples = pcm_bytes_to_samples(stream_size, channel_count, 16); + break; + case 0x01: /* MS-IMA variation */ vgmstream->coding_type = coding_REF_IMA; vgmstream->layout_type = layout_none; vgmstream->interleave_block_size = read_32bitLE(0x24,streamFile); - vgmstream->num_samples = ms_ima_bytes_to_samples(data_size, vgmstream->interleave_block_size, channel_count); + vgmstream->num_samples = ms_ima_bytes_to_samples(stream_size, vgmstream->interleave_block_size, channel_count); break; - case 0x00: /* PCM? */ default: goto fail; } diff --git a/src/meta/ps2_adm.c b/src/meta/ps2_adm.c index 82e2f51b..cd23625f 100644 --- a/src/meta/ps2_adm.c +++ b/src/meta/ps2_adm.c @@ -1,75 +1,109 @@ #include "meta.h" #include "../layout/layout.h" -#include "../util.h" +#include "../coding/coding.h" +#include -/* WAD (from The golden Compass) */ +static int get_adm_loop_info(STREAMFILE *streamFile, off_t *loop_start_offset); + +/* .adm - from Dragon Quest V (PS2) */ VGMSTREAM * init_vgmstream_ps2_adm(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; - int loop_flag = 0; - int channel_count; - int i; - off_t start_offset; - + int channel_count, loop_flag = 0; + off_t start_offset, loop_start_offset = 0; + /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("adm",filename_extension(filename))) goto fail; + if (!check_extensions(streamFile,"adm")) + goto fail; - loop_flag = 0; - channel_count = 2; - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); - if (!vgmstream) goto fail; - - /* fill in the vital statistics */ - start_offset = 0x0; - vgmstream->channels = channel_count; - vgmstream->sample_rate = 44100; - vgmstream->coding_type = coding_PSX; - -#if 0 - vgmstream->num_samples = read_32bitLE(0x0,streamFile)/channel_count/16*28; - if (loop_flag) { - vgmstream->loop_start_sample = 0; - vgmstream->loop_end_sample = read_32bitLE(0x0,streamFile)/channel_count/16*28; - } -#endif - - vgmstream->layout_type = layout_ps2_adm_blocked; - vgmstream->interleave_block_size = 0x400; - - - vgmstream->meta_type = meta_PS2_ADM; - - /* open the file for reading by each channel */ + /* raw data, but test some .ADM blocks as they always start with PS-ADPCM flag 0x06 every 0x1000 */ { - for (i=0;ich[i].streamfile = streamFile->open(streamFile,filename,vgmstream->interleave_block_size); - - if (!vgmstream->ch[i].streamfile) goto fail; - vgmstream->ch[i].channel_start_offset= - vgmstream->ch[i].offset=i*vgmstream->interleave_block_size; + int i; + for (i = 0; i < 10; i++) { + if (read_8bit(0x1000*i + 0x01, streamFile) != 0x06) + goto fail; } } + start_offset = 0x00; + loop_flag = get_adm_loop_info(streamFile, &loop_start_offset); + channel_count = 2; - /* Calc num_samples */ - ps2_adm_block_update(start_offset,vgmstream); - vgmstream->num_samples=0; //(get_streamfile_size(streamFile)/0x1000*0xFE0)/32*28; + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count,loop_flag); + if (!vgmstream) goto fail; - do { - - vgmstream->num_samples += 0xFE0*14/16; - ps2_adm_block_update(vgmstream->next_block_offset,vgmstream); - } while (vgmstream->next_block_offsetsample_rate = 44100; + vgmstream->meta_type = meta_PS2_ADM; - ps2_adm_block_update(start_offset,vgmstream); + vgmstream->coding_type = coding_PSX; + vgmstream->layout_type = layout_blocked_adm; + if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + goto fail; + + /* calc num_samples as playable data size varies between files/blocks */ + vgmstream->num_samples = 0; //ps_bytes_to_samples(get_streamfile_size(streamFile), channel_count); + block_update_adm(start_offset,vgmstream); + while (vgmstream->next_block_offset < get_streamfile_size(streamFile)) { + if (loop_flag && vgmstream->current_block_offset == loop_start_offset) + vgmstream->loop_start_sample = vgmstream->num_samples; + + vgmstream->num_samples += ps_bytes_to_samples(vgmstream->current_block_size * channel_count, channel_count); + + block_update_adm(vgmstream->next_block_offset,vgmstream); + } + + + if (loop_flag) + vgmstream->loop_end_sample = vgmstream->num_samples; + + + block_update_adm(start_offset,vgmstream); return vgmstream; - /* clean up anything we may have opened */ fail: - if (vgmstream) close_vgmstream(vgmstream); + close_vgmstream(vgmstream); return NULL; } + +/* loops are not in the .ADM or .DAT bigfile containing them but in the exe; manually get them (a bit meh but whatevs) */ +static int get_adm_loop_info(STREAMFILE *streamFile, off_t *loop_start_offset) { + char file_name[PATH_LIMIT]; + char index_name[PATH_LIMIT]; + STREAMFILE *streamExe = NULL; + int i, name_index = -1, loop_flag; + off_t offset; + + streamExe = open_stream_name(streamFile, "SLPM_655.55"); + if (!streamExe) goto fail; + + get_streamfile_filename(streamFile, file_name, PATH_LIMIT); + + /* get file index from name list (file_name == index_name = index number */ + offset = 0x23B3c0; + for (i = 0; i < 51; i++) { + read_string(index_name,0x20+1, offset,streamExe); + + if (strcmp(index_name, file_name)==0) { + name_index = i; + break; + } + offset += 0x20; + } + if (name_index < 0) + goto fail; + + /* get file info using index */ + offset = 0x23BAEC + 0x1c*name_index; + loop_flag = (read_32bitLE(offset + 0x10, streamExe) == 0); /* 1: don't loop, 0: loop */ + if (loop_flag) { /* loop flag */ + *loop_start_offset = read_32bitLE(offset + 0x04, streamExe); + } + /* 0x08: num_samples/loop_end, 0x0c: sample rate (always 44100), 0x14/18: some size? */ + + close_streamfile(streamExe); + return loop_flag; +fail: + close_streamfile(streamExe); + return 0; +} diff --git a/src/meta/riff.c b/src/meta/riff.c index 04d0cc14..7b810ae5 100644 --- a/src/meta/riff.c +++ b/src/meta/riff.c @@ -131,7 +131,7 @@ static int read_fmt(int big_endian, STREAMFILE * streamFile, off_t current_chunk fmt->coding_type = coding_MS_IMA; break; - case 0x69: /* XBOX IMA ADPCM [Rayman Raving Rabbids 2 (PC) --maybe waa/wac/wam/wad?] */ + case 0x69: /* XBOX IMA ADPCM [Dynasty Warriors 5 (Xbox), Rayman Raving Rabbids 2 (PC) --maybe waa/wac/wam/wad?] */ if (fmt->bps != 4) goto fail; fmt->coding_type = coding_XBOX_IMA; break; @@ -232,10 +232,9 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { int fact_sample_skip = 0; int loop_flag = 0; - long loop_start_ms = -1; - long loop_end_ms = -1; - off_t loop_start_offset = -1; - off_t loop_end_offset = -1; + int loop_start_sample = 0, loop_end_sample = 0; + long loop_start_ms = -1, loop_end_ms = -1; + off_t loop_start_offset = -1, loop_end_offset = -1; int FormatChunkFound = 0, DataChunkFound = 0, JunkFound = 0; @@ -248,8 +247,9 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { /* check extension */ + /* .lwav: to avoid hijacking .wav, .xwav: fake for Xbox games (unneded anymore) */ /* .da: The Great Battle VI (PS), .cd: Exector (PS), .med: Psi Ops (PC), .snd: Layton Brothers (iOS/Android) */ - if ( check_extensions(streamFile, "wav,lwav,da,cd,med,snd") ) { + if ( check_extensions(streamFile, "wav,lwav,xwav,da,cd,med,snd") ) { ; } else if ( check_extensions(streamFile, "mwv") ) { @@ -282,6 +282,10 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { if (riff_size+0x08+0x01 == file_size) riff_size += 0x01; + /* some Xbox games do this [Dynasty Warriors 3 (Xbox), BloodRayne (Xbox)] */ + if (riff_size == file_size && read_16bitLE(0x14,streamFile)==0x0069) + riff_size -= 0x08; + /* check for truncated RIFF */ if (file_size < riff_size+0x08) goto fail; @@ -296,12 +300,11 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { if (fmt.codec == 0x6771 && chunk_type == 0x64617461) /* Liar-soft again */ chunk_size += (chunk_size%2) ? 0x01 : 0x00; - if (current_chunk+8+chunk_size > file_size) goto fail; + if (current_chunk+0x08+chunk_size > file_size) goto fail; switch(chunk_type) { case 0x666d7420: /* "fmt " */ - /* only one per file */ - if (FormatChunkFound) goto fail; + if (FormatChunkFound) goto fail; /* only one per file */ FormatChunkFound = 1; if (-1 == read_fmt(0, /* big endian == false*/ @@ -311,20 +314,20 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { sns, mwv)) goto fail; - break; - case 0x64617461: /* data */ - /* at most one per file */ - if (DataChunkFound) goto fail; + + case 0x64617461: /* "data" */ + if (DataChunkFound) goto fail; /* only one per file */ DataChunkFound = 1; - start_offset = current_chunk + 8; + start_offset = current_chunk + 0x08; data_size = chunk_size; break; - case 0x4C495354: /* LIST */ + + case 0x4C495354: /* "LIST" */ /* what lurks within?? */ - switch (read_32bitBE(current_chunk + 8, streamFile)) { - case 0x6164746C: /* adtl */ + switch (read_32bitBE(current_chunk+0x08, streamFile)) { + case 0x6164746C: /* "adtl" */ /* yay, atdl is its own little world */ parse_adtl(current_chunk + 8, chunk_size, streamFile, @@ -334,28 +337,40 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { break; } break; - case 0x736D706C: /* smpl */ - /* check loop count and loop info */ - if (read_32bitLE(current_chunk+0x24, streamFile)==1) { - if (read_32bitLE(current_chunk+0x2c+4, streamFile)==0) { + + case 0x736D706C: /* "smpl" (RIFFMIDISample + MIDILoop chunk) */ + /* check loop count/loop info (most common) *///todo double check values + /* 0x00: manufacturer id, 0x04: product id, 0x08: sample period, 0x0c: unity node, + * 0x10: pitch fraction, 0x14: SMPTE format, 0x18: SMPTE offset, 0x1c: loop count, 0x20: sampler data */ + if (read_32bitLE(current_chunk+0x08+0x1c, streamFile)==1) { + /* 0x24: cue point id, 0x28: type (0=forward, 1=alternating, 2=backward) + * 0x2c: start, 0x30: end, 0x34: fraction, 0x38: play count */ + if (read_32bitLE(current_chunk+0x08+0x28, streamFile)==0) { loop_flag = 1; - loop_start_offset = read_32bitLE(current_chunk+0x2c+8, streamFile); - loop_end_offset = read_32bitLE(current_chunk+0x2c+0xc,streamFile); + loop_start_offset = read_32bitLE(current_chunk+0x08+0x2c, streamFile); + loop_end_offset = read_32bitLE(current_chunk+0x08+0x30, streamFile); } } break; - case 0x70666c74: /* pflt */ - if (!mwv) break; /* ignore if not in an mwv */ - mwv_pflt_offset = current_chunk; /* predictor filters */ + case 0x77736D70: /* "wsmp" (RIFFDLSSample + DLSLoop chunk) */ + /* check loop count/info (found in some Xbox games: Halo (non-looping), Dynasty Warriors 3, Crimson Sea) */ + /* 0x00: size, 0x04: unity note, 0x06: fine tune, 0x08: gain, 0x10: loop count */ + if (chunk_size >= 0x24 + && read_32bitLE(current_chunk+0x08+0x00, streamFile) == 0x14 + && read_32bitLE(current_chunk+0x08+0x10, streamFile) > 0 + && read_32bitLE(current_chunk+0x08+0x14, streamFile) == 0x10) { + /* 0x14: size, 0x18: loop type (0=forward, 1=release), 0x1c: loop start, 0x20: loop length */ + if (read_32bitLE(current_chunk+0x08+0x18, streamFile)==0) { + loop_flag = 1; + loop_start_sample = read_32bitLE(current_chunk+0x08+0x1c, streamFile); + loop_end_sample = read_32bitLE(current_chunk+0x08+0x20, streamFile); + loop_end_sample += loop_start_sample; + } + } break; - case 0x6374726c: /* ctrl */ - if (!mwv) break; - loop_flag = read_32bitLE(current_chunk+0x08, streamFile); - mwv_ctrl_offset = current_chunk; - break; - case 0x66616374: /* fact */ + case 0x66616374: /* "fact" */ if (chunk_size == 0x04) { /* standard, usually found with ADPCM */ fact_sample_count = read_32bitLE(current_chunk+0x08, streamFile); } else if (sns && chunk_size == 0x10) { @@ -367,11 +382,22 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { fact_sample_count = read_32bitLE(current_chunk+0x08, streamFile); fact_sample_skip = read_32bitLE(current_chunk+0x10, streamFile); } - break; - case 0x4A554E4B: /* JUNK */ + + case 0x70666c74: /* "pflt" (.mwv extension) */ + if (!mwv) break; /* ignore if not in an mwv */ + mwv_pflt_offset = current_chunk; /* predictor filters */ + break; + case 0x6374726c: /* "ctrl" (.mwv extension) */ + if (!mwv) break; + loop_flag = read_32bitLE(current_chunk+0x08, streamFile); + mwv_ctrl_offset = current_chunk; + break; + + case 0x4A554E4B: /* "JUNK" */ JunkFound = 1; break; + default: /* ignorance is bliss */ break; @@ -383,7 +409,7 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { if (!FormatChunkFound || !DataChunkFound) goto fail; - //todo improve detection using fmt sizes/values as Wwise's don't match the RIFF standard + //todo improve detection using fmt sizes/values as Wwise's don't match the RIFF standard /* JUNK is an optional Wwise chunk, and Wwise hijacks the MSADPCM/MS_IMA/XBOX IMA ids (how nice). * To ensure their stuff is parsed in wwise.c we reject their JUNK, which they put almost always. * As JUNK is legal (if unusual) we only reject those codecs. @@ -445,18 +471,21 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { break; case coding_MSADPCM: - vgmstream->num_samples = fact_sample_count ? fact_sample_count : - msadpcm_bytes_to_samples(data_size, fmt.block_size, fmt.channel_count); + vgmstream->num_samples = msadpcm_bytes_to_samples(data_size, fmt.block_size, fmt.channel_count); + if (fact_sample_count && fact_sample_count < vgmstream->num_samples) + vgmstream->num_samples = fact_sample_count; break; case coding_MS_IMA: - vgmstream->num_samples = fact_sample_count ? fact_sample_count : - ms_ima_bytes_to_samples(data_size, fmt.block_size, fmt.channel_count); + vgmstream->num_samples = ms_ima_bytes_to_samples(data_size, fmt.block_size, fmt.channel_count); + if (fact_sample_count && fact_sample_count < vgmstream->num_samples) + vgmstream->num_samples = fact_sample_count; break; case coding_XBOX_IMA: - vgmstream->num_samples = fact_sample_count ? fact_sample_count : - xbox_ima_bytes_to_samples(data_size, fmt.channel_count); + vgmstream->num_samples = xbox_ima_bytes_to_samples(data_size, fmt.channel_count); + if (fact_sample_count && fact_sample_count < vgmstream->num_samples) + vgmstream->num_samples = fact_sample_count; /* some (converted?) Xbox games have bigger fact_samples */ break; case coding_NGC_DSP: @@ -579,6 +608,11 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { vgmstream->loop_end_sample = loop_end_offset; vgmstream->meta_type = meta_RIFF_WAVE_smpl; } + else if (loop_start_sample >= 0) { + vgmstream->loop_start_sample = loop_start_sample; + vgmstream->loop_end_sample = loop_end_sample; + vgmstream->meta_type = meta_RIFF_WAVE_wsmp; + } else if (mwv && mwv_ctrl_offset != -1) { vgmstream->loop_start_sample = read_32bitLE(mwv_ctrl_offset+12, streamFile); vgmstream->loop_end_sample = vgmstream->num_samples; diff --git a/src/meta/rws.c b/src/meta/rws.c index 0f4796f1..c4089875 100644 --- a/src/meta/rws.c +++ b/src/meta/rws.c @@ -155,7 +155,7 @@ VGMSTREAM * init_vgmstream_rws(STREAMFILE *streamFile) { if (name_offset) read_string(vgmstream->stream_name,STREAM_NAME_SIZE, name_offset,streamFile); - vgmstream->layout_type = layout_rws_blocked; + vgmstream->layout_type = layout_blocked_rws; vgmstream->current_block_size = block_size / vgmstream->channels; vgmstream->full_block_size = block_size_total; @@ -207,7 +207,7 @@ VGMSTREAM * init_vgmstream_rws(STREAMFILE *streamFile) { if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) goto fail; - rws_block_update(start_offset, vgmstream); /* block init */ + block_update_rws(start_offset, vgmstream); /* block init */ return vgmstream; diff --git a/src/meta/wwise.c b/src/meta/wwise.c index 8cd588f8..61c72e02 100644 --- a/src/meta/wwise.c +++ b/src/meta/wwise.c @@ -123,9 +123,9 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) { //todo fix repeat looping } } - else if (find_chunk(streamFile, 0x4C495354,first_offset,0, &loop_offset,&loop_size, ww.big_endian, 0)) { /*"LIST", common */ - //todo parse "adtl" (does it ever contain loop info in Wwise?) - } + //else if (find_chunk(streamFile, 0x4C495354,first_offset,0, &loop_offset,&loop_size, ww.big_endian, 0)) { /*"LIST", common */ + // /* usually contains "cue"s with sample positions for events (ex. Platinum Games) but no real looping info */ + //} /* other Wwise specific: */ //"JUNK": optional padding for usually aligment (0-size JUNK exists too) @@ -194,15 +194,14 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) { break; case IMA: /* common */ - /* slightly modified XBOX-IMA with interleaved sub-blocks and LE/BE header */ - - /* Wwise uses common codecs (ex. 0x0002 MSADPCM) so this parser should go AFTER riff.c avoid misdetection */ + /* slightly modified XBOX-IMA */ + /* Wwise reuses common codec ids (ex. 0x0002 MSADPCM) for IMA so this parser should go AFTER riff.c avoid misdetection */ if (ww.bits_per_sample != 4) goto fail; if (ww.block_align != 0x24 * ww.channels) goto fail; vgmstream->coding_type = coding_WWISE_IMA; - vgmstream->layout_type = layout_none; - vgmstream->interleave_block_size = ww.block_align; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = ww.block_align / ww.channels; vgmstream->codec_endian = ww.big_endian; if (ww.truncated) /* enough to get real samples */ diff --git a/src/meta/xbox_xwav.c b/src/meta/xbox_xwav.c deleted file mode 100644 index 735a1120..00000000 --- a/src/meta/xbox_xwav.c +++ /dev/null @@ -1,91 +0,0 @@ -#include "meta.h" -#include "../coding/coding.h" - -/* XWAV - renamed WAV with XBOX-IMA - * (could be parsed as RIFF/.lwav but has a custom loop chunk and multichannel) */ -VGMSTREAM * init_vgmstream_xbox_xwav(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - int loop_flag, channel_count; - off_t start_offset; - - /* check extension */ - if (!check_extensions(streamFile,"xwav")) - goto fail; - - /* check for headers */ - if(!((read_32bitBE(0x00,streamFile) == 0x52494646) && /* "RIFF" */ - (read_32bitBE(0x08,streamFile) == 0x57415645) && /* "WAVE" */ - (read_32bitBE(0x0C,streamFile) == 0x666D7420) && /* "fmt " */ - (read_16bitLE(0x14,streamFile) == 0x0069))) /* codec */ - goto fail; - - /* loop chunk found on Koei/Omega Force games [Crimson Sea, Dynasty Warriors 5] */ - loop_flag = (read_32bitBE(0x28,streamFile) == 0x77736D70); /* "wsmp" */ - channel_count = read_16bitLE(0x16,streamFile); - - /* search for "data" */ - start_offset = 0x1C; - do { - if (read_32bitBE(start_offset,streamFile)==0x64617461) - break; - start_offset += 0x04; - } while (start_offset < (off_t)get_streamfile_size(streamFile)); - - if (start_offset >= (off_t)get_streamfile_size(streamFile)) - goto fail; - start_offset += 0x04; - - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); - if (!vgmstream) goto fail; - - vgmstream->num_samples = xbox_ima_bytes_to_samples(read_32bitLE(start_offset,streamFile), vgmstream->channels); - vgmstream->sample_rate = read_32bitLE(0x18,streamFile); - if (loop_flag) { - vgmstream->loop_start_sample = read_32bitLE(0x4C,streamFile); - vgmstream->loop_end_sample = vgmstream->loop_start_sample + read_32bitLE(0x50,streamFile); - } - - vgmstream->coding_type = coding_XBOX_IMA; - vgmstream->layout_type = layout_none; - vgmstream->meta_type = meta_XBOX_RIFF; - - //if (!vgmstream_open_stream(vgmstream, streamFile, start_offset)) - // goto fail; - - //custom init - { - int i, ch; - char filename[PATH_LIMIT]; - streamFile->get_name(streamFile,filename,sizeof(filename)); - - if (channel_count > 2) { /* multichannel interleaved init */ - for (i=0, ch=0;ich[i].streamfile = streamFile->open(streamFile,filename,0x24); - vgmstream->ch[i].offset = start_offset + 0x04; - - if (!vgmstream->ch[i].streamfile) goto fail; - } - } - else { - for (i=0; i < channel_count; i++) { - vgmstream->ch[i].streamfile = streamFile->open(streamFile,filename,0x24); - vgmstream->ch[i].offset = start_offset + 0x04; - - if (!vgmstream->ch[i].streamfile) goto fail; - } - } - } - - return vgmstream; - -fail: - close_vgmstream(vgmstream); - return NULL; -} diff --git a/src/streamfile.c b/src/streamfile.c index f62b7842..9c3c75c0 100644 --- a/src/streamfile.c +++ b/src/streamfile.c @@ -993,6 +993,27 @@ int get_streamfile_name(STREAMFILE *streamFile, char * buffer, size_t size) { streamFile->get_name(streamFile,buffer,size); return 1; } +int get_streamfile_filename(STREAMFILE *streamFile, char * buffer, size_t size) { + char foldername[PATH_LIMIT]; + const char *path; + + streamFile->get_name(streamFile,foldername,sizeof(foldername)); + + //todo Windows CMD accepts both \\ and /, better way to handle this? + path = strrchr(foldername,'\\'); + if (!path) + path = strrchr(foldername,'/'); + if (path != NULL) + path = path+1; + + //todo validate sizes and copy sensible max + if (path) { + strcpy(buffer, path); + } else { + strcpy(buffer, foldername); + } + return 1; +} int get_streamfile_path(STREAMFILE *streamFile, char * buffer, size_t size) { const char *path; diff --git a/src/streamfile.h b/src/streamfile.h index 49818e9c..6841b939 100644 --- a/src/streamfile.h +++ b/src/streamfile.h @@ -179,6 +179,7 @@ int find_chunk_le(STREAMFILE *streamFile, uint32_t chunk_id, off_t start_offset, int find_chunk(STREAMFILE *streamFile, uint32_t chunk_id, off_t start_offset, int full_chunk_size, off_t *out_chunk_offset, size_t *out_chunk_size, int size_big_endian, int zero_size_end); int get_streamfile_name(STREAMFILE *streamFile, char * buffer, size_t size); +int get_streamfile_filename(STREAMFILE *streamFile, char * buffer, size_t size); int get_streamfile_path(STREAMFILE *streamFile, char * buffer, size_t size); int get_streamfile_ext(STREAMFILE *streamFile, char * filename, size_t size); #endif diff --git a/src/vgmstream.c b/src/vgmstream.c index c9d88360..463f1e09 100644 --- a/src/vgmstream.c +++ b/src/vgmstream.c @@ -55,7 +55,6 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_ps2_ild, init_vgmstream_ps2_pnb, init_vgmstream_xbox_wavm, - init_vgmstream_xbox_xwav, init_vgmstream_ngc_str, init_vgmstream_ea_schl, init_vgmstream_caf, @@ -628,7 +627,9 @@ VGMSTREAM * allocate_vgmstream(int channel_count, int looped) { VGMSTREAMCHANNEL * start_channels; VGMSTREAMCHANNEL * loop_channels; - if (channel_count <= 0) return NULL; + /* up to ~16 aren't too rare for multilayered files, more is probably a bug */ + if (channel_count <= 0 || channel_count > 64) + return NULL; vgmstream = calloc(1,sizeof(VGMSTREAM)); if (!vgmstream) return NULL; @@ -969,13 +970,13 @@ void render_vgmstream(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstre case layout_filp_blocked: case layout_blocked_ivaud: case layout_blocked_ea_swvr: - case layout_ps2_adm_blocked: + case layout_blocked_adm: case layout_dsp_bdsp_blocked: case layout_tra_blocked: case layout_ps2_iab_blocked: case layout_ps2_strlr_blocked: - case layout_rws_blocked: - case layout_hwas_blocked: + case layout_blocked_rws: + case layout_blocked_hwas: case layout_blocked_ea_sns: case layout_blocked_awc: case layout_blocked_vgs: @@ -1072,6 +1073,7 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) { case coding_XBOX_IMA: case coding_XBOX_IMA_int: case coding_FSB_IMA: + case coding_WWISE_IMA: return 64; case coding_APPLE_IMA4: return 64; @@ -1079,7 +1081,6 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) { case coding_REF_IMA: return ((vgmstream->interleave_block_size - 0x04*vgmstream->channels) * 2 / vgmstream->channels) + 1; case coding_RAD_IMA: - case coding_WWISE_IMA: return (vgmstream->interleave_block_size - 0x04*vgmstream->channels) * 2 / vgmstream->channels; case coding_NDS_IMA: case coding_DAT4_IMA: @@ -1146,6 +1147,8 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) { return 128*2; case coding_MC3: return 10; + case coding_FADPCM: + return 256; /* (0x8c - 0xc) * 2 */ case coding_EA_MT: return 432; case coding_CRI_HCA: @@ -1227,7 +1230,6 @@ int get_vgmstream_frame_size(VGMSTREAM * vgmstream) { case coding_RAD_IMA: case coding_NDS_IMA: case coding_DAT4_IMA: - case coding_WWISE_IMA: case coding_REF_IMA: return vgmstream->interleave_block_size; case coding_AWC_IMA: @@ -1240,8 +1242,11 @@ int get_vgmstream_frame_size(VGMSTREAM * vgmstream) { case coding_UBI_IMA: /* variable (PCM then IMA) */ return 0; case coding_XBOX_IMA: + //todo should be 0x48 when stereo, but blocked/interleave layout don't understand stereo codecs + return 0x24; //vgmstream->channels==1 ? 0x24 : 0x48; case coding_XBOX_IMA_int: case coding_FSB_IMA: + case coding_WWISE_IMA: return 0x24; case coding_APPLE_IMA4: return 0x22; @@ -1299,6 +1304,8 @@ int get_vgmstream_frame_size(VGMSTREAM * vgmstream) { return 0x90; case coding_MC3: return 0x04; + case coding_FADPCM: + return 0x8c; case coding_EA_MT: return 0; /* variable (frames of bit counts or PCM frames) */ #ifdef VGM_USE_ATRAC9 @@ -1916,6 +1923,13 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to chan); } break; + case coding_FADPCM: + for (chan=0;chanchannels;chan++) { + decode_fadpcm(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, + vgmstream->channels,vgmstream->samples_into_block, + samples_to_do); + } + break; case coding_EA_MT: for (chan=0;chanchannels;chan++) { decode_ea_mt(vgmstream, buffer+samples_written*vgmstream->channels+chan, @@ -2532,7 +2546,7 @@ int get_vgmstream_average_bitrate(VGMSTREAM * vgmstream) { * Inits vgmstreams' channels doing two things: * - sets the starting offset per channel (depending on the layout) * - opens its own streamfile from on a base one. One streamfile per channel may be open (to improve read/seeks). - * Should be called in metas before returning the VGMSTREAM.. + * Should be called in metas before returning the VGMSTREAM. */ int vgmstream_open_stream(VGMSTREAM * vgmstream, STREAMFILE *streamFile, off_t start_offset) { STREAMFILE * file; diff --git a/src/vgmstream.h b/src/vgmstream.h index 7bdcda26..5121be79 100644 --- a/src/vgmstream.h +++ b/src/vgmstream.h @@ -152,6 +152,7 @@ typedef enum { coding_MTAF, /* Konami MTAF ADPCM */ coding_MTA2, /* Konami MTA2 ADPCM */ coding_MC3, /* Paradigm MC3 3-bit ADPCM */ + coding_FADPCM, /* FMOD FADPCM 4-bit ADCPM */ /* others */ coding_SDX2, /* SDX2 2:1 Squareroot-Delta-Exact compression DPCM */ @@ -238,15 +239,15 @@ typedef enum { layout_thp_blocked, layout_filp_blocked, layout_blocked_ea_swvr, - layout_ps2_adm_blocked, + layout_blocked_adm, layout_dsp_bdsp_blocked, layout_mxch_blocked, layout_blocked_ivaud, /* GTA IV .ivaud blocks */ layout_tra_blocked, /* DefJam Rapstar .tra blocks */ layout_ps2_iab_blocked, layout_ps2_strlr_blocked, - layout_rws_blocked, - layout_hwas_blocked, + layout_blocked_rws, + layout_blocked_hwas, layout_blocked_ea_sns, /* newest Electronic Arts blocks, found in SNS/SNU/SPS/etc formats */ layout_blocked_awc, /* Rockstar AWC */ layout_blocked_vgs, /* Guitar Hero II (PS2) */ @@ -456,7 +457,6 @@ typedef enum { meta_UBI_CKD, /* Ubisoft CKD RIFF header (Rayman Origins Wii) */ meta_XBOX_WAVM, /* XBOX WAVM File */ - meta_XBOX_RIFF, /* XBOX RIFF/WAVE File */ meta_XBOX_WVS, /* XBOX WVS */ meta_NGC_WVS, /* Metal Arms - Glitch in the System */ meta_XBOX_MATX, /* XBOX MATX */ @@ -481,6 +481,7 @@ typedef enum { meta_RIFF_WAVE_POS, /* .wav + .pos for looping (Ys Complete PC) */ meta_RIFF_WAVE_labl, /* RIFF w/ loop Markers in LIST-adtl-labl */ meta_RIFF_WAVE_smpl, /* RIFF w/ loop data in smpl chunk */ + meta_RIFF_WAVE_wsmp, /* RIFF w/ loop data in wsmp chunk */ meta_RIFF_WAVE_MWV, /* .mwv RIFF w/ loop data in ctrl chunk pflt */ meta_RIFF_WAVE_SNS, /* .sns RIFF */ meta_RIFX_WAVE, /* RIFX, for big-endian WAVs */ @@ -550,7 +551,7 @@ typedef enum { meta_PS2_WAD, /* The golden Compass */ meta_DSP_XIII, /* XIII, possibly more (Ubisoft header???) */ meta_DSP_CABELAS, /* Cabelas games */ - meta_PS2_ADM, /* Dragon Quest 5 */ + meta_PS2_ADM, /* Dragon Quest V (PS2) */ meta_PS2_LPCM, /* Ah! My Goddess */ meta_DSP_BDSP, /* Ah! My Goddess */ meta_PS2_VMS, /* Autobahn Raser - Police Madness */