From a4f67bf0770691b5118db4489b88b1015d322c74 Mon Sep 17 00:00:00 2001 From: bnnm Date: Thu, 2 Aug 2018 17:15:09 +0200 Subject: [PATCH 01/13] Add Cricket Audio .cks/ckb [Part Time UFO, Mega Man 1-6 (Android)] Includes Cricket Audio's MSADPCM variation, also cleaned up MSADPCM code --- src/coding/coding.h | 3 +- src/coding/msadpcm_decoder.c | 257 +++++++++++++++++++------------ src/formats.c | 7 +- src/libvgmstream.vcproj | 4 + src/libvgmstream.vcxproj | 1 + src/libvgmstream.vcxproj.filters | 3 + src/meta/ck.c | 177 +++++++++++++++++++++ src/meta/meta.h | 3 + src/vgmstream.c | 30 ++-- src/vgmstream.h | 5 +- 10 files changed, 380 insertions(+), 110 deletions(-) create mode 100644 src/meta/ck.c diff --git a/src/coding/coding.h b/src/coding/coding.h index 99fe5af8..a656e326 100644 --- a/src/coding/coding.h +++ b/src/coding/coding.h @@ -114,7 +114,8 @@ void decode_nwa(NWAData *nwa, sample *outbuf, int32_t samples_to_do); /* msadpcm_decoder */ void decode_msadpcm_stereo(VGMSTREAM * vgmstream, sample * outbuf, int32_t first_sample, int32_t samples_to_do); -void decode_msadpcm_mono(VGMSTREAM * vgmstream, sample * outbuf, int32_t first_sample, int32_t samples_to_do); +void decode_msadpcm_mono(VGMSTREAM * vgmstream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); +void decode_msadpcm_ck(VGMSTREAM * vgmstream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); long msadpcm_bytes_to_samples(long bytes, int block_size, int channels); /* yamaha_decoder */ diff --git a/src/coding/msadpcm_decoder.c b/src/coding/msadpcm_decoder.c index e3b7ef0a..64602776 100644 --- a/src/coding/msadpcm_decoder.c +++ b/src/coding/msadpcm_decoder.c @@ -1,17 +1,15 @@ #include "../util.h" #include "coding.h" -/* used to compute next scale */ -static const int ADPCMTable[16] = -{ + +static const int msadpcm_steps[16] = { 230, 230, 230, 230, 307, 409, 512, 614, 768, 614, 512, 409, 307, 230, 230, 230 }; -static const int ADPCMCoeffs[7][2] = -{ +static const int msadpcm_coefs[7][2] = { { 256, 0 }, { 512, -256 }, { 0, 0 }, @@ -23,138 +21,203 @@ static const int ADPCMCoeffs[7][2] = void decode_msadpcm_stereo(VGMSTREAM * vgmstream, sample * outbuf, int32_t first_sample, int32_t samples_to_do) { VGMSTREAMCHANNEL *ch1,*ch2; - int i; - int framesin; STREAMFILE *streamfile; - off_t offset; - - framesin = first_sample/get_vgmstream_samples_per_frame(vgmstream); - first_sample = first_sample%get_vgmstream_samples_per_frame(vgmstream); + int i, frames_in; + size_t bytes_per_frame, samples_per_frame; + off_t frame_offset; ch1 = &vgmstream->ch[0]; ch2 = &vgmstream->ch[1]; streamfile = ch1->streamfile; - offset = ch1->offset+framesin*get_vgmstream_frame_size(vgmstream); + /* external interleave (variable size), stereo */ + bytes_per_frame = get_vgmstream_frame_size(vgmstream); + samples_per_frame = get_vgmstream_samples_per_frame(vgmstream); + frames_in = first_sample / samples_per_frame; + first_sample = first_sample % samples_per_frame; + + frame_offset = ch1->offset + frames_in*bytes_per_frame; + + /* parse frame header */ + if (first_sample == 0) { + ch1->adpcm_coef[0] = msadpcm_coefs[read_8bit(frame_offset+0x00,streamfile) & 0x07][0]; + ch1->adpcm_coef[1] = msadpcm_coefs[read_8bit(frame_offset+0x00,streamfile) & 0x07][1]; + ch2->adpcm_coef[0] = msadpcm_coefs[read_8bit(frame_offset+0x01,streamfile)][0]; + ch2->adpcm_coef[1] = msadpcm_coefs[read_8bit(frame_offset+0x01,streamfile)][1]; + ch1->adpcm_scale = read_16bitLE(frame_offset+0x02,streamfile); + ch2->adpcm_scale = read_16bitLE(frame_offset+0x04,streamfile); + ch1->adpcm_history1_16 = read_16bitLE(frame_offset+0x06,streamfile); + ch2->adpcm_history1_16 = read_16bitLE(frame_offset+0x08,streamfile); + ch1->adpcm_history2_16 = read_16bitLE(frame_offset+0x0a,streamfile); + ch2->adpcm_history2_16 = read_16bitLE(frame_offset+0x0c,streamfile); + } + + /* write header samples (needed) */ if (first_sample==0) { - ch1->adpcm_coef[0] = ADPCMCoeffs[read_8bit(offset,streamfile)][0]; - ch1->adpcm_coef[1] = ADPCMCoeffs[read_8bit(offset,streamfile)][1]; - ch2->adpcm_coef[0] = ADPCMCoeffs[read_8bit(offset+1,streamfile)][0]; - ch2->adpcm_coef[1] = ADPCMCoeffs[read_8bit(offset+1,streamfile)][1]; - ch1->adpcm_scale = read_16bitLE(offset+2,streamfile); - ch2->adpcm_scale = read_16bitLE(offset+4,streamfile); - ch1->adpcm_history1_16 = read_16bitLE(offset+6,streamfile); - ch2->adpcm_history1_16 = read_16bitLE(offset+8,streamfile); - ch1->adpcm_history2_16 = read_16bitLE(offset+10,streamfile); - ch2->adpcm_history2_16 = read_16bitLE(offset+12,streamfile); - outbuf[0] = ch1->adpcm_history2_16; outbuf[1] = ch2->adpcm_history2_16; - - outbuf+=2; + outbuf += 2; first_sample++; samples_to_do--; } - if (first_sample==1 && samples_to_do > 0) { + if (first_sample == 1 && samples_to_do > 0) { outbuf[0] = ch1->adpcm_history1_16; outbuf[1] = ch2->adpcm_history1_16; - - outbuf+=2; + outbuf += 2; first_sample++; samples_to_do--; } - for (i=first_sample; ich[j]; - int sample_nibble = - (j == 0 ? - get_high_nibble_signed(read_8bit(offset+14+i-2,streamfile)) : - get_low_nibble_signed(read_8bit(offset+14+i-2,streamfile)) - ); - int32_t hist1,hist2; - int32_t predicted; + for (ch = 0; ch < 2; ch++) { + VGMSTREAMCHANNEL *stream = &vgmstream->ch[ch]; + int32_t hist1,hist2, predicted; + int sample_nibble = (ch == 0) ? /* L = high nibble first */ + get_high_nibble_signed(read_8bit(frame_offset+0x07*2+(i-2),streamfile)) : + get_low_nibble_signed (read_8bit(frame_offset+0x07*2+(i-2),streamfile)); - hist1 = ch->adpcm_history1_16; - hist2 = ch->adpcm_history2_16; - predicted = hist1 * ch->adpcm_coef[0] + hist2 * ch->adpcm_coef[1]; - predicted /= 256; - predicted += sample_nibble*ch->adpcm_scale; + hist1 = stream->adpcm_history1_16; + hist2 = stream->adpcm_history2_16; + predicted = hist1*stream->adpcm_coef[0] + hist2*stream->adpcm_coef[1]; + predicted = predicted / 256; + predicted = predicted + sample_nibble*stream->adpcm_scale; outbuf[0] = clamp16(predicted); - ch->adpcm_history2_16 = ch->adpcm_history1_16; - ch->adpcm_history1_16 = outbuf[0]; - ch->adpcm_scale = (ADPCMTable[sample_nibble&0xf] * - ch->adpcm_scale) / 256; - if (ch->adpcm_scale < 0x10) ch->adpcm_scale = 0x10; + + stream->adpcm_history2_16 = stream->adpcm_history1_16; + stream->adpcm_history1_16 = outbuf[0]; + stream->adpcm_scale = (msadpcm_steps[sample_nibble & 0xf] * stream->adpcm_scale) / 256; + if (stream->adpcm_scale < 0x10) + stream->adpcm_scale = 0x10; outbuf++; } } } -void decode_msadpcm_mono(VGMSTREAM * vgmstream, sample * outbuf, int32_t first_sample, int32_t samples_to_do) { - VGMSTREAMCHANNEL *ch1; - int i; - int framesin; - STREAMFILE *streamfile; - off_t offset; +void decode_msadpcm_mono(VGMSTREAM * vgmstream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { + VGMSTREAMCHANNEL *stream = &vgmstream->ch[channel]; + int i, frames_in; + size_t bytes_per_frame, samples_per_frame; + off_t frame_offset; - framesin = first_sample/get_vgmstream_samples_per_frame(vgmstream); - first_sample = first_sample%get_vgmstream_samples_per_frame(vgmstream); + /* external interleave (variable size), mono */ + bytes_per_frame = get_vgmstream_frame_size(vgmstream); + samples_per_frame = get_vgmstream_samples_per_frame(vgmstream); + frames_in = first_sample / samples_per_frame; + first_sample = first_sample % samples_per_frame; - ch1 = &vgmstream->ch[0]; - streamfile = ch1->streamfile; - offset = ch1->offset+framesin*get_vgmstream_frame_size(vgmstream); + frame_offset = stream->offset + frames_in*bytes_per_frame; - if (first_sample==0) { - ch1->adpcm_coef[0] = ADPCMCoeffs[read_8bit(offset,streamfile)][0]; - ch1->adpcm_coef[1] = ADPCMCoeffs[read_8bit(offset,streamfile)][1]; - ch1->adpcm_scale = read_16bitLE(offset+1,streamfile); - ch1->adpcm_history1_16 = read_16bitLE(offset+3,streamfile); - ch1->adpcm_history2_16 = read_16bitLE(offset+5,streamfile); + /* parse frame header */ + if (first_sample == 0) { + stream->adpcm_coef[0] = msadpcm_coefs[read_8bit(frame_offset+0x00,stream->streamfile) & 0x07][0]; + stream->adpcm_coef[1] = msadpcm_coefs[read_8bit(frame_offset+0x00,stream->streamfile) & 0x07][1]; + stream->adpcm_scale = read_16bitLE(frame_offset+0x01,stream->streamfile); + stream->adpcm_history1_16 = read_16bitLE(frame_offset+0x03,stream->streamfile); + stream->adpcm_history2_16 = read_16bitLE(frame_offset+0x05,stream->streamfile); + } - outbuf[0] = ch1->adpcm_history2_16; - - outbuf++; + /* write header samples (needed) */ + if (first_sample == 0) { + outbuf[0] = stream->adpcm_history2_16; + outbuf += channelspacing; first_sample++; samples_to_do--; } - if (first_sample==1 && samples_to_do > 0) { - outbuf[0] = ch1->adpcm_history1_16; - - outbuf++; + if (first_sample == 1 && samples_to_do > 0) { + outbuf[0] = stream->adpcm_history1_16; + outbuf += channelspacing; first_sample++; samples_to_do--; } - for (i=first_sample; ich[0]; - int sample_nibble = - (i & 1 ? - get_low_nibble_signed(read_8bit(offset+7+(i-2)/2,streamfile)) : - get_high_nibble_signed(read_8bit(offset+7+(i-2)/2,streamfile)) - ); - int32_t hist1,hist2; - int32_t predicted; + /* decode nibbles */ + for (i = first_sample; i < first_sample+samples_to_do; i++) { + int32_t hist1,hist2, predicted; + int sample_nibble = (i & 1) ? /* high nibble first */ + get_low_nibble_signed (read_8bit(frame_offset+0x07+(i-2)/2,stream->streamfile)) : + get_high_nibble_signed(read_8bit(frame_offset+0x07+(i-2)/2,stream->streamfile)); - hist1 = ch->adpcm_history1_16; - hist2 = ch->adpcm_history2_16; - predicted = hist1 * ch->adpcm_coef[0] + hist2 * ch->adpcm_coef[1]; - predicted /= 256; - predicted += sample_nibble*ch->adpcm_scale; - outbuf[0] = clamp16(predicted); - ch->adpcm_history2_16 = ch->adpcm_history1_16; - ch->adpcm_history1_16 = outbuf[0]; - ch->adpcm_scale = (ADPCMTable[sample_nibble&0xf] * - ch->adpcm_scale) / 256; - if (ch->adpcm_scale < 0x10) ch->adpcm_scale = 0x10; + hist1 = stream->adpcm_history1_16; + hist2 = stream->adpcm_history2_16; + predicted = hist1*stream->adpcm_coef[0] + hist2*stream->adpcm_coef[1]; + predicted = predicted / 256; + predicted = predicted + sample_nibble*stream->adpcm_scale; + outbuf[0] = clamp16(predicted); - outbuf++; - } + stream->adpcm_history2_16 = stream->adpcm_history1_16; + stream->adpcm_history1_16 = outbuf[0]; + stream->adpcm_scale = (msadpcm_steps[sample_nibble & 0xf] * stream->adpcm_scale) / 256; + if (stream->adpcm_scale < 0x10) + stream->adpcm_scale = 0x10; + + outbuf += channelspacing; + } +} + +/* Cricket Audio's MSADPCM, same thing with reversed hist and nibble order + * (their tools may convert to float/others but internally it's all PCM16, from debugging). */ +void decode_msadpcm_ck(VGMSTREAM * vgmstream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { + VGMSTREAMCHANNEL *stream = &vgmstream->ch[channel]; + int i, frames_in; + size_t bytes_per_frame, samples_per_frame; + off_t frame_offset; + + /* external interleave (variable size), mono */ + bytes_per_frame = get_vgmstream_frame_size(vgmstream); + samples_per_frame = get_vgmstream_samples_per_frame(vgmstream); + frames_in = first_sample / samples_per_frame; + first_sample = first_sample % samples_per_frame; + + frame_offset = stream->offset + frames_in*bytes_per_frame; + + /* parse frame header */ + if (first_sample == 0) { + stream->adpcm_coef[0] = msadpcm_coefs[read_8bit(frame_offset+0x00,stream->streamfile) & 0x07][0]; + stream->adpcm_coef[1] = msadpcm_coefs[read_8bit(frame_offset+0x00,stream->streamfile) & 0x07][1]; + stream->adpcm_scale = read_16bitLE(frame_offset+0x01,stream->streamfile); + stream->adpcm_history2_16 = read_16bitLE(frame_offset+0x03,stream->streamfile); /* hist2 first, unlike normal MSADPCM */ + stream->adpcm_history1_16 = read_16bitLE(frame_offset+0x05,stream->streamfile); + } + + /* write header samples (needed) */ + if (first_sample == 0) { + outbuf[0] = stream->adpcm_history2_16; + outbuf += channelspacing; + first_sample++; + samples_to_do--; + } + if (first_sample == 1 && samples_to_do > 0) { + outbuf[0] = stream->adpcm_history1_16; + outbuf += channelspacing; + first_sample++; + samples_to_do--; + } + + /* decode nibbles */ + for (i = first_sample; i < first_sample+samples_to_do; i++) { + int32_t hist1,hist2, predicted; + int sample_nibble = (i & 1) ? /* low nibble first, unlike normal MSADPCM */ + get_high_nibble_signed (read_8bit(frame_offset+0x07+(i-2)/2,stream->streamfile)) : + get_low_nibble_signed(read_8bit(frame_offset+0x07+(i-2)/2,stream->streamfile)); + + hist1 = stream->adpcm_history1_16; + hist2 = stream->adpcm_history2_16; + predicted = hist1*stream->adpcm_coef[0] + hist2*stream->adpcm_coef[1]; + predicted = predicted >> 8; /* probably no difference vs MSADPCM */ + predicted = predicted + sample_nibble*stream->adpcm_scale; + outbuf[0] = clamp16(predicted); + + stream->adpcm_history2_16 = stream->adpcm_history1_16; + stream->adpcm_history1_16 = outbuf[0]; + stream->adpcm_scale = (msadpcm_steps[sample_nibble & 0xf] * stream->adpcm_scale) >> 8; + if (stream->adpcm_scale < 0x10) + stream->adpcm_scale = 0x10; + + outbuf += channelspacing; } } diff --git a/src/formats.c b/src/formats.c index 75dfc06e..91e7754a 100644 --- a/src/formats.c +++ b/src/formats.c @@ -4,7 +4,7 @@ /* defines the list of accepted extensions. vgmstream doesn't use it internally so it's here * to inform plugins that need it. Common extensions are commented out to avoid stealing them. */ -/* some extensions require external libraries and could be #ifdef, no really needed */ +/* some extensions require external libraries and could be #ifdef, not really needed */ /* some formats marked as "not parsed" mean they'll go through FFmpeg, the header/extension is not parsed */ @@ -98,7 +98,9 @@ static const char* extension_list[] = { "ccc", "cd", "cfn", //fake extension/header id for .caf (to be removed) + "ckb", "ckd", + "cks", "cnk", "cps", "cvs", @@ -543,6 +545,7 @@ static const coding_info coding_info_list[] = { {coding_UBI_IMA, "Ubisoft 4-bit IMA ADPCM"}, {coding_MSADPCM, "Microsoft 4-bit ADPCM"}, + {coding_MSADPCM_ck, "Microsoft 4-bit ADPCM (Cricket Audio)"}, {coding_WS, "Westwood Studios VBR ADPCM"}, {coding_AICA, "Yamaha AICA 4-bit ADPCM"}, {coding_AICA_int, "Yamaha AICA 4-bit ADPCM (mono/interleave)"}, @@ -1045,6 +1048,8 @@ static const meta_info meta_info_list[] = { {meta_OGG_MUS, "Ogg Vorbis (MUS header)"}, {meta_ASF, "Argonaut ASF header"}, {meta_XMD, "Konami XMD header"}, + {meta_CKS, "Cricket Audio CKS header"}, + {meta_CKB, "Cricket Audio CKB header"}, #ifdef VGM_USE_FFMPEG {meta_FFmpeg, "FFmpeg supported file format"}, diff --git a/src/libvgmstream.vcproj b/src/libvgmstream.vcproj index 8c85ee0b..cafa822a 100644 --- a/src/libvgmstream.vcproj +++ b/src/libvgmstream.vcproj @@ -356,6 +356,10 @@ RelativePath=".\meta\capdsp.c" > + + diff --git a/src/libvgmstream.vcxproj b/src/libvgmstream.vcxproj index 43e161b8..e5d37ec4 100644 --- a/src/libvgmstream.vcxproj +++ b/src/libvgmstream.vcxproj @@ -207,6 +207,7 @@ + diff --git a/src/libvgmstream.vcxproj.filters b/src/libvgmstream.vcxproj.filters index 1fa23f82..0995e443 100644 --- a/src/libvgmstream.vcxproj.filters +++ b/src/libvgmstream.vcxproj.filters @@ -214,6 +214,9 @@ meta\Source Files + + meta\Source Files + meta\Source Files diff --git a/src/meta/ck.c b/src/meta/ck.c new file mode 100644 index 00000000..d01f62a4 --- /dev/null +++ b/src/meta/ck.c @@ -0,0 +1,177 @@ +#include "meta.h" + + +/* .cks - Cricket Audio stream [Part Time UFO (Android), Mega Man 1-6 (Android)] */ +VGMSTREAM * init_vgmstream_cks(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + off_t start_offset; + int loop_flag, channel_count, codec, sample_rate; + int32_t num_samples, loop_start, loop_end; + size_t block_size; + + + /* checks */ + if (!check_extensions(streamFile, "cks")) + goto fail; + if (read_32bitBE(0x00,streamFile) != 0x636B6D6B) /* "ckmk" */ + goto fail; + /* 0x04(4): platform bitflags (from LSB: iOS, Android, OS X, Windows, WP8, Linux, tvOS, undefined/ignored) */ + if (read_32bitLE(0x08,streamFile) != 0x00) /* expected file type (0x00: stream, 0x01: bank, 0x02+: unknown) */ + goto fail; + if (read_32bitLE(0x0c,streamFile) != 0x02) /* file version (always 0x02) */ + goto fail; + + codec = read_8bit(0x10,streamFile); + channel_count = read_8bit(0x11,streamFile); + sample_rate = (uint16_t)read_16bitLE(0x12,streamFile); + num_samples = read_32bitLE(0x14,streamFile) * read_16bitLE(0x1a,streamFile); /* number_of_blocks * samples_per_frame */ + block_size = read_16bitLE(0x18,streamFile); + /* 0x1c(2): volume */ + /* 0x1e(2): pan */ + loop_start = read_32bitLE(0x20,streamFile); + loop_end = read_32bitLE(0x24,streamFile); + loop_flag = read_16bitLE(0x28,streamFile) != 0; /* loop count (-1 = forever) */ + /* 0x2a(2): unused? */ + + start_offset = 0x2c; + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count, loop_flag); + if (!vgmstream) goto fail; + + vgmstream->sample_rate = sample_rate; + vgmstream->num_samples = num_samples; + vgmstream->loop_start_sample = loop_start; + vgmstream->loop_end_sample = loop_end; + + vgmstream->meta_type = meta_CKS; + + switch(codec) { + case 0x00: /* pcm16 [from tests] */ + vgmstream->coding_type = coding_PCM16LE; + break; + case 0x01: /* pcm8 [from tests] */ + vgmstream->coding_type = coding_PCM8; + break; + case 0x02: /* adpcm [Part Time UFO (Android), Mega Man 1-6 (Android)] */ + vgmstream->coding_type = coding_MSADPCM_ck; + /* frame_size is always 0x18 */ + break; + default: + goto fail; + } + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = block_size / channel_count; + + if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + goto fail; + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} + + +/* .ckb - Cricket Audio bank [Fire Emblem Heroes (Android), Mega Man 1-6 (Android)] */ +VGMSTREAM * init_vgmstream_ckb(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + off_t start_offset, name_offset = 0; + int loop_flag, channel_count, codec, sample_rate; + int32_t num_samples, loop_start, loop_end; + size_t block_size, stream_size; + int total_subsongs, target_subsong = streamFile->stream_index; + + + /* checks */ + if (!check_extensions(streamFile, "ckb")) + goto fail; + if (read_32bitBE(0x00,streamFile) != 0x636B6D6B) /* "ckmk" */ + goto fail; + /* 0x04(4): platform bitflags (from LSB: iOS, Android, OS X, Windows, WP8, Linux, tvOS, undefined/ignored) */ + if (read_32bitLE(0x08,streamFile) != 0x01) /* expected file type (0x00: stream, 0x01: bank, 0x02+: unknown) */ + goto fail; + if (read_32bitLE(0x0c,streamFile) != 0x02) /* file version (always 0x02) */ + goto fail; + + /* 0x10: bank name (size 0x1c+1) */ + /* 0x30/34: reserved? */ + total_subsongs = read_32bitLE(0x38,streamFile); + if (target_subsong == 0) target_subsong = 1; + if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail; + /* 0x3c: total_subsongs again? (ignored) */ + /* 0x40/44: unknown (ignored) */ + + /* get subsong (stream offset isn't given so must calc manually) */ + { + int i; + off_t header_offset = 0x48; + off_t stream_offset = 0x48 + total_subsongs*0x48; + + for (i = 0; i < total_subsongs; i++) { + name_offset = header_offset+0x00; /* stream name (size 0x1c+1) */ + codec = read_8bit(header_offset+0x20,streamFile); + channel_count = read_8bit(header_offset+0x21,streamFile); + sample_rate = (uint16_t)read_16bitLE(header_offset+0x22,streamFile); + num_samples = read_32bitLE(header_offset+0x24,streamFile) * read_16bitLE(header_offset+0x2a,streamFile); /* number_of_blocks * samples_per_frame */ + block_size = read_16bitLE(header_offset+0x28,streamFile); + /* 0x2c(2): volume */ + /* 0x2e(2): pan */ + loop_start = read_32bitLE(header_offset+0x30,streamFile); + loop_end = read_32bitLE(header_offset+0x34,streamFile); + loop_flag = read_16bitLE(header_offset+0x38,streamFile) != 0; /* loop count (-1 = forever) */ + /* 0x3a(2): unused? */ + stream_size = read_32bitLE(header_offset+0x3c,streamFile); + /* 0x40/44(4): unused? */ + + if (target_subsong == (i+1)) + break; + header_offset += 0x48; + stream_offset += stream_size; + } + + start_offset = stream_offset; + } + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count, loop_flag); + if (!vgmstream) goto fail; + + vgmstream->sample_rate = sample_rate; + vgmstream->num_samples = num_samples; + vgmstream->loop_start_sample = loop_start; + vgmstream->loop_end_sample = loop_end; + + vgmstream->num_streams = total_subsongs; + vgmstream->stream_size = stream_size; + read_string(vgmstream->stream_name,0x1c+1, name_offset,streamFile); + + vgmstream->meta_type = meta_CKB; + + switch(codec) { + case 0x00: /* pcm16 [Mega Man 1-6 (Android)] */ + vgmstream->coding_type = coding_PCM16LE; + break; + case 0x01: /* pcm8 */ + vgmstream->coding_type = coding_PCM8; + break; + case 0x02: /* adpcm [Fire Emblem Heroes (Android)] */ + vgmstream->coding_type = coding_MSADPCM_ck; + /* frame_size is always 0x18 */ + break; + default: + goto fail; + } + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = block_size / channel_count; + + if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + goto fail; + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} diff --git a/src/meta/meta.h b/src/meta/meta.h index 3fc10128..6d4e8773 100644 --- a/src/meta/meta.h +++ b/src/meta/meta.h @@ -767,4 +767,7 @@ VGMSTREAM * init_vgmstream_asf(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_xmd(STREAMFILE *streamFile); +VGMSTREAM * init_vgmstream_cks(STREAMFILE *streamFile); +VGMSTREAM * init_vgmstream_ckb(STREAMFILE *streamFile); + #endif /*_META_H*/ diff --git a/src/vgmstream.c b/src/vgmstream.c index 357466c0..19c782eb 100644 --- a/src/vgmstream.c +++ b/src/vgmstream.c @@ -420,10 +420,12 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_ps2_ads_container, init_vgmstream_asf, init_vgmstream_xmd, + init_vgmstream_cks, + init_vgmstream_ckb, init_vgmstream_txth, /* should go at the end (lower priority) */ #ifdef VGM_USE_FFMPEG - init_vgmstream_ffmpeg, /* should go at the end */ + init_vgmstream_ffmpeg, /* should go at the end (lowest priority) */ #endif }; @@ -1099,7 +1101,9 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) { return 128; case coding_MSADPCM: - return (vgmstream->interleave_block_size-(7-1)*vgmstream->channels)*2/vgmstream->channels; + return (vgmstream->interleave_block_size - 0x07*vgmstream->channels)*2 / vgmstream->channels + 2; + case coding_MSADPCM_ck: + return (vgmstream->interleave_block_size - 0x07)*2 + 2; case coding_WS: /* only works if output sample size is 8 bit, which always is for WS ADPCM */ return vgmstream->ws_output_size; case coding_AICA: @@ -1269,6 +1273,7 @@ int get_vgmstream_frame_size(VGMSTREAM * vgmstream) { return 0x4c*vgmstream->channels; case coding_MSADPCM: + case coding_MSADPCM_ck: return vgmstream->interleave_block_size; case coding_WS: return vgmstream->current_block_size; @@ -1846,17 +1851,21 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to break; case coding_MSADPCM: if (vgmstream->channels == 2) { - decode_msadpcm_stereo(vgmstream, - buffer+samples_written*vgmstream->channels, + decode_msadpcm_stereo(vgmstream,buffer+samples_written*vgmstream->channels, vgmstream->samples_into_block, samples_to_do); } - else if (vgmstream->channels == 1) - { - decode_msadpcm_mono(vgmstream, - buffer+samples_written*vgmstream->channels, - vgmstream->samples_into_block, - samples_to_do); + else if (vgmstream->channels == 1) { + decode_msadpcm_mono(vgmstream,buffer+samples_written*vgmstream->channels, + vgmstream->channels,vgmstream->samples_into_block, + samples_to_do,0); + } + break; + case coding_MSADPCM_ck: + for (chan=0;chanchannels;chan++) { + decode_msadpcm_ck(vgmstream,buffer+samples_written*vgmstream->channels+chan, + vgmstream->channels,vgmstream->samples_into_block, + samples_to_do, chan); } break; case coding_AICA: @@ -2224,6 +2233,7 @@ void describe_vgmstream(VGMSTREAM * vgmstream, char * desc, int length) { if (vgmstream->layout_type == layout_none && vgmstream->interleave_block_size > 0) { switch (vgmstream->coding_type) { case coding_MSADPCM: + case coding_MSADPCM_ck: case coding_MS_IMA: case coding_MC3: case coding_WWISE_IMA: diff --git a/src/vgmstream.h b/src/vgmstream.h index 6f14d15c..1d1b3501 100644 --- a/src/vgmstream.h +++ b/src/vgmstream.h @@ -141,7 +141,8 @@ typedef enum { coding_AWC_IMA, /* Rockstar AWC IMA ADPCM */ coding_UBI_IMA, /* Ubisoft IMA ADPCM */ - coding_MSADPCM, /* Microsoft ADPCM */ + coding_MSADPCM, /* Microsoft ADPCM (stereo/mono) */ + coding_MSADPCM_ck, /* Microsoft ADPCM (Cricket Audio variation) */ coding_WS, /* Westwood Studios VBR ADPCM */ coding_AICA, /* Yamaha AICA ADPCM (stereo) */ coding_AICA_int, /* Yamaha AICA ADPCM (mono/interleave) */ @@ -685,6 +686,8 @@ typedef enum { meta_OGG_MUS, /* Ogg Vorbis with encryption [Redux - Dark Matters (PC)] */ meta_ASF, /* Argonaut ASF [Croc 2 (PC)] */ meta_XMD, /* Konami XMD [Silent Hill 4 (Xbox), Castlevania: Curse of Darkness (Xbox)] */ + meta_CKS, /* Cricket Audio stream [Part Time UFO (Android), Mega Man 1-6 (Android)] */ + meta_CKB, /* Cricket Audio bank [Fire Emblem Heroes (Android), Mega Man 1-6 (Android)] */ #ifdef VGM_USE_FFMPEG meta_FFmpeg, From 5dc8d55c7f5da738a0facb1b65de1c94952b4596 Mon Sep 17 00:00:00 2001 From: bnnm Date: Thu, 2 Aug 2018 17:17:58 +0200 Subject: [PATCH 02/13] Add file_size fail check for +2GB files --- src/streamfile.c | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/src/streamfile.c b/src/streamfile.c index b285164e..bde4973a 100644 --- a/src/streamfile.c +++ b/src/streamfile.c @@ -143,19 +143,14 @@ static STREAMFILE *open_stdio(STDIOSTREAMFILE *streamFile,const char * const fil } static STREAMFILE * open_stdio_streamfile_buffer_by_file(FILE *infile,const char * const filename, size_t buffersize) { - uint8_t * buffer; - STDIOSTREAMFILE * streamfile; + uint8_t * buffer = NULL; + STDIOSTREAMFILE * streamfile = NULL; buffer = calloc(buffersize,1); - if (!buffer) { - return NULL; - } + if (!buffer) goto fail; streamfile = calloc(1,sizeof(STDIOSTREAMFILE)); - if (!streamfile) { - free(buffer); - return NULL; - } + if (!streamfile) goto fail; streamfile->sf.read = (void*)read_stdio; streamfile->sf.get_size = (void*)get_size_stdio; @@ -175,7 +170,19 @@ static STREAMFILE * open_stdio_streamfile_buffer_by_file(FILE *infile,const char fseeko(streamfile->infile,0,SEEK_END); streamfile->filesize = ftello(streamfile->infile); + /* some compilers/flags may use ftell, which only handles up to ~2.14GB + * (possible for banks like FSB, though unlikely). */ + if (streamfile->filesize == 0xFFFFFFFF) { /* -1 on error */ + VGM_LOG("STREAMFILE: ftell error\n"); + goto fail; /* can be ignored but may result in strange/unexpected behaviors */ + } + return &streamfile->sf; + +fail: + free(buffer); + free(streamfile); + return NULL; } static STREAMFILE * open_stdio_streamfile_buffer(const char * const filename, size_t buffersize) { From 6a8641499004319dc08b45c807df1312bfae5aaa Mon Sep 17 00:00:00 2001 From: bnnm Date: Thu, 2 Aug 2018 17:21:09 +0200 Subject: [PATCH 03/13] Fix form1 XA bugs [Glint Glitters (PS1), Dance! Dance! Dance! (PS1)] --- src/layout/blocked_xa.c | 32 ++++++++++++++++++++------------ src/meta/psx_cdxa.c | 17 +++++++++++++++-- 2 files changed, 35 insertions(+), 14 deletions(-) diff --git a/src/layout/blocked_xa.c b/src/layout/blocked_xa.c index d4aa734c..91d2b10f 100644 --- a/src/layout/blocked_xa.c +++ b/src/layout/blocked_xa.c @@ -28,25 +28,33 @@ void block_update_xa(off_t block_offset, VGMSTREAM * vgmstream) { (uint8_t)read_8bit(block_offset + 0x930 + 0x11,streamFile), "XA block: subchannel change at %lx\n", block_offset); - /* submode flag bits (typical audio value = 0x64) - * - 7: end of file - * - 6: real time mode - * - 5: sector form (0=form1, 1=form2) - * - 4: trigger (for application) - * - 3: data sector - * - 2: audio sector - * - 1: video sector - * - 0: end of audio + /* submode flag bits (typical audio value = 0x64 01100100) + * - 7 (0x80 10000000): end of file + * - 6 (0x40 01000000): real time mode + * - 5 (0x20 00100000): sector form (0=form1, 1=form2) + * - 4 (0x10 00010000): trigger (for application) + * - 3 (0x08 00001000): data sector + * - 2 (0x04 00000100): audio sector + * - 1 (0x02 00000010): video sector + * - 0 (0x01 00000001): end of audio */ xa_submode = (uint8_t)read_8bit(block_offset + 0x12,streamFile); /* audio sector must set/not set certain flags, as per spec */ - if ((xa_submode & 0x20) && !(xa_submode & 0x08) && (xa_submode & 0x04) && !(xa_submode & 0x02) ) { - block_samples = (28*8 / vgmstream->channels) * 18; /* size 0x900, 18 frames of size 0x80 with 8 subframes of 28 samples */ + if (!(xa_submode & 0x08) && (xa_submode & 0x04) && !(xa_submode & 0x02)) { + if (xa_submode & 0x20) { + /* form2 audio: size 0x900, 18 frames of size 0x80 with 8 subframes of 28 samples */ + block_samples = (28*8 / vgmstream->channels) * 18; + } + else { /* rare, found with empty audio [Glint Glitters (PS1), Dance! Dance! Dance! (PS1)] */ + /* form1 audio: size 0x800, 16 frames of size 0x80 with 8 subframes of 28 samples (rest is garbage/other data) */ + block_samples = (28*8 / vgmstream->channels) * 16; + } } else { + ;VGM_ASSERT_ONCE(block_offset < get_streamfile_size(streamFile), + "XA block: non audio block found at %lx\n", block_offset); block_samples = 0; /* not an audio sector */ - ;VGM_LOG("XA block: non audio block found at %lx\n", block_offset); } vgmstream->current_block_offset = block_offset; diff --git a/src/meta/psx_cdxa.c b/src/meta/psx_cdxa.c index d35d860a..8b21efb7 100644 --- a/src/meta/psx_cdxa.c +++ b/src/meta/psx_cdxa.c @@ -140,8 +140,6 @@ VGMSTREAM * init_vgmstream_cdxa(STREAMFILE *streamFile) { if (!vgmstream) goto fail; vgmstream->sample_rate = sample_rate; - //todo do block_updates to find num_samples? (to skip non-audio blocks) - vgmstream->num_samples = xa_bytes_to_samples(file_size - start_offset, channel_count, is_blocked); vgmstream->meta_type = meta_PSX_XA; vgmstream->coding_type = coding_XA; @@ -151,6 +149,21 @@ VGMSTREAM * init_vgmstream_cdxa(STREAMFILE *streamFile) { if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) ) goto fail; + + if (is_blocked) { + /* calc num_samples as blocks may be empty or smaller than usual depending on flags */ + vgmstream->next_block_offset = start_offset; + do { + block_update_xa(vgmstream->next_block_offset,vgmstream); + vgmstream->num_samples += vgmstream->current_block_samples; + } + while (vgmstream->next_block_offset < get_streamfile_size(streamFile)); + + } + else { + vgmstream->num_samples = xa_bytes_to_samples(file_size - start_offset, channel_count, is_blocked); + } + if (vgmstream->layout_type == layout_blocked_xa) block_update_xa(start_offset,vgmstream); return vgmstream; From 16f2474a506166546d15285cd8d297084fef60ad Mon Sep 17 00:00:00 2001 From: bnnm Date: Thu, 2 Aug 2018 18:22:01 +0200 Subject: [PATCH 04/13] Add .WV6 IMA [The Fairly OddParents: Breakin' Da Rules (PC)] --- src/coding/coding.h | 19 ++++++----- src/coding/ima_decoder.c | 41 ++++++++++++++++++++++++ src/formats.c | 8 +++-- src/libvgmstream.vcproj | 4 +++ src/libvgmstream.vcxproj | 1 + src/libvgmstream.vcxproj.filters | 3 ++ src/meta/meta.h | 2 ++ src/meta/wv6.c | 55 ++++++++++++++++++++++++++++++++ src/vgmstream.c | 12 ++++++- src/vgmstream.h | 7 ++-- 10 files changed, 139 insertions(+), 13 deletions(-) create mode 100644 src/meta/wv6.c diff --git a/src/coding/coding.h b/src/coding/coding.h index a656e326..672c9cc4 100644 --- a/src/coding/coding.h +++ b/src/coding/coding.h @@ -15,22 +15,25 @@ void decode_g721(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, void g72x_init_state(struct g72x_state *state_ptr); /* ima_decoder */ -void decode_nds_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); -void decode_dat4_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); +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); +void decode_3ds_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); +void decode_snds_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); +void decode_otns_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); +void decode_wv6_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); + +void decode_ms_ima(VGMSTREAM * vgmstream,VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do,int channel); +void decode_ref_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do,int channel); + void decode_xbox_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); void decode_xbox_ima_mch(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); void decode_xbox_ima_int(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); -void decode_snds_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); -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); -void decode_3ds_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); +void decode_nds_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); +void decode_dat4_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); void decode_rad_ima(VGMSTREAM * vgmstream,VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do,int channel); void decode_rad_ima_mono(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); void decode_apple_ima4(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); -void decode_ms_ima(VGMSTREAM * vgmstream,VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do,int channel); -void decode_otns_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); void decode_fsb_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); void decode_wwise_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); -void decode_ref_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do,int channel); void decode_awc_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); void decode_ubi_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); size_t ima_bytes_to_samples(size_t bytes, int channels); diff --git a/src/coding/ima_decoder.c b/src/coding/ima_decoder.c index 9fc8902d..1db3f407 100644 --- a/src/coding/ima_decoder.c +++ b/src/coding/ima_decoder.c @@ -169,6 +169,25 @@ static void otns_ima_expand_nibble(VGMSTREAMCHANNEL * stream, off_t byte_offset, if (*step_index > 88) *step_index=88; } +/* Fairly OddParents (PC) .WV6: minor variation, reverse engineered from the .exe */ +static void wv6_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 & 0x7); + delta = ((delta * step) >> 3) + ((delta * step) >> 2); + 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 */ /* ************************************ */ @@ -269,6 +288,28 @@ void decode_otns_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * stream->adpcm_step_index = step_index; } +/* WV6 IMA, DVI IMA with custom nibble expand */ +void decode_wv6_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; + + //external interleave + + //no header + + for (i=first_sample,sample_count=0; ioffset + i/2; + int nibble_shift = (i&1?0:4); //high nibble first + + wv6_ima_expand_nibble(stream, byte_offset,nibble_shift, &hist1, &step_index); + outbuf[sample_count] = (short)(hist1); + } + + stream->adpcm_history1_32 = hist1; + stream->adpcm_step_index = step_index; +} + /* ************************************ */ /* MS-IMA */ /* ************************************ */ diff --git a/src/formats.c b/src/formats.c index 5f750a58..10f46b24 100644 --- a/src/formats.c +++ b/src/formats.c @@ -422,6 +422,7 @@ static const char* extension_list[] = { "wsd", "wsi", "wv2", //txth/reserved [Slave Zero (PC)] + "wv6", "wve", "wvs", @@ -528,6 +529,10 @@ static const coding_info coding_info_list[] = { {coding_DVI_IMA, "Intel DVI 4-bit IMA ADPCM"}, {coding_DVI_IMA_int, "Intel DVI 4-bit IMA ADPCM (mono/interleave)"}, {coding_3DS_IMA, "3DS IMA 4-bit ADPCM"}, + {coding_SNDS_IMA, "Heavy Iron .snds 4-bit IMA ADPCM"}, + {coding_OTNS_IMA, "Omikron: The Nomad Soul 4-bit IMA ADPCM"}, + {coding_WV6_IMA, "Gorilla Systems WV6 4-bit IMA ADPCM"}, + {coding_MS_IMA, "Microsoft 4-bit IMA ADPCM"}, {coding_XBOX_IMA, "XBOX 4-bit IMA ADPCM"}, {coding_XBOX_IMA_mch, "XBOX 4-bit IMA ADPCM (multichannel)"}, @@ -537,8 +542,6 @@ static const coding_info coding_info_list[] = { {coding_RAD_IMA, "Radical 4-bit IMA ADPCM"}, {coding_RAD_IMA_mono, "Radical 4-bit IMA ADPCM (mono/interleave)"}, {coding_APPLE_IMA4, "Apple Quicktime 4-bit IMA ADPCM"}, - {coding_SNDS_IMA, "Heavy Iron .snds 4-bit IMA ADPCM"}, - {coding_OTNS_IMA, "Omikron: The Nomad Soul 4-bit IMA ADPCM"}, {coding_FSB_IMA, "FSB 4-bit IMA ADPCM"}, {coding_WWISE_IMA, "Audiokinetic Wwise 4-bit IMA ADPCM"}, {coding_REF_IMA, "Reflections 4-bit IMA ADPCM"}, @@ -1051,6 +1054,7 @@ static const meta_info meta_info_list[] = { {meta_XMD, "Konami XMD header"}, {meta_CKS, "Cricket Audio CKS header"}, {meta_CKB, "Cricket Audio CKB header"}, + {meta_WV6, "Gorilla Systems WV6 header"}, #ifdef VGM_USE_FFMPEG {meta_FFmpeg, "FFmpeg supported file format"}, diff --git a/src/libvgmstream.vcproj b/src/libvgmstream.vcproj index cafa822a..585b6835 100644 --- a/src/libvgmstream.vcproj +++ b/src/libvgmstream.vcproj @@ -1414,6 +1414,10 @@ RelativePath=".\meta\ws_aud.c" > + + diff --git a/src/libvgmstream.vcxproj b/src/libvgmstream.vcxproj index e5d37ec4..01508a31 100644 --- a/src/libvgmstream.vcxproj +++ b/src/libvgmstream.vcxproj @@ -430,6 +430,7 @@ + diff --git a/src/libvgmstream.vcxproj.filters b/src/libvgmstream.vcxproj.filters index 0995e443..b7e46897 100644 --- a/src/libvgmstream.vcxproj.filters +++ b/src/libvgmstream.vcxproj.filters @@ -865,6 +865,9 @@ meta\Source Files + + meta\Source Files + meta\Source Files diff --git a/src/meta/meta.h b/src/meta/meta.h index cde8065a..34ff328a 100644 --- a/src/meta/meta.h +++ b/src/meta/meta.h @@ -771,4 +771,6 @@ VGMSTREAM * init_vgmstream_xmd(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_cks(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_ckb(STREAMFILE *streamFile); +VGMSTREAM * init_vgmstream_wv6(STREAMFILE *streamFile); + #endif /*_META_H*/ diff --git a/src/meta/wv6.c b/src/meta/wv6.c new file mode 100644 index 00000000..bbbf62ec --- /dev/null +++ b/src/meta/wv6.c @@ -0,0 +1,55 @@ +#include "meta.h" +#include "../coding/coding.h" + +/* WV6 - Gorilla Systems PC games [Spy Kids: Mega Mission Zone (PC), Lilo & Stitch: Hawaiian Adventure (PC)] */ +VGMSTREAM * init_vgmstream_wv6(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + off_t start_offset; + int loop_flag, channel_count; + + + /* checks */ + if (!check_extensions(streamFile, "wv6")) + goto fail; + + if (read_32bitLE(0x00,streamFile) != get_streamfile_size(streamFile)) + goto fail; + if (read_32bitBE(0x2c,streamFile) != 0x57563620 || /* "WV6 " */ + read_32bitBE(0x30,streamFile) != 0x494D415F) /* "IMA_" ("WV6 IMA_ADPCM COMPRESSED 16 BIT AUDIO") */ + goto fail; + + /* 0x54/58/5c/60/6c: unknown (reject to catch possible stereo files, but don't seem to exist) */ + if (read_32bitLE(0x54,streamFile) != 0x01 || + read_32bitLE(0x58,streamFile) != 0x01 || + read_32bitLE(0x5c,streamFile) != 0x10 || + read_32bitLE(0x68,streamFile) != 0x01 || + read_32bitLE(0x6c,streamFile) != 0x88) + goto fail; + /* 0x64: PCM size (samples*channels*2) */ + + channel_count = 1; + loop_flag = 0; + start_offset = 0x8c; + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count, loop_flag); + if (!vgmstream) goto fail; + + vgmstream->sample_rate = read_32bitLE(0x60, streamFile); + vgmstream->num_samples = ima_bytes_to_samples(read_32bitLE(0x88,streamFile), channel_count); + + vgmstream->meta_type = meta_WV6; + vgmstream->coding_type = coding_WV6_IMA; + vgmstream->layout_type = layout_none; + + read_string(vgmstream->stream_name,0x1c+1, 0x04,streamFile); + + if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + goto fail; + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} diff --git a/src/vgmstream.c b/src/vgmstream.c index 0c43bbdb..b2799f7e 100644 --- a/src/vgmstream.c +++ b/src/vgmstream.c @@ -423,6 +423,7 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_xmd, init_vgmstream_cks, init_vgmstream_ckb, + init_vgmstream_wv6, init_vgmstream_txth, /* should go at the end (lower priority) */ #ifdef VGM_USE_FFMPEG @@ -1062,6 +1063,7 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) { case coding_IMA_int: case coding_DVI_IMA_int: case coding_3DS_IMA: + case coding_WV6_IMA: return 2; case coding_XBOX_IMA: case coding_XBOX_IMA_mch: @@ -1225,6 +1227,7 @@ int get_vgmstream_frame_size(VGMSTREAM * vgmstream) { case coding_DVI_IMA: case coding_DVI_IMA_int: case coding_3DS_IMA: + case coding_WV6_IMA: return 0x01; case coding_MS_IMA: case coding_RAD_IMA: @@ -1238,7 +1241,7 @@ int get_vgmstream_frame_size(VGMSTREAM * vgmstream) { return 0x14; case coding_SNDS_IMA: case coding_OTNS_IMA: - return 0; + return 0; //todo: 0x01? case coding_UBI_IMA: /* variable (PCM then IMA) */ return 0; case coding_XBOX_IMA: @@ -1719,6 +1722,13 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to samples_to_do); } break; + case coding_WV6_IMA: + for (chan=0;chanchannels;chan++) { + decode_wv6_ima(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, + vgmstream->channels,vgmstream->samples_into_block, + samples_to_do); + } + break; case coding_APPLE_IMA4: for (chan=0;chanchannels;chan++) { decode_apple_ima4(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, diff --git a/src/vgmstream.h b/src/vgmstream.h index 1d1b3501..f976b0ab 100644 --- a/src/vgmstream.h +++ b/src/vgmstream.h @@ -124,6 +124,10 @@ typedef enum { coding_DVI_IMA, /* DVI IMA ADPCM (stereo or mono, high nibble first) */ coding_DVI_IMA_int, /* DVI IMA ADPCM (mono/interleave, high nibble first) */ coding_3DS_IMA, /* 3DS IMA ADPCM */ + coding_SNDS_IMA, /* Heavy Iron Studios .snds IMA ADPCM */ + coding_OTNS_IMA, /* Omikron The Nomad Soul IMA ADPCM */ + coding_WV6_IMA, /* Gorilla Systems WV6 4-bit IMA ADPCM */ + coding_MS_IMA, /* Microsoft IMA ADPCM */ coding_XBOX_IMA, /* XBOX IMA ADPCM */ coding_XBOX_IMA_mch, /* XBOX IMA ADPCM (multichannel) */ @@ -133,8 +137,6 @@ typedef enum { coding_RAD_IMA, /* Radical IMA ADPCM */ coding_RAD_IMA_mono, /* Radical IMA ADPCM (mono/interleave) */ coding_APPLE_IMA4, /* Apple Quicktime IMA4 */ - coding_SNDS_IMA, /* Heavy Iron Studios .snds IMA ADPCM */ - coding_OTNS_IMA, /* Omikron The Nomad Soul IMA ADPCM */ coding_FSB_IMA, /* FMOD's FSB multichannel IMA ADPCM */ coding_WWISE_IMA, /* Audiokinetic Wwise IMA ADPCM */ coding_REF_IMA, /* Reflections IMA ADPCM */ @@ -688,6 +690,7 @@ typedef enum { meta_XMD, /* Konami XMD [Silent Hill 4 (Xbox), Castlevania: Curse of Darkness (Xbox)] */ meta_CKS, /* Cricket Audio stream [Part Time UFO (Android), Mega Man 1-6 (Android)] */ meta_CKB, /* Cricket Audio bank [Fire Emblem Heroes (Android), Mega Man 1-6 (Android)] */ + meta_WV6, /* Gorilla Systems PC games */ #ifdef VGM_USE_FFMPEG meta_FFmpeg, From e1434bc4328282bf54c5db9893311184b87e28fb Mon Sep 17 00:00:00 2001 From: bnnm Date: Fri, 3 Aug 2018 00:46:36 +0200 Subject: [PATCH 05/13] Clean DTK decoder to clarify similarities with XA --- src/coding/ngc_dtk_decoder.c | 62 ++++++++++++++++++++++-------------- src/meta/ngc_adpdtk.c | 26 ++++++++------- 2 files changed, 53 insertions(+), 35 deletions(-) diff --git a/src/coding/ngc_dtk_decoder.c b/src/coding/ngc_dtk_decoder.c index 21d18902..b07c6834 100644 --- a/src/coding/ngc_dtk_decoder.c +++ b/src/coding/ngc_dtk_decoder.c @@ -1,51 +1,65 @@ #include "coding.h" #include "../util.h" + +/* Nintendo GC Disc TracK streaming ADPCM (similar to CD-XA) */ void decode_ngc_dtk(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { - int i=first_sample; - int32_t sample_count; - - int framesin = first_sample/28; - - uint8_t q = read_8bit(framesin*32+stream->offset+channel,stream->streamfile); + off_t frame_offset; + int i, frames_in, sample_count = 0; + size_t bytes_per_frame, samples_per_frame; + uint8_t coef_index, shift_factor; int32_t hist1 = stream->adpcm_history1_32; int32_t hist2 = stream->adpcm_history2_32; - first_sample = first_sample%28; - for (i=first_sample,sample_count=0; ioffset+4+i,stream->streamfile); + /* external interleave (fixed size), stereo */ + bytes_per_frame = 0x20; + samples_per_frame = 28; + frames_in = first_sample / samples_per_frame; + first_sample = first_sample % samples_per_frame; - int32_t hist=0; + /* parse frame L/R header (repeated at 0x03/04) */ + frame_offset = stream->offset + bytes_per_frame*frames_in; + coef_index = ((uint8_t)read_8bit(frame_offset+channel,stream->streamfile) >> 4) & 0xf; + shift_factor = ((uint8_t)read_8bit(frame_offset+channel,stream->streamfile) >> 0) & 0xf; + /* rare but happens, also repeated headers don't match (ex. Ikaruga (GC) SONG02.adp) */ + VGM_ASSERT_ONCE(coef_index > 4 || shift_factor > 12, "DTK: incorrect coefs/shift at %lx\n", frame_offset); - switch (q>>4) - { + /* decode nibbles */ + for (i = first_sample; i < first_sample+samples_to_do; i++) { + int32_t hist = 0, new_sample; + uint8_t nibbles = (uint8_t)read_8bit(frame_offset+0x04+i,stream->streamfile); + + /* apply XA filters << 6 */ + switch(coef_index) { case 0: - hist = 0; + hist = 0; // (hist1 * 0) - (hist2 * 0); break; case 1: - hist = (hist1 * 0x3c); + hist = (hist1 * 60); // - (hist2 * 0); break; case 2: - hist = (hist1 * 0x73) - (hist2 * 0x34); + hist = (hist1 * 115) - (hist2 * 52); break; case 3: - hist = (hist1 * 0x62) - (hist2 * 0x37); + hist = (hist1 * 98) - (hist2 * 55); break; } - - hist = (hist+0x20)>>6; + hist = (hist + 32) >> 6; if (hist > 0x1fffff) hist = 0x1fffff; if (hist < -0x200000) hist = -0x200000; + new_sample = (channel==0) ? /* L=low nibble first */ + get_low_nibble_signed(nibbles) : + get_high_nibble_signed(nibbles); + new_sample = (new_sample << 12) >> shift_factor; + new_sample = (new_sample << 6) + hist; + hist2 = hist1; + hist1 = new_sample; - hist1 = ((((channel==0? - get_low_nibble_signed(sample_byte): - get_high_nibble_signed(sample_byte) - ) << 12) >> (q & 0xf)) << 6) + hist; - - outbuf[sample_count] = clamp16(hist1 >> 6); + outbuf[sample_count] = clamp16(new_sample >> 6); + sample_count += channelspacing; } stream->adpcm_history1_32 = hist1; diff --git a/src/meta/ngc_adpdtk.c b/src/meta/ngc_adpdtk.c index ab3852c6..c7e4d0b1 100644 --- a/src/meta/ngc_adpdtk.c +++ b/src/meta/ngc_adpdtk.c @@ -2,40 +2,45 @@ #include "meta.h" #include "../util.h" -/* DTK - headerless Nintendo DTK file [Harvest Moon - Another Wonderful Life (GC), XGRA (GC)] */ +/* DTK - headerless Nintendo GC DTK file [Harvest Moon: Another Wonderful Life (GC), XGRA (GC)] */ VGMSTREAM * init_vgmstream_ngc_adpdtk(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; - off_t start_offset = 0; - int channel_count = 2, loop_flag = 0; /* always stereo, no loop */ + off_t start_offset; + int channel_count, loop_flag; + /* checks */ - /* dtk: standard [XGRA (GC)], adp: standard [Harvest Moon AWL (GC)], wav/lwav: Alien Hominid (GC) */ + /* .dtk: standard [XGRA (GC)], .adp: standard [Harvest Moon AWL (GC)], .wav/lwav: Alien Hominid (GC) */ if ( !check_extensions(streamFile,"dtk,adp,wav,lwav")) goto fail; - /* files have no header, and the ext is common, so all we can do is look for valid first frames */ - if (check_extensions(streamFile,"adp,wav,lwav")) { + /* check valid frames as files have no header, and .adp/wav are common */ + { int i; for (i = 0; i < 10; i++) { /* try a bunch of frames */ if (read_8bit(0x00 + i*0x20,streamFile) != read_8bit(0x02 + i*0x20,streamFile) || read_8bit(0x01 + i*0x20,streamFile) != read_8bit(0x03 + i*0x20,streamFile)) goto fail; + /* header 0x00/01 are repeated in 0x02/03 (for error correction?), + * could also test header values (upper nibble should be 0..3, and lower nibble 0..C) */ } } + /* always stereo, no loop (since it's hardware-decoded and streamed) */ + channel_count = 2; + loop_flag = 0; + start_offset = 0x00; /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channel_count, loop_flag); if (!vgmstream) goto fail; - vgmstream->num_samples = get_streamfile_size(streamFile) / 32 * 28; - vgmstream->sample_rate = 48000; + vgmstream->num_samples = get_streamfile_size(streamFile) / 0x20 * 28; + vgmstream->sample_rate = 48000; /* due to a GC hardware defect this may be closer to 48043 */ vgmstream->coding_type = coding_NGC_DTK; vgmstream->layout_type = layout_none; vgmstream->meta_type = meta_NGC_ADPDTK; - - /* open the file for reading */ if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) ) goto fail; @@ -45,4 +50,3 @@ fail: close_vgmstream(vgmstream); return NULL; } - From d4c0362c300d7eb98b4b46a810231b5ca70ac228 Mon Sep 17 00:00:00 2001 From: bnnm Date: Fri, 3 Aug 2018 00:47:54 +0200 Subject: [PATCH 06/13] Minor info (algorithm verified) --- src/coding/xmd_decoder.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/coding/xmd_decoder.c b/src/coding/xmd_decoder.c index ddd4d170..a438cea5 100644 --- a/src/coding/xmd_decoder.c +++ b/src/coding/xmd_decoder.c @@ -1,7 +1,8 @@ #include "coding.h" -/* Decodes Konami XMD from Xbox games (algorithm info from xmd2wav/xmddecode.dll). */ +/* Decodes Konami XMD from Xbox games. + * Algorithm reverse engineered from SH4/CV:CoD's xbe (byte-accurate). */ void decode_xmd(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, size_t frame_size) { off_t frame_offset; int i, frames_in, sample_count = 0, samples_done = 0; @@ -42,10 +43,9 @@ void decode_xmd(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, new_sample = i&1 ? /* low nibble first */ get_high_nibble_signed(nibbles): get_low_nibble_signed(nibbles); - new_sample = (new_sample*(scale<<14) + (hist1*0x7298) - (hist2*0x3350)) >> 14; - /* Coefs are similar to XA's filter 2, but using those creates hissing in some songs. + /* Coefs are based on XA's filter 2 (using those creates hissing in some songs though) * ex. 1.796875 * (1 << 14) = 0x7300, -0.8125 * (1 << 14) = -0x3400 */ - //new_sample = (int32_t)(new_sample*scale + hist1*1.796875 + hist2*-0.8125); + new_sample = (new_sample*(scale<<14) + (hist1*0x7298) - (hist2*0x3350)) >> 14; //new_sample = clamp16(new_sample); /* not needed */ if (sample_count >= first_sample && samples_done < samples_to_do) { From 8f17cf75d635a62b24f3e138ccb46df9e52e44a8 Mon Sep 17 00:00:00 2001 From: bnnm Date: Fri, 3 Aug 2018 02:12:47 +0200 Subject: [PATCH 07/13] Tweak non-interleaved mode for VAG/GCADPCM (from tool tests), cleanup --- src/meta/fsb.c | 86 +++++++++++++++++++++++++------------------------- 1 file changed, 43 insertions(+), 43 deletions(-) diff --git a/src/meta/fsb.c b/src/meta/fsb.c index 30676564..078e7c66 100644 --- a/src/meta/fsb.c +++ b/src/meta/fsb.c @@ -230,7 +230,7 @@ VGMSTREAM * init_vgmstream_fsb(STREAMFILE *streamFile) { data_offset += fsb.stream_size; /* there is no offset so manually count */ /* some subsongs offsets need padding (most FSOUND_IMAADPCM, few MPEG too [Hard Reset (PC) subsong 5]) - * other PADDED4 may set it (ex. XMA) but don't seem to use it and work fine */ + * other codecs may set PADDED4 (ex. XMA) but don't seem to need it and work fine */ if (fsb.flags & FMOD_FSB_SOURCE_MPEG_PADDED4) { if (data_offset % 0x20) data_offset += 0x20 - (data_offset % 0x20); @@ -252,12 +252,12 @@ VGMSTREAM * init_vgmstream_fsb(STREAMFILE *streamFile) { "FSB wrong head/data_size found (expected 0x%x vs 0x%x)\n", fsb.base_header_size + fsb.sample_header_size + fsb.data_size, streamFile->get_size(streamFile)); - /* Loops unless disabled. FMOD default seems full loops (0/num_samples-1) without flags, for repeating tracks + /* Loops unless disabled. FMOD default seems to be full loops (0/num_samples-1) without flags, for repeating tracks * that should loop and jingles/sfx that shouldn't. We'll try to disable looping if it looks jingly enough. */ loop_flag = !(fsb.mode & FSOUND_LOOP_OFF); if(!(fsb.mode & FSOUND_LOOP_NORMAL) /* rarely set */ - && fsb.loop_start+fsb.loop_end+1 == fsb.num_samples /* full loop */ - && fsb.num_samples < 20*fsb.sample_rate) /* seconds, lame but no other way to know */ + && fsb.loop_start+fsb.loop_end+1 == fsb.num_samples /* full loop */ + && fsb.num_samples < 20*fsb.sample_rate) /* in seconds (lame but no other way to know) */ loop_flag = 0; /* ping-pong looping = no looping? (forward > reverse > forward) [ex. Biker Mice from Mars (PS2)] */ @@ -280,7 +280,7 @@ VGMSTREAM * init_vgmstream_fsb(STREAMFILE *streamFile) { /* parse codec */ - if (fsb.mode & FSOUND_MPEG) { /* FSB4: Shatter, Way of the Samurai 3/4 (PS3) */ + if (fsb.mode & FSOUND_MPEG) { /* FSB4: Shatter (PS3), Way of the Samurai 3/4 (PS3) */ #if defined(VGM_USE_MPEG) mpeg_custom_config cfg = {0}; @@ -288,33 +288,38 @@ VGMSTREAM * init_vgmstream_fsb(STREAMFILE *streamFile) { (fsb.flags & FMOD_FSB_SOURCE_MPEG_PADDED4 ? 4 : (fsb.flags & FMOD_FSB_SOURCE_MPEG_PADDED ? 2 : 0))); - //VGM_ASSERT(fsb.mode & FSOUND_MPEG_LAYER2, "FSB FSOUND_MPEG_LAYER2 found\n");/* not always set anyway */ - VGM_ASSERT(fsb.mode & FSOUND_IGNORETAGS, "FSB FSOUND_IGNORETAGS found\n"); - vgmstream->codec_data = init_mpeg_custom(streamFile, start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_FSB, &cfg); if (!vgmstream->codec_data) goto fail; vgmstream->layout_type = layout_none; + + //VGM_ASSERT(fsb.mode & FSOUND_MPEG_LAYER2, "FSB FSOUND_MPEG_LAYER2 found\n");/* not always set anyway */ + VGM_ASSERT(fsb.mode & FSOUND_IGNORETAGS, "FSB FSOUND_IGNORETAGS found\n"); #else goto fail; /* FFmpeg can't properly read FSB4 or FMOD's 0-padded MPEG data @ start_offset */ #endif } - else if (fsb.mode & FSOUND_IMAADPCM) { /* FSB3: Bioshock (PC); FSB4: Blade Kitten (PC) */ - /* FSOUND_IMAADPCMSTEREO is "noninterleaved, true stereo IMA", but doesn't seem to be any different - * (found in FSB4: Shatter, Blade Kitten (PC), Hard Corps: Uprising (PS3)) */ - + else if (fsb.mode & FSOUND_IMAADPCM) { /* FSB3: Bioshock (PC), FSB4: Blade Kitten (PC) */ vgmstream->coding_type = coding_XBOX_IMA; vgmstream->layout_type = layout_none; /* "interleaved header" IMA, only used with >2ch (ex. Blade Kitten 6ch) - * or (seemingly) when flag is used (ex. Dead to Rights 2 (Xbox) 2ch in FSB3.1 */ + * or (seemingly) when flag is used (ex. Dead to Rights 2 (Xbox) 2ch in FSB3.1) */ if (vgmstream->channels > 2 || (fsb.mode & FSOUND_MULTICHANNEL)) vgmstream->coding_type = coding_FSB_IMA; + + /* FSOUND_IMAADPCMSTEREO is "noninterleaved, true stereo IMA", but doesn't seem to be any different + * (found in FSB4: Shatter, Blade Kitten (PC), Hard Corps: Uprising (PS3)) */ } else if (fsb.mode & FSOUND_VAG) { /* FSB1: Jurassic Park Operation Genesis (PS2), FSB4: Spider Man Web of Shadows (PSP) */ vgmstream->coding_type = coding_PSX; vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = 0x10; + if (fsb.flags & FMOD_FSB_SOURCE_NOTINTERLEAVED) { + vgmstream->interleave_block_size = fsb.stream_size / fsb.channels; + } + else { + vgmstream->interleave_block_size = 0x10; + } } - else if (fsb.mode & FSOUND_XMA) { /* FSB4: Armored Core V (X360), Hard Corps (X360) */ + else if (fsb.mode & FSOUND_XMA) { /* FSB3: The Bourne Conspiracy 2008 (X360), FSB4: Armored Core V (X360), Hard Corps (X360) */ #if defined(VGM_USE_FFMPEG) uint8_t buf[0x100]; size_t bytes, block_size, block_count; @@ -323,8 +328,6 @@ VGMSTREAM * init_vgmstream_fsb(STREAMFILE *streamFile) { block_count = fsb.stream_size / block_size; /* not accurate but not needed (custom_data_offset+0x14 -1?) */ bytes = ffmpeg_make_riff_xma2(buf, 0x100, fsb.num_samples, fsb.stream_size, fsb.channels, fsb.sample_rate, block_count, block_size); - if (bytes <= 0) goto fail; - vgmstream->codec_data = init_ffmpeg_header_offset(streamFile, buf,bytes, start_offset,fsb.stream_size); if (!vgmstream->codec_data) goto fail; vgmstream->coding_type = coding_FFmpeg; @@ -333,38 +336,35 @@ VGMSTREAM * init_vgmstream_fsb(STREAMFILE *streamFile) { goto fail; #endif } - else if (fsb.mode & FSOUND_GCADPCM) { - /* FSB3: ?; FSB4: de Blob (Wii), Night at the Museum, M. Night Shyamalan Avatar: The Last Airbender */ - vgmstream->coding_type = coding_NGC_DSP_subint; - vgmstream->layout_type = layout_none; - vgmstream->interleave_block_size = 0x2; + else if (fsb.mode & FSOUND_GCADPCM) { /* FSB3: Metroid Prime 3 (GC), FSB4: de Blob (Wii) */ + if (fsb.flags & FMOD_FSB_SOURCE_NOTINTERLEAVED) { /* [de Blob (Wii) sfx)] */ + vgmstream->coding_type = coding_NGC_DSP; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = fsb.stream_size / fsb.channels; + } + else { + vgmstream->coding_type = coding_NGC_DSP_subint; + vgmstream->layout_type = layout_none; + vgmstream->interleave_block_size = 0x2; + } dsp_read_coefs_be(vgmstream, streamFile, custom_data_offset, 0x2e); } else if (fsb.mode & FSOUND_CELT) { /* FSB4: War Thunder (PC), The Witcher 2 (PC) */ - VGM_LOG("FSB4 FSOUND_CELT found\n"); + VGM_LOG("FSB4: FSOUND_CELT found\n"); goto fail; } - else { /* PCM */ - if (fsb.mode & FSOUND_8BITS) { - vgmstream->coding_type = (fsb.mode & FSOUND_UNSIGNED) ? coding_PCM8_U : coding_PCM8; - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = 0x1; - } - else { /* Rocket Knight (PC), Another Century's Episode R (PS3), Toy Story 3 (Wii) */ - /* sometimes FSOUND_STEREO/FSOUND_MONO is not set (ex. Dead Space iOS), - * or only STEREO/MONO but not FSOUND_8BITS/FSOUND_16BITS is set */ - vgmstream->coding_type = (fsb.flags & FMOD_FSB_SOURCE_BIGENDIANPCM) ? coding_PCM16BE : coding_PCM16LE; - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = 0x2; - } - } - - /* full channel interleave, used in short streams (ex. de Blob Wii SFXs) */ - if (fsb.channels > 1 && (fsb.flags & FMOD_FSB_SOURCE_NOTINTERLEAVED)) { - if (vgmstream->coding_type == coding_NGC_DSP_subint) - vgmstream->coding_type = coding_NGC_DSP; + else if (fsb.mode & FSOUND_8BITS) { /* assumed, no games known */ + vgmstream->coding_type = (fsb.mode & FSOUND_UNSIGNED) ? coding_PCM8_U : coding_PCM8; vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = fsb.stream_size / fsb.channels; + vgmstream->interleave_block_size = 0x1; + } + else { /* (PCM16) FSB4: Rocket Knight (PC), Another Century's Episode R (PS3), Toy Story 3 (Wii) */ + vgmstream->coding_type = (fsb.flags & FMOD_FSB_SOURCE_BIGENDIANPCM) ? coding_PCM16BE : coding_PCM16LE; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x2; + + /* sometimes FSOUND_MONO/FSOUND_STEREO is not set (ex. Dead Space iOS), + * or only STEREO/MONO but not FSOUND_8BITS/FSOUND_16BITS is set */ } From 4703dcaf4f2f526b92933f6019883048fd5376ef Mon Sep 17 00:00:00 2001 From: bnnm Date: Fri, 3 Aug 2018 02:16:19 +0200 Subject: [PATCH 08/13] Add FSB5 PCM16BE/VAG/XWMA and improve rare behaviors (from tool tests) --- src/meta/fsb5.c | 136 +++++++++++++++++++++++++++++++----------------- 1 file changed, 87 insertions(+), 49 deletions(-) diff --git a/src/meta/fsb5.c b/src/meta/fsb5.c index 0a0526ac..17065f2d 100644 --- a/src/meta/fsb5.c +++ b/src/meta/fsb5.c @@ -10,7 +10,7 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) { size_t SampleHeaderLength, NameTableLength, SampleDataLength, BaseHeaderLength, StreamSize = 0, ExtraInfoSize = 0; uint32_t NumSamples = 0, LoopStart = 0, LoopEnd = 0; - int LoopFlag = 0, ChannelCount = 0, Version, SampleRate = 0, CodingID; + int LoopFlag = 0, ChannelCount = 0, Version, SampleRate = 0, Codec, Flags = 0; int TotalSubsongs, TargetSubsong = streamFile->stream_index; int i; @@ -29,17 +29,20 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) { SampleHeaderLength = read_32bitLE(0x0C,streamFile); NameTableLength = read_32bitLE(0x10,streamFile); SampleDataLength = read_32bitLE(0x14,streamFile); - CodingID = read_32bitLE(0x18,streamFile); - /* type 0x01 - 0x1c(8): zero, 0x24(16): hash, 0x34(8): unk - * type 0x00 has an extra field (always 0?) at 0x1c */ - BaseHeaderLength = (Version==0x00) ? 0x40 : 0x3C; + Codec = read_32bitLE(0x18,streamFile); + /* version 0x01 - 0x1c(4): zero, 0x24(16): hash, 0x34(8): unk + * version 0x00 has an extra field (always 0?) at 0x1c */ + if (Version == 0x01) { + Flags = read_32bitLE(0x20,streamFile); /* found by tests and assumed to be flags, no games known */ + } + BaseHeaderLength = (Version==0x00) ? 0x40 : 0x3C; if ((SampleHeaderLength + NameTableLength + SampleDataLength + BaseHeaderLength) != get_streamfile_size(streamFile)) { VGM_LOG("FSB5: bad size (%x + %x + %x + %x != %x)\n", SampleHeaderLength, NameTableLength, SampleDataLength, BaseHeaderLength, get_streamfile_size(streamFile)); goto fail; } - if (TargetSubsong == 0) TargetSubsong = 1; /* default to 1 */ + if (TargetSubsong == 0) TargetSubsong = 1; if (TargetSubsong > TotalSubsongs || TotalSubsongs <= 0) goto fail; SampleHeaderStart = BaseHeaderLength; @@ -49,34 +52,34 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) { for (i = 1; i <= TotalSubsongs; i++) { off_t DataStart = 0; size_t StreamHeaderLength = 0; - uint32_t SampleMode1, SampleMode2; + uint32_t SampleMode1, SampleMode2; /* maybe one uint64? */ - /* seems ok but could use some testing against FMOD's SDK */ SampleMode1 = (uint32_t)read_32bitLE(SampleHeaderStart+0x00,streamFile); SampleMode2 = (uint32_t)read_32bitLE(SampleHeaderStart+0x04,streamFile); StreamHeaderLength += 0x08; /* get samples */ - NumSamples = ((SampleMode2 >> 2) & 0x3FFFFFFF); /* bits 31..2 (30) */ + NumSamples = ((SampleMode2 >> 2) & 0x3FFFFFFF); /* bits2: 31..2 (30) */ /* get offset inside data section */ - DataStart = ((SampleMode1 >> 7) & 0x1FFFFFF) << 5; /* bits 31..8 (25) * 0x20 */ - //SampleMode2 bits 1..0 part of DataStart for files larger than 0x3FFFFFE0? + /* up to 0x07FFFFFF * 0x20 = full 32b offset 0xFFFFFFE0 (recheck, after 0x80000000 some calcs may be off?) */ + DataStart = ((SampleMode2 & 0x03) << 25) | ((SampleMode1 >> 7) & 0x1FFFFFF) << 5; /* bits2: 1..0 (2) | bits1: 31..8 (25) */ - /* get channels (from tests seems correct, but multichannel isn't very common, ex. no 4ch mode?) */ - switch ((SampleMode1 >> 5) & 0x03) { /* bits 7..6 (2) */ + /* get channels */ + switch ((SampleMode1 >> 5) & 0x03) { /* bits1: 7..6 (2) */ case 0: ChannelCount = 1; break; case 1: ChannelCount = 2; break; case 2: ChannelCount = 6; break;/* some Dark Souls 2 MPEG; some IMA ADPCM */ case 3: ChannelCount = 8; break;/* some IMA ADPCM */ - default: /* other values (ex. 10ch) are specified in the extra flags, using 0 here */ + /* other channels (ex. 4/10/12ch) use 0 here + set extra flags */ + default: /* not possible */ goto fail; } /* get sample rate */ - switch ((SampleMode1 >> 1) & 0x0f) { /* bits 5..1 (4) */ - case 0: SampleRate = 4000; break; //??? + switch ((SampleMode1 >> 1) & 0x0f) { /* bits1: 5..1 (4) */ + case 0: SampleRate = 4000; break; case 1: SampleRate = 8000; break; case 2: SampleRate = 11000; break; case 3: SampleRate = 11025; break; @@ -86,14 +89,14 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) { case 7: SampleRate = 32000; break; case 8: SampleRate = 44100; break; case 9: SampleRate = 48000; break; - case 10: SampleRate = 96000; break; //??? - default: /* probably specified in the extra flags */ - SampleRate = 44100; - break; + case 10: SampleRate = 96000; break; + /* other sample rates (ex. 3000/64000/192000) use 0 here + set extra flags */ + default: /* 11-15: rejected (FMOD error) */ + goto fail; } /* get extra flags */ - if (SampleMode1 & 0x01) { /* bit 0 (1) */ + if (SampleMode1 & 0x01) { /* bits1: 0 (1) */ uint32_t ExtraFlag, ExtraFlagStart, ExtraFlagType, ExtraFlagSize, ExtraFlagEnd; ExtraFlagStart = SampleHeaderStart+0x08; @@ -104,13 +107,13 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) { ExtraFlagEnd = (ExtraFlag & 0x01); /* bit 0 (1) */ switch(ExtraFlagType) { - case 0x01: /* Channel Info */ + case 0x01: /* channels */ ChannelCount = read_8bit(ExtraFlagStart+0x04,streamFile); break; - case 0x02: /* Sample Rate Info */ + case 0x02: /* sample rate */ SampleRate = read_32bitLE(ExtraFlagStart+0x04,streamFile); break; - case 0x03: /* Loop Info */ + case 0x03: /* loop info */ LoopStart = read_32bitLE(ExtraFlagStart+0x04,streamFile); if (ExtraFlagSize > 0x04) /* probably not needed */ LoopEnd = read_32bitLE(ExtraFlagStart+0x08,streamFile); @@ -126,14 +129,15 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) { case 0x06: /* XMA seek table */ /* no need for it */ break; - case 0x07: /* DSP coeffs */ + case 0x07: /* DSP coefs */ ExtraInfoStart = ExtraFlagStart + 0x04; break; case 0x09: /* ATRAC9 config */ ExtraInfoStart = ExtraFlagStart + 0x04; ExtraInfoSize = ExtraFlagSize; break; - case 0x0a: /* XWMA data */ + case 0x0a: /* XWMA config */ + ExtraInfoStart = ExtraFlagStart + 0x04; break; case 0x0b: /* Vorbis setup ID and seek table */ ExtraInfoStart = ExtraFlagStart + 0x04; @@ -202,7 +206,7 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) { /* parse codec */ - switch (CodingID) { + switch (Codec) { case 0x00: /* FMOD_SOUND_FORMAT_NONE */ goto fail; @@ -212,48 +216,65 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) { vgmstream->interleave_block_size = 0x01; break; - case 0x02: /* FMOD_SOUND_FORMAT_PCM16 */ - vgmstream->coding_type = coding_PCM16LE; + case 0x02: /* FMOD_SOUND_FORMAT_PCM16 [Shantae Risky's Revenge (PC)] */ + vgmstream->coding_type = (Flags & 0x01) ? coding_PCM16BE : coding_PCM16LE; vgmstream->layout_type = ChannelCount == 1 ? layout_none : layout_interleave; vgmstream->interleave_block_size = 0x02; break; case 0x03: /* FMOD_SOUND_FORMAT_PCM24 */ - goto fail; /* not used */ + VGM_LOG("FSB5: FMOD_SOUND_FORMAT_PCM24 found\n"); + goto fail; case 0x04: /* FMOD_SOUND_FORMAT_PCM32 */ - goto fail; /* not used */ + VGM_LOG("FSB5: FMOD_SOUND_FORMAT_PCM32 found\n"); + goto fail; - case 0x05: /* FMOD_SOUND_FORMAT_PCMFLOAT [Anima - Gate of Memories (PC)] */ + case 0x05: /* FMOD_SOUND_FORMAT_PCMFLOAT [Anima: Gate of Memories (PC)] */ vgmstream->coding_type = coding_PCMFLOAT; vgmstream->layout_type = (ChannelCount == 1) ? layout_none : layout_interleave; vgmstream->interleave_block_size = 0x04; break; - case 0x06: /* FMOD_SOUND_FORMAT_GCADPCM [Sonic Boom - Fire and Ice (3DS)] */ - vgmstream->coding_type = coding_NGC_DSP_subint; - vgmstream->layout_type = layout_none; - vgmstream->interleave_block_size = 0x02; + case 0x06: /* FMOD_SOUND_FORMAT_GCADPCM [Sonic Boom: Fire and Ice (3DS)] */ + if (Flags & 0x02) { /* non-interleaved mode */ + vgmstream->coding_type = coding_NGC_DSP; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = (StreamSize / ChannelCount); + } + else { + vgmstream->coding_type = coding_NGC_DSP_subint; + vgmstream->layout_type = layout_none; + vgmstream->interleave_block_size = 0x02; + } dsp_read_coefs_be(vgmstream,streamFile,ExtraInfoStart,0x2E); break; - case 0x07: /* FMOD_SOUND_FORMAT_IMAADPCM */ + case 0x07: /* FMOD_SOUND_FORMAT_IMAADPCM [Skylanders] */ vgmstream->coding_type = (vgmstream->channels > 2) ? coding_FSB_IMA : coding_XBOX_IMA; vgmstream->layout_type = layout_none; break; - case 0x08: /* FMOD_SOUND_FORMAT_VAG */ - goto fail; /* not used */ + case 0x08: /* FMOD_SOUND_FORMAT_VAG [from fsbankex tests, no known games] */ + vgmstream->coding_type = coding_PSX; + vgmstream->layout_type = layout_interleave; + if (Flags & 0x02) { /* non-interleaved mode */ + vgmstream->interleave_block_size = (StreamSize / ChannelCount); + } + else { + vgmstream->interleave_block_size = 0x10; + } + break; - case 0x09: /* FMOD_SOUND_FORMAT_HEVAG */ + case 0x09: /* FMOD_SOUND_FORMAT_HEVAG [Guacamelee (Vita)] */ vgmstream->coding_type = coding_HEVAG; vgmstream->layout_type = layout_interleave; vgmstream->interleave_block_size = 0x10; break; #ifdef VGM_USE_FFMPEG - case 0x0A: {/* FMOD_SOUND_FORMAT_XMA */ + case 0x0A: {/* FMOD_SOUND_FORMAT_XMA [Dark Souls 2 (X360)] */ uint8_t buf[0x100]; int bytes, block_size, block_count; @@ -261,8 +282,6 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) { block_count = StreamSize / block_size + (StreamSize % block_size ? 1 : 0); bytes = ffmpeg_make_riff_xma2(buf, 0x100, vgmstream->num_samples, StreamSize, vgmstream->channels, vgmstream->sample_rate, block_count, block_size); - if (bytes <= 0) goto fail; - vgmstream->codec_data = init_ffmpeg_header_offset(streamFile, buf,bytes, StartOffset,StreamSize); if ( !vgmstream->codec_data ) goto fail; vgmstream->coding_type = coding_FFmpeg; @@ -272,7 +291,7 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) { #endif #ifdef VGM_USE_MPEG - case 0x0B: {/* FMOD_SOUND_FORMAT_MPEG */ + case 0x0B: {/* FMOD_SOUND_FORMAT_MPEG [Final Fantasy X HD (PS3), Shantae Risky's Revenge (PC)] */ mpeg_custom_config cfg = {0}; cfg.fsb_padding = (vgmstream->channels > 2 ? 16 : 4); /* observed default */ @@ -283,7 +302,8 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) { break; } #endif - case 0x0C: /* FMOD_SOUND_FORMAT_CELT */ + case 0x0C: /* FMOD_SOUND_FORMAT_CELT [BIT.TRIP Presents Runner2 (PC), Full Bore (PC)] */ + VGM_LOG("FSB5: FMOD_SOUND_FORMAT_CELT found\n"); goto fail; #ifdef VGM_USE_ATRAC9 @@ -317,11 +337,28 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) { } #endif - case 0x0E: /* FMOD_SOUND_FORMAT_XWMA */ - goto fail; +#ifdef VGM_USE_FFMPEG + case 0x0E: { /* FMOD_SOUND_FORMAT_XWMA [from fsbankex tests, no known games] */ + uint8_t buf[0x100]; + int bytes, format, average_bps, block_align; + + format = read_16bitBE(ExtraInfoStart+0x00,streamFile); + block_align = (uint16_t)read_16bitBE(ExtraInfoStart+0x02,streamFile); + average_bps = (uint32_t)read_32bitBE(ExtraInfoStart+0x04,streamFile); + /* rest: seek entries + mini seek table? */ + /* XWMA encoder only does up to 6ch (doesn't use FSB multistreams for more) */ + + bytes = ffmpeg_make_riff_xwma(buf,0x100, format, StreamSize, vgmstream->channels, vgmstream->sample_rate, average_bps, block_align); + vgmstream->codec_data = init_ffmpeg_header_offset(streamFile, buf,bytes, StartOffset,StreamSize); + if ( !vgmstream->codec_data ) goto fail; + vgmstream->coding_type = coding_FFmpeg; + vgmstream->layout_type = layout_none; + break; + } +#endif #ifdef VGM_USE_VORBIS - case 0x0F: {/* FMOD_SOUND_FORMAT_VORBIS */ + case 0x0F: {/* FMOD_SOUND_FORMAT_VORBIS [Shantae Half Genie Hero (PC), Pokemon Go (iOS)] */ vorbis_custom_config cfg = {0}; cfg.channels = vgmstream->channels; @@ -337,13 +374,14 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) { } #endif - case 0x10: /* FMOD_SOUND_FORMAT_FADPCM */ + case 0x10: /* FMOD_SOUND_FORMAT_FADPCM [Dead Rising 4 (PC), Sine Mora Ex (Switch)] */ vgmstream->coding_type = coding_FADPCM; vgmstream->layout_type = layout_interleave; vgmstream->interleave_block_size = 0x8c; break; default: + VGM_LOG("FSB5: unknown codec %x found\n", Codec); goto fail; } From ffe7de0acc3decebde46f42de842d087051dea58 Mon Sep 17 00:00:00 2001 From: bnnm Date: Sat, 4 Aug 2018 20:39:33 +0200 Subject: [PATCH 09/13] Improve XA detection to avoid clashing with .str --- src/meta/psx_cdxa.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/meta/psx_cdxa.c b/src/meta/psx_cdxa.c index 8b21efb7..fab4911a 100644 --- a/src/meta/psx_cdxa.c +++ b/src/meta/psx_cdxa.c @@ -63,14 +63,18 @@ VGMSTREAM * init_vgmstream_cdxa(STREAMFILE *streamFile) { if (read_32bitBE(test_offset+0x00,streamFile) != read_32bitBE(test_offset+0x04,streamFile) || read_32bitBE(test_offset+0x08,streamFile) != read_32bitBE(test_offset+0x0c,streamFile)) goto fail; + /* blank frames should always use 0x0c0c0c0c (due to how shift works) */ + if (read_32bitBE(test_offset+0x00,streamFile) == 0 && + read_32bitBE(test_offset+0x04,streamFile) == 0 && + read_32bitBE(test_offset+0x08,streamFile) == 0 && + read_32bitBE(test_offset+0x0c,streamFile) == 0) + goto fail; test_offset += 0x80; } test_offset += (is_blocked ? 0x18 : 0x00); /* footer */ } - /* (the above could get fooled by files with many 0s at the beginning; - * this could be detected as blank XA frames should have have 0c0c0c0c... headers */ } From 6384b91f8f841d8cd84744530bae1f8fc83f60f3 Mon Sep 17 00:00:00 2001 From: bnnm Date: Sat, 4 Aug 2018 20:40:02 +0200 Subject: [PATCH 10/13] Fix FSB4 MPEG weird padding bug [Mafia II (PS3)] --- src/coding/mpeg_custom_utils.c | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/src/coding/mpeg_custom_utils.c b/src/coding/mpeg_custom_utils.c index a7737ddf..3d203fcf 100644 --- a/src/coding/mpeg_custom_utils.c +++ b/src/coding/mpeg_custom_utils.c @@ -127,12 +127,33 @@ int mpeg_custom_parse_frame_default(VGMSTREAMCHANNEL *stream, mpeg_codec_data *d goto fail; current_data_size = info.frame_size; - /* get FSB padding for Layer III or multichannel Layer II (Layer I doesn't seem to be supported) + /* get FSB padding for Layer III or multichannel Layer II (Layer I isn't supported by FMOD). * Padding sometimes contains garbage like the next frame header so we can't feed it to mpg123 or it gets confused. */ if ((info.layer == 3 && data->config.fsb_padding) || data->config.fsb_padding == 16) { current_padding = (current_data_size % data->config.fsb_padding) ? data->config.fsb_padding - (current_data_size % data->config.fsb_padding) : 0; + + /* Rare Mafia II (PS3) bug (GP_0701_music multilang only): some frame paddings "4" are incorrect, + * calcs give 0xD0+0x00 but need 0xD0+0x04 (unlike all other fsbs, which never do that). + * FMOD tools decode fine, so they may be doing special detection too, since even + * re-encoding the same file and using the same FSB flags/modes won't trigger the bug. */ + if (info.layer == 3 && data->config.fsb_padding == 4 && current_data_size == 0xD0) { + uint32_t next_header; + off_t next_offset; + + next_offset = stream->offset + current_data_size + current_padding; + if (current_interleave && ((next_offset - stream->channel_start_offset + current_interleave_pre + current_interleave_post) % current_interleave == 0)) { + next_offset += current_interleave_pre + current_interleave_post; + } + + next_header = read_32bitBE(next_offset, stream->streamfile); + if ((next_header & 0xFFE00000) != 0xFFE00000) { /* doesn't land in a proper frame, fix sizes and hope */ + VGM_LOG_ONCE("MPEG FSB: stream with wrong padding found\n"); + current_padding = 0x04; + } + } + } VGM_ASSERT(data->streams_size > 1 && current_interleave != current_data_size+current_padding, @@ -267,9 +288,9 @@ int mpeg_get_frame_info(STREAMFILE *streamfile, off_t offset, mpeg_frame_info * /* calculate frame length (from hcs's fsb_mpeg) */ switch (info->frame_samples) { - case 384: info->frame_size = (12l * info->bit_rate * 1000l / info->sample_rate + padding) * 4; break; - case 576: info->frame_size = (72l * info->bit_rate * 1000l / info->sample_rate + padding); break; - case 1152: info->frame_size = (144l * info->bit_rate * 1000l / info->sample_rate + padding); break; + case 384: info->frame_size = (12l * info->bit_rate * 1000l / info->sample_rate + padding) * 4; break; /* 384/32 = 12 */ + case 576: info->frame_size = (72l * info->bit_rate * 1000l / info->sample_rate + padding); break; /* 576/8 = 72 */ + case 1152: info->frame_size = (144l * info->bit_rate * 1000l / info->sample_rate + padding); break; /* 1152/8 = 144 */ default: goto fail; } From 0fc5b466c813a5c678d5bd15e3fb2c657b9b344f Mon Sep 17 00:00:00 2001 From: bnnm Date: Sat, 4 Aug 2018 20:42:00 +0200 Subject: [PATCH 11/13] Add some extra streamfile helpers --- src/streamfile.c | 17 +++++++++++++++++ src/streamfile.h | 5 +++++ 2 files changed, 22 insertions(+) diff --git a/src/streamfile.c b/src/streamfile.c index bde4973a..3019b35f 100644 --- a/src/streamfile.c +++ b/src/streamfile.c @@ -772,6 +772,10 @@ fail: /* **************************************************** */ +STREAMFILE * open_streamfile(STREAMFILE *streamFile, const char * pathname) { + return streamFile->open(streamFile,pathname,STREAMFILE_DEFAULT_BUFFER_SIZE); +} + STREAMFILE * open_streamfile_by_ext(STREAMFILE *streamFile, const char * ext) { char filename_ext[PATH_LIMIT]; @@ -1035,6 +1039,7 @@ int find_chunk(STREAMFILE *streamFile, uint32_t chunk_id, off_t start_offset, in return 0; } +/* copies name as-is (may include full path included) */ void get_streamfile_name(STREAMFILE *streamFile, char * buffer, size_t size) { streamFile->get_name(streamFile,buffer,size); } @@ -1060,6 +1065,18 @@ void get_streamfile_filename(STREAMFILE *streamFile, char * buffer, size_t size) strcpy(buffer, foldername); } } +/* copies the filename without path or extension */ +void get_streamfile_basename(STREAMFILE *streamFile, char * buffer, size_t size) { + char *ext; + + get_streamfile_filename(streamFile,buffer,size); + + ext = strrchr(buffer,'.'); + if (ext) { + ext[0] = '\0'; /* remove .ext from buffer */ + } +} +/* copies path removing name (NULL when if filename has no path) */ void get_streamfile_path(STREAMFILE *streamFile, char * buffer, size_t size) { const char *path; diff --git a/src/streamfile.h b/src/streamfile.h index f7a3949f..9a674662 100644 --- a/src/streamfile.h +++ b/src/streamfile.h @@ -98,6 +98,10 @@ STREAMFILE *open_fakename_streamfile(STREAMFILE *streamfile, const char * fakena * The first streamfile is used to get names, stream index and so on. */ STREAMFILE *open_multifile_streamfile(STREAMFILE **streamfiles, size_t streamfiles_size); +/* Opens a STREAMFILE from a (path)+filename. + * Just a wrapper, to avoid having to access the STREAMFILE's callbacks directly. */ +STREAMFILE * open_streamfile(STREAMFILE *streamFile, const char * pathname); + /* Opens a STREAMFILE from a base pathname + new extension * Can be used to get companion headers. */ STREAMFILE * open_streamfile_by_ext(STREAMFILE *streamFile, const char * ext); @@ -197,6 +201,7 @@ int find_chunk(STREAMFILE *streamFile, uint32_t chunk_id, off_t start_offset, in void get_streamfile_name(STREAMFILE *streamFile, char * buffer, size_t size); void get_streamfile_filename(STREAMFILE *streamFile, char * buffer, size_t size); +void get_streamfile_basename(STREAMFILE *streamFile, char * buffer, size_t size); void get_streamfile_path(STREAMFILE *streamFile, char * buffer, size_t size); void get_streamfile_ext(STREAMFILE *streamFile, char * filename, size_t size); #endif From 10dc43f2fb956f0398d5de32da984bf14c081926 Mon Sep 17 00:00:00 2001 From: bnnm Date: Sat, 4 Aug 2018 20:42:25 +0200 Subject: [PATCH 12/13] Use streamfile helpers --- src/meta/txth.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/meta/txth.c b/src/meta/txth.c index 235b9ddd..d1bab2c5 100644 --- a/src/meta/txth.c +++ b/src/meta/txth.c @@ -397,7 +397,7 @@ static STREAMFILE * open_txth(STREAMFILE * streamFile) { /* try "(path/)(name.ext).txth" */ get_streamfile_name(streamFile,filename,PATH_LIMIT); strcat(filename, ".txth"); - streamText = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); + streamText = open_streamfile(streamFile,filename); if (streamText) return streamText; /* try "(path/)(.ext).txth" */ @@ -406,13 +406,13 @@ static STREAMFILE * open_txth(STREAMFILE * streamFile) { strcat(filename,"."); strcat(filename, fileext); strcat(filename, ".txth"); - streamText = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); + streamText = open_streamfile(streamFile,filename); if (streamText) return streamText; /* try "(path/).txth" */ get_streamfile_path(streamFile,filename,PATH_LIMIT); strcat(filename, ".txth"); - streamText = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); + streamText = open_streamfile(streamFile,filename); if (streamText) return streamText; /* not found */ From 790525f990cb669f20c5a1c517d6be5cb3a87482 Mon Sep 17 00:00:00 2001 From: bnnm Date: Sat, 4 Aug 2018 21:02:10 +0200 Subject: [PATCH 13/13] Redo Blitz Games STR+WAV [Zapper (GC), Tak (PS2)] Also removes 3 metas that parsed Blitz stuff --- src/formats.c | 4 +- src/libvgmstream.vcproj | 16 +- src/libvgmstream.vcxproj | 4 +- src/libvgmstream.vcxproj.filters | 12 +- src/meta/dsp_sth_str.c | 320 ---------------------- src/meta/meta.h | 10 +- src/meta/ps2_str.c | 96 ------- src/meta/str_wav.c | 452 +++++++++++++++++++++++++++++++ src/meta/wii_str.c | 90 ------ src/vgmstream.c | 6 +- src/vgmstream.h | 4 +- 11 files changed, 465 insertions(+), 549 deletions(-) delete mode 100644 src/meta/dsp_sth_str.c delete mode 100644 src/meta/ps2_str.c create mode 100644 src/meta/str_wav.c delete mode 100644 src/meta/wii_str.c diff --git a/src/formats.c b/src/formats.c index 10f46b24..27ed56b8 100644 --- a/src/formats.c +++ b/src/formats.c @@ -704,7 +704,7 @@ static const meta_info meta_info_list[] = { {meta_PS2_VAGm, "Sony VAG Mono header (VAGm)"}, {meta_PS2_pGAV, "Sony VAG Stereo Little Endian header (pGAV)"}, {meta_PSX_GMS, "assumed Grandia GMS file by .gms extension"}, - {meta_PS2_STR, "assumed STR + STH File by .str & .sth extension"}, + {meta_STR_WAV, "Blitz Games STR+WAV header"}, {meta_PS2_ILD, "ILD header"}, {meta_PS2_PNB, "assumed PNB (PsychoNauts Bgm File) by .pnb extension"}, {meta_XBOX_WAVM, "Xbox WAVM raw header"}, @@ -851,7 +851,6 @@ static const meta_info meta_info_list[] = { {meta_PS2_SND, "Might and Magic SSND Header"}, {meta_PS2_VSF_TTA, "VSF with SMSS Header"}, {meta_ADS, "dhSS Header"}, - {meta_WII_STR, "HOTD Overkill - STR+STH WII Header"}, {meta_PS2_MCG, "Gunvari MCG Header"}, {meta_ZSD, "ZSD Header"}, {meta_RedSpark, "RedSpark Header"}, @@ -908,7 +907,6 @@ static const meta_info meta_info_list[] = { {meta_PS2_B1S, "B1S header"}, {meta_PS2_WAD, "WAD header"}, {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"}, diff --git a/src/libvgmstream.vcproj b/src/libvgmstream.vcproj index 585b6835..2c2816ca 100644 --- a/src/libvgmstream.vcproj +++ b/src/libvgmstream.vcproj @@ -400,10 +400,6 @@ RelativePath=".\meta\dsp_bdsp.c" > - - @@ -1038,10 +1034,6 @@ RelativePath=".\meta\ps2_ster.c" > - - @@ -1286,6 +1278,10 @@ RelativePath=".\meta\str_snds.c" > + + @@ -1398,10 +1394,6 @@ RelativePath=".\meta\wii_sng.c" > - - diff --git a/src/libvgmstream.vcxproj b/src/libvgmstream.vcxproj index 01508a31..e1c05038 100644 --- a/src/libvgmstream.vcxproj +++ b/src/libvgmstream.vcxproj @@ -218,7 +218,6 @@ - @@ -353,7 +352,6 @@ - @@ -403,6 +401,7 @@ + @@ -427,7 +426,6 @@ - diff --git a/src/libvgmstream.vcxproj.filters b/src/libvgmstream.vcxproj.filters index b7e46897..b624bf6a 100644 --- a/src/libvgmstream.vcxproj.filters +++ b/src/libvgmstream.vcxproj.filters @@ -244,9 +244,6 @@ meta\Source Files - - meta\Source Files - meta\Source Files @@ -634,9 +631,6 @@ meta\Source Files - - meta\Source Files - meta\Source Files @@ -784,6 +778,9 @@ meta\Source Files + + meta\Source Files + meta\Source Files @@ -856,9 +853,6 @@ meta\Source Files - - meta\Source Files - meta\Source Files diff --git a/src/meta/dsp_sth_str.c b/src/meta/dsp_sth_str.c deleted file mode 100644 index 4db018c1..00000000 --- a/src/meta/dsp_sth_str.c +++ /dev/null @@ -1,320 +0,0 @@ -#include "meta.h" -#include "../util.h" - -/* - STH+STR - found in SpongebobSquarepants: Creature From The Krusty Krab (NGC) -*/ - -VGMSTREAM * init_vgmstream_ngc_dsp_sth_str1(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - STREAMFILE * streamFileSTR = NULL; - char filename[PATH_LIMIT]; - char filenameSTR[PATH_LIMIT]; - int i, j; - int channel_count; - int loop_flag; - off_t coef_table[8] = {0x12C,0x18C,0x1EC,0x24C,0x2AC,0x30C,0x36C,0x3CC}; - - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("sth",filename_extension(filename))) goto fail; - - strcpy(filenameSTR,filename); - strcpy(filenameSTR+strlen(filenameSTR)-3,"str"); - streamFileSTR = streamFile->open(streamFile,filenameSTR,STREAMFILE_DEFAULT_BUFFER_SIZE); - if (!streamFileSTR) goto fail; - - if (read_32bitBE(0x0,streamFile) != 0x0) - { - goto fail; - } - - if (read_32bitBE(0x4,streamFile) != 0x800) - { - goto fail; - } - - /* Not really channel_count, just 'included tracks * channels per track */ - loop_flag = (read_32bitBE(0xD8,streamFile) != 0xFFFFFFFF); - channel_count = (read_32bitBE(0x70,streamFile)) * (read_32bitBE(0x88,streamFile)); - - if (channel_count > 8) - { - goto fail; - } - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); - if (!vgmstream) goto fail; - - /* fill in the vital statistics */ - vgmstream->channels = channel_count; - vgmstream->sample_rate = read_32bitBE(0x24,streamFile); - vgmstream->num_samples=get_streamfile_size(streamFileSTR)/8/channel_count*14; - vgmstream->coding_type = coding_NGC_DSP; - - if(loop_flag) - { - vgmstream->loop_start_sample = read_32bitBE(0xD8,streamFile); - vgmstream->loop_end_sample = read_32bitBE(0xDC,streamFile); - } - - if (channel_count == 1) - { - vgmstream->layout_type = layout_none; - } - else - { - vgmstream->layout_type = layout_interleave; - if (channel_count == 2) - { - vgmstream->interleave_block_size=0x10000; - } - else - { - vgmstream->interleave_block_size=0x8000; - } - } - - vgmstream->meta_type = meta_NGC_DSP_STH_STR; - - /* open the file for reading */ - for (i=0;ich[i].streamfile = streamFileSTR->open(streamFileSTR,filenameSTR,0x8000); - if (!vgmstream->ch[i].streamfile) goto fail; - vgmstream->ch[i].channel_start_offset=vgmstream->ch[i].offset=i*vgmstream->interleave_block_size; - } - - // COEFFS - for (j=0;jchannels;j++) - { - for (i=0;i<16;i++) - { - vgmstream->ch[j].adpcm_coef[i] = read_16bitBE(coef_table[j]+i*2,streamFile); - } - } - - close_streamfile(streamFileSTR); streamFileSTR=NULL; - return vgmstream; - - /* clean up anything we may have opened */ -fail: - if (streamFileSTR) close_streamfile(streamFileSTR); - if (vgmstream) close_vgmstream(vgmstream); - return NULL; -} - - -/* - STH+STR - found in Taz Wanted (NGC), Cubix Robots for Everyone: Showdown (NGC) -*/ - -VGMSTREAM * init_vgmstream_ngc_dsp_sth_str2(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - STREAMFILE * streamFileSTR = NULL; - char filename[PATH_LIMIT]; - char filenameSTR[PATH_LIMIT]; - int i, j; - int channel_count; - int loop_flag; - off_t coef_table[8] = {0xDC,0x13C,0x19C,0x1FC,0x25C,0x2BC,0x31C,0x37C}; - - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("sth",filename_extension(filename))) goto fail; - - strcpy(filenameSTR,filename); - strcpy(filenameSTR+strlen(filenameSTR)-3,"str"); - streamFileSTR = streamFile->open(streamFile,filenameSTR,STREAMFILE_DEFAULT_BUFFER_SIZE); - if (!streamFileSTR) goto fail; - - if (read_32bitBE(0x0,streamFile) != 0x0) - { - goto fail; - } - - if (read_32bitBE(0x4,streamFile) != 0x900) - { - goto fail; - } - - /* Not really channel_count, just 'included tracks * channels per track */ - loop_flag = (read_32bitBE(0xB8,streamFile) != 0xFFFFFFFF); - channel_count = read_32bitBE(0x50,streamFile)*2; - - if (channel_count > 8) - { - goto fail; - } - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); - if (!vgmstream) goto fail; - - /* fill in the vital statistics */ - vgmstream->channels = channel_count; - vgmstream->sample_rate = read_32bitBE(0x24,streamFile); - vgmstream->num_samples=get_streamfile_size(streamFileSTR)/8/channel_count*14; - vgmstream->coding_type = coding_NGC_DSP; - - if(loop_flag) - { - vgmstream->loop_start_sample = read_32bitBE(0xB8,streamFile); - vgmstream->loop_end_sample = read_32bitBE(0xBC,streamFile); - } - - if (channel_count == 1) - { - vgmstream->layout_type = layout_none; - } - else - { - vgmstream->layout_type = layout_interleave; - if (channel_count == 2) - { - vgmstream->interleave_block_size=0x10000; - } - else - { - vgmstream->interleave_block_size=0x8000; - } - } - - vgmstream->meta_type = meta_NGC_DSP_STH_STR; - - /* open the file for reading */ - for (i=0;ich[i].streamfile = streamFileSTR->open(streamFileSTR,filenameSTR,0x8000); - if (!vgmstream->ch[i].streamfile) goto fail; - vgmstream->ch[i].channel_start_offset=vgmstream->ch[i].offset=i*vgmstream->interleave_block_size; - } - - // COEFFS - for (j=0;jchannels;j++) - { - for (i=0;i<16;i++) - { - vgmstream->ch[j].adpcm_coef[i] = read_16bitBE(coef_table[j]+i*2,streamFile); - } - } - - close_streamfile(streamFileSTR); streamFileSTR=NULL; - return vgmstream; - - /* clean up anything we may have opened */ -fail: - if (streamFileSTR) close_streamfile(streamFileSTR); - if (vgmstream) close_vgmstream(vgmstream); - return NULL; -} - - -/* - STH+STR - found in Tak and the Guardians of Gross (WII) -*/ - -VGMSTREAM * init_vgmstream_ngc_dsp_sth_str3(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - STREAMFILE * streamFileSTR = NULL; - char filename[PATH_LIMIT]; - char filenameSTR[PATH_LIMIT]; - int i, j; - int channel_count; - int loop_flag; - off_t coef_table[8] = {read_32bitBE(0x7C,streamFile),read_32bitBE(0x80,streamFile),read_32bitBE(0x84,streamFile),read_32bitBE(0x88,streamFile),read_32bitBE(0x8C,streamFile),read_32bitBE(0x90,streamFile),read_32bitBE(0x94,streamFile),read_32bitBE(0x98,streamFile)}; - - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("sth",filename_extension(filename))) goto fail; - - strcpy(filenameSTR,filename); - strcpy(filenameSTR+strlen(filenameSTR)-3,"str"); - streamFileSTR = streamFile->open(streamFile,filenameSTR,STREAMFILE_DEFAULT_BUFFER_SIZE); - if (!streamFileSTR) goto fail; - - if (read_32bitBE(0x0,streamFile) != 0x0) - { - goto fail; - } - - if ((read_32bitBE(0x4,streamFile) != 0x700) && - (read_32bitBE(0x4,streamFile) != 0x800)) - { - goto fail; - } - - /* Not really channel_count, just 'included tracks * channels per track */ - loop_flag = 0; - channel_count = read_32bitBE(0x70,streamFile); - - if (channel_count > 8) - { - goto fail; - } - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); - if (!vgmstream) goto fail; - - /* fill in the vital statistics */ - vgmstream->channels = channel_count; - vgmstream->sample_rate = read_32bitBE(0x38,streamFile); - vgmstream->num_samples=get_streamfile_size(streamFileSTR)/8/channel_count*14; - vgmstream->coding_type = coding_NGC_DSP; - - if(loop_flag) - { - vgmstream->loop_start_sample = 0; - vgmstream->loop_end_sample = 0; - } - - if (channel_count == 1) - { - vgmstream->layout_type = layout_none; - } - else - { - vgmstream->layout_type = layout_interleave; - if (channel_count == 2 || channel_count == 4) - { - vgmstream->interleave_block_size=0x8000; - } - else - { - vgmstream->interleave_block_size=0x4000; - } - } - - vgmstream->meta_type = meta_NGC_DSP_STH_STR; - - /* open the file for reading */ - for (i=0;ich[i].streamfile = streamFileSTR->open(streamFileSTR,filenameSTR,0x8000); - if (!vgmstream->ch[i].streamfile) goto fail; - vgmstream->ch[i].channel_start_offset=vgmstream->ch[i].offset=i*vgmstream->interleave_block_size; - } - - // COEFFS - for (j=0;jchannels;j++) - { - for (i=0;i<16;i++) - { - vgmstream->ch[j].adpcm_coef[i] = read_16bitBE(coef_table[j]+i*2,streamFile); - } - } - - close_streamfile(streamFileSTR); streamFileSTR=NULL; - return vgmstream; - - /* clean up anything we may have opened */ -fail: - if (streamFileSTR) close_streamfile(streamFileSTR); - if (vgmstream) close_vgmstream(vgmstream); - return NULL; -} diff --git a/src/meta/meta.h b/src/meta/meta.h index 34ff328a..4b4d7b0a 100644 --- a/src/meta/meta.h +++ b/src/meta/meta.h @@ -69,8 +69,6 @@ VGMSTREAM * init_vgmstream_ps2_vag(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_psx_gms(STREAMFILE *streamFile); -VGMSTREAM * init_vgmstream_ps2_str(STREAMFILE *streamFile); - VGMSTREAM * init_vgmstream_ps2_ild(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_ps2_pnb(STREAMFILE *streamFile); @@ -385,8 +383,6 @@ VGMSTREAM * init_vgmstream_ps2_vsf_tta(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_ads(STREAMFILE *streamFile); -VGMSTREAM * init_vgmstream_wii_str(STREAMFILE *streamFile); - VGMSTREAM * init_vgmstream_ps2_mcg(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_zsd(STREAMFILE *streamFile); @@ -490,10 +486,6 @@ VGMSTREAM * init_vgmstream_dsp_str_ig(STREAMFILE* streamFile); VGMSTREAM * init_vgmstream_ea_swvr(STREAMFILE* streamFile); -VGMSTREAM * init_vgmstream_ngc_dsp_sth_str1(STREAMFILE* streamFile); -VGMSTREAM * init_vgmstream_ngc_dsp_sth_str2(STREAMFILE* streamFile); -VGMSTREAM * init_vgmstream_ngc_dsp_sth_str3(STREAMFILE* streamFile); - VGMSTREAM * init_vgmstream_ps2_b1s(STREAMFILE* streamFile); VGMSTREAM * init_vgmstream_ps2_wad(STREAMFILE* streamFile); @@ -773,4 +765,6 @@ VGMSTREAM * init_vgmstream_ckb(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_wv6(STREAMFILE *streamFile); +VGMSTREAM * init_vgmstream_str_wav(STREAMFILE *streamFile); + #endif /*_META_H*/ diff --git a/src/meta/ps2_str.c b/src/meta/ps2_str.c deleted file mode 100644 index d02dcba1..00000000 --- a/src/meta/ps2_str.c +++ /dev/null @@ -1,96 +0,0 @@ -#include "meta.h" -#include "../util.h" - -/* STR - - 2008-05-19 - Fastelbja : Test version ... -*/ - -VGMSTREAM * init_vgmstream_ps2_str(STREAMFILE *streamFile) { - - VGMSTREAM * vgmstream = NULL; - STREAMFILE * infileSTH = NULL; - char filename[PATH_LIMIT]; - - char * filenameSTH = NULL; - - int i, channel_count, loop_flag; - - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("str",filename_extension(filename))) goto fail; - - /* check for .MIH file */ - filenameSTH=(char *)malloc(strlen(filename)+1); - - if (!filenameSTH) goto fail; - - strcpy(filenameSTH,filename); - strcpy(filenameSTH+strlen(filenameSTH)-3,"STH"); - - infileSTH = streamFile->open(streamFile,filenameSTH,STREAMFILE_DEFAULT_BUFFER_SIZE); - - /* STH File is necessary, so we can't confuse those file */ - /* with others .STR file as it is a very common extension */ - if (!infileSTH) goto fail; - - if(read_32bitLE(0x2C,infileSTH)==0) - goto fail; - - if((read_32bitLE(0x2C,infileSTH)==0x07) || - (read_32bitLE(0x2C,infileSTH)==0x06)) - channel_count=2; - if(read_32bitLE(0x2C,infileSTH)==0x05) - channel_count=1; - - loop_flag = read_32bitLE(0x2C,infileSTH) & 0x01; - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); - if (!vgmstream) goto fail; - - /* fill in the vital statistics */ - vgmstream->channels = channel_count; - vgmstream->sample_rate = read_32bitLE(0x24,infileSTH); - - vgmstream->interleave_block_size=0x4000; - - if(read_32bitLE(0x40,infileSTH)==0x01) - vgmstream->interleave_block_size = 0x8000; - - vgmstream->num_samples=read_32bitLE(0x20,infileSTH); - - vgmstream->coding_type = coding_PSX; - vgmstream->layout_type = layout_interleave; - - vgmstream->meta_type = meta_PS2_STR; - - if(loop_flag) { - vgmstream->loop_start_sample = 0; - vgmstream->loop_end_sample = read_32bitLE(0x20,infileSTH); - } - - close_streamfile(infileSTH); infileSTH=NULL; - - /* open the file for reading by each channel */ - { - 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+=(off_t)(vgmstream->interleave_block_size*i); - - } - } - - return vgmstream; - - /* clean up anything we may have opened */ -fail: - if (infileSTH) close_streamfile(infileSTH); - if (filenameSTH) {free(filenameSTH); filenameSTH=NULL;} - if (vgmstream) close_vgmstream(vgmstream); - return NULL; -} diff --git a/src/meta/str_wav.c b/src/meta/str_wav.c new file mode 100644 index 00000000..9ac552d5 --- /dev/null +++ b/src/meta/str_wav.c @@ -0,0 +1,452 @@ +#include "meta.h" +#include "../coding/coding.h" + + +typedef enum { PSX, DSP, XBOX, WMA } strwav_codec; +typedef struct { + int32_t channels; + int32_t sample_rate; + int32_t num_samples; + int32_t loop_start; + int32_t loop_end; + int32_t loop_flag; + size_t interleave; + + off_t coefs_offset; + off_t dsps_table; + off_t coefs_table; + + uint32_t flags; + strwav_codec codec; +} strwav_header; + +static int parse_header(STREAMFILE* streamHeader, strwav_header* strwav); + + +/* STR+WAV - Blitz Games streams+header */ +VGMSTREAM * init_vgmstream_str_wav(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + STREAMFILE * streamHeader = NULL; + strwav_header strwav = {0}; + off_t start_offset; + + + /* checks */ + if (!check_extensions(streamFile, "str")) + goto fail; + + /* get external header (extracted with filenames from bigfiles) */ + { + /* try with standard file.wav.str=body + file.wav=header (or file.wma.str + file.wma for Fuzion Frenzy (Xbox)) */ + char basename[PATH_LIMIT]; + get_streamfile_basename(streamFile,basename,PATH_LIMIT); + streamHeader = open_streamfile_by_filename(streamFile, basename); + if (!streamHeader) { + /* try again with file.str=body + file.wav=header [Bad Boys II (PS2), Zapper (PS2)] */ + streamHeader = open_streamfile_by_ext(streamFile, "wav"); + if (!streamHeader) { + /* try again with file.str=body + file.sth=header (renamed/fake extension) */ + streamHeader = open_streamfile_by_ext(streamFile, "sth"); + if (!streamHeader) goto fail; + } + } + else { + if (!check_extensions(streamHeader, "wav,wma")) + goto fail; + } + } + + /* detect version */ + if (!parse_header(streamHeader, &strwav)) + goto fail; + + if (strwav.flags == 0) + goto fail; + if (strwav.channels > 8) + goto fail; + + /* &0x01: loop?, &0x02: non-mono?, &0x04: stream???, &0x08: unused? */ + if (strwav.flags != 0x07 && strwav.flags != 0x06 && strwav.flags != 0x05 && strwav.flags != 0x04 && strwav.flags != 0x02) { + VGM_LOG("STR+WAV: unknown flags\n"); + goto fail; + } + + + start_offset = 0x00; + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(strwav.channels,strwav.loop_flag); + if (!vgmstream) goto fail; + + vgmstream->sample_rate = strwav.sample_rate; + vgmstream->num_samples = strwav.num_samples; + if (strwav.loop_flag) { + vgmstream->loop_start_sample = strwav.loop_start; + vgmstream->loop_end_sample = strwav.loop_end; + } + + vgmstream->meta_type = meta_STR_WAV; + + switch(strwav.codec) { + case PSX: + vgmstream->coding_type = coding_PSX; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = strwav.interleave; + break; + + case DSP: + vgmstream->coding_type = coding_NGC_DSP; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = strwav.interleave; + + /* get coefs */ + { + int i, ch; + for (ch = 0; ch < vgmstream->channels; ch++) { + for (i = 0; i < 16; i++) { + off_t coef_offset; + if (strwav.dsps_table) /* mini table with offsets to DSP headers */ + coef_offset = read_32bitBE(strwav.dsps_table+0x04*ch,streamHeader) + 0x1c; + else if (strwav.coefs_table) /* mini table with offsets to coefs then header */ + coef_offset = read_32bitBE(strwav.coefs_table+0x04*ch,streamHeader); + else + coef_offset = strwav.coefs_offset + 0x60*ch; + vgmstream->ch[ch].adpcm_coef[i] = read_16bitBE(coef_offset+i*0x02,streamHeader); + } + } + } + break; + + case XBOX: + vgmstream->coding_type = coding_XBOX_IMA; + vgmstream->layout_type = layout_none; + if (strwav.channels > 2) goto fail; //todo multistreams are 2ch*N interleaved using ~0xD000 + break; + +#ifdef VGM_USE_FFMPEG + case WMA: { + ffmpeg_codec_data *ffmpeg_data = NULL; + + ffmpeg_data = init_ffmpeg_offset(streamFile, start_offset,get_streamfile_size(streamFile)); + if (!ffmpeg_data) goto fail; + vgmstream->codec_data = ffmpeg_data; + vgmstream->coding_type = coding_FFmpeg; + vgmstream->layout_type = layout_none; + + vgmstream->num_samples = ffmpeg_data->totalSamples; + if (vgmstream->channels != ffmpeg_data->channels) + goto fail; + + break; + } +#endif + + default: + goto fail; + } + + + if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + goto fail; + close_streamfile(streamHeader); + return vgmstream; + +fail: + close_streamfile(streamHeader); + close_vgmstream(vgmstream); + return NULL; +} + + + +/* Parse header versions. Almost every game uses its own variation (struct serialization?), + * so detection could be improved once enough versions are known. */ +static int parse_header(STREAMFILE* streamHeader, strwav_header* strwav) { + size_t header_size; + + if (read_32bitBE(0x00,streamHeader) != 0x00000000) + goto fail; + + header_size = get_streamfile_size(streamHeader); + + //todo loop start/end values may be off for some headers + + /* Fuzion Frenzy (Xbox)[2001] wma */ + if ( read_32bitBE(0x04,streamHeader) == 0x00000900 && + read_32bitLE(0x20,streamHeader) == 0 && /* no num_samples */ + read_32bitLE(0x24,streamHeader) == read_32bitLE(0x80,streamHeader) && /* sample rate repeat */ + read_32bitLE(0x28,streamHeader) == 0x10 && + header_size == 0x110 /* no value in header */ + ) { + strwav->num_samples = read_32bitLE(0x20,streamHeader); /* 0 but will be rectified later */ + strwav->sample_rate = read_32bitLE(0x24,streamHeader); + strwav->flags = read_32bitLE(0x2c,streamHeader); + strwav->loop_start = 0; + strwav->loop_end = 0; + + strwav->channels = read_32bitLE(0x60,streamHeader) * (strwav->flags & 0x02 ? 2 : 1); /* tracks of 2/1ch */ + strwav->loop_flag = strwav->flags & 0x01; + strwav->interleave = 0; + + strwav->codec = WMA; + ;VGM_LOG("STR+WAV: header Fuzion Frenzy (Xbox)\n"); + return 1; + } + + /* Taz: Wanted (GC)[2002] */ + /* Cubix Robots for Everyone: Showdown (GC)[2003] */ + if ( read_32bitBE(0x04,streamHeader) == 0x00000900 && + read_32bitBE(0x24,streamHeader) == read_32bitBE(0x90,streamHeader) && /* sample rate repeat */ + read_32bitBE(0x28,streamHeader) == 0x10 && + read_32bitBE(0xa0,streamHeader) == header_size /* ~0x3C0 */ + ) { + strwav->num_samples = read_32bitBE(0x20,streamHeader); + strwav->sample_rate = read_32bitBE(0x24,streamHeader); + strwav->flags = read_32bitBE(0x2c,streamHeader); + strwav->loop_start = read_32bitBE(0xb8,streamHeader); + strwav->loop_end = read_32bitBE(0xbc,streamHeader); + + strwav->channels = read_32bitBE(0x50,streamHeader) * (strwav->flags & 0x02 ? 2 : 1); /* tracks of 2/1ch */ + strwav->loop_flag = strwav->flags & 0x01; + strwav->interleave = strwav->channels > 2 ? 0x8000 : 0x10000; + + strwav->coefs_offset = 0xdc; + strwav->codec = DSP; + ;VGM_LOG("STR+WAV: header Taz: Wanted (GC)\n"); + return 1; + } + + /* The Fairly OddParents - Breakin' da Rules (Xbox)[2003] */ + if ( read_32bitBE(0x04,streamHeader) == 0x00000900 && + read_32bitLE(0x24,streamHeader) == read_32bitLE(0xb0,streamHeader) && /* sample rate repeat */ + read_32bitLE(0x28,streamHeader) == 0x10 && + read_32bitLE(0xc0,streamHeader)*0x04 + read_32bitLE(0xc4,streamHeader) == header_size /* ~0xe0 + variable */ + ) { + strwav->num_samples = read_32bitLE(0x20,streamHeader); + strwav->sample_rate = read_32bitLE(0x24,streamHeader); + strwav->flags = read_32bitLE(0x2c,streamHeader); + strwav->loop_start = read_32bitLE(0x38,streamHeader); + strwav->loop_end = strwav->num_samples; + + strwav->channels = read_32bitLE(0x70,streamHeader) * (strwav->flags & 0x02 ? 2 : 1); /* tracks of 2/1ch */ + strwav->loop_flag = strwav->flags & 0x01; + strwav->interleave = strwav->channels > 2 ? 0xD000 : 0x0; + + strwav->codec = XBOX; + ;VGM_LOG("STR+WAV: header The Fairly OddParents (Xbox)\n"); + return 1; + } + + /* Bad Boys II (GC)[2004] */ + if ( read_32bitBE(0x04,streamHeader) == 0x00000800 && + read_32bitBE(0x24,streamHeader) == read_32bitBE(0xb0,streamHeader) && /* sample rate repeat */ + read_32bitBE(0x24,streamHeader) == read_32bitBE(read_32bitBE(0xe0,streamHeader)+0x08,streamHeader) && /* sample rate vs 1st DSP header */ + read_32bitBE(0x28,streamHeader) == 0x10 && + read_32bitBE(0xc0,streamHeader)*0x04 + read_32bitBE(0xc4,streamHeader) == header_size /* variable + variable */ + ) { + strwav->num_samples = read_32bitBE(0x20,streamHeader); + strwav->sample_rate = read_32bitBE(0x24,streamHeader); + strwav->flags = read_32bitBE(0x2c,streamHeader); + strwav->loop_start = read_32bitBE(0xd8,streamHeader); + strwav->loop_end = read_32bitBE(0xdc,streamHeader); + + strwav->channels = read_32bitBE(0x70,streamHeader) * read_32bitBE(0x88,streamHeader); /* tracks of Nch */ + strwav->loop_flag = strwav->flags & 0x01; + strwav->interleave = strwav->channels > 2 ? 0x8000 : 0x10000; + + strwav->dsps_table = 0xe0; + strwav->codec = DSP; + ;VGM_LOG("STR+WAV: header Bad Boys II (GC)\n"); + return 1; + } +#if 0 + if ( read_32bitBE(0x04,streamHeader) == 0x00000800 && + read_32bitBE(0x24,streamHeader) == read_32bitBE(0xb0,streamHeader) && /* sample rate repeat */ + read_32bitBE(0x24,streamHeader) == read_32bitBE(read_32bitBE(0xf0,streamHeader)+0x08,streamHeader) && /* sample rate vs 1st DSP header */ + read_32bitBE(0x28,streamHeader) == 0x10 && + read_32bitBE(0xc0,streamHeader)*0x04 + read_32bitBE(0xc4,streamHeader) == header_size /* variable + variable */ + ) { + strwav->num_samples = read_32bitBE(0x20,streamHeader); + strwav->sample_rate = read_32bitBE(0x24,streamHeader); + strwav->flags = read_32bitBE(0x2c,streamHeader); + strwav->loop_start = read_32bitBE(0xd8,streamHeader); + strwav->loop_end = read_32bitBE(0xdc,streamHeader); + + strwav->channels = read_32bitBE(0x70,streamHeader) * read_32bitBE(0x88,streamHeader); /* tracks of Nch */ + strwav->loop_flag = strwav->flags & 0x01; + strwav->interleave = strwav->channels > 2 ? 0x8000 : 0x10000; + + strwav->dsps_table = 0xf0; + strwav->codec = DSP; + ;VGM_LOG("STR+WAV: header Pac-Man World 3 (GC)\n"); + return 1; + } +#endif + /* Bad Boys II (PS2)[2004] */ + /* Pac-Man World 3 (PS2)[2005] */ + if ((read_32bitBE(0x04,streamHeader) == 0x00000800 || + read_32bitBE(0x04,streamHeader) == 0x01000800) && /* rare (PW3 mu_spectral1_explore_2) */ + read_32bitLE(0x24,streamHeader) == read_32bitLE(0x70,streamHeader) && /* sample rate repeat */ + read_32bitLE(0x28,streamHeader) == 0x10 && + read_32bitLE(0x78,streamHeader)*0x04 + read_32bitLE(0x7c,streamHeader) == header_size /* ~0xe0 + variable */ + ) { + strwav->num_samples = read_32bitLE(0x20,streamHeader); + strwav->sample_rate = read_32bitLE(0x24,streamHeader); + strwav->flags = read_32bitLE(0x2c,streamHeader); + strwav->loop_start = read_32bitLE(0x38,streamHeader); + strwav->interleave = read_32bitLE(0x40,streamHeader) == 1 ? 0x8000 : 0x4000; + strwav->loop_end = read_32bitLE(0x54,streamHeader); + + strwav->channels = read_32bitLE(0x40,streamHeader) * (strwav->flags & 0x02 ? 2 : 1); /* tracks of 2/1ch */ + strwav->loop_flag = strwav->flags & 0x01; + strwav->interleave = strwav->channels > 2 ? 0x4000 : 0x8000; + + strwav->codec = PSX; + ;VGM_LOG("STR+WAV: header Bad Boys II (PS2)\n"); + return 1; + } + + /* Zapper: One Wicked Cricket! (PS2)[2005] */ + if ( read_32bitBE(0x04,streamHeader) == 0x00000900 && + read_32bitLE(0x24,streamHeader) == read_32bitLE(0x70,streamHeader) && /* sample rate repeat */ + read_32bitLE(0x28,streamHeader) == 0x10 && + read_32bitLE(0x7c,streamHeader) == header_size /* ~0xD0 */ + ) { + strwav->num_samples = read_32bitLE(0x20,streamHeader); + strwav->sample_rate = read_32bitLE(0x24,streamHeader); + strwav->flags = read_32bitLE(0x2c,streamHeader); + strwav->loop_start = read_32bitLE(0x38,streamHeader); + strwav->loop_end = read_32bitLE(0x54,streamHeader); + + strwav->channels = read_32bitLE(0x40,streamHeader) * (strwav->flags & 0x02 ? 2 : 1); /* tracks of 2/1ch */ + strwav->loop_flag = strwav->flags & 0x01; + strwav->interleave = strwav->channels > 2 ? 0x4000 : 0x8000; + + strwav->codec = PSX; + ;VGM_LOG("STR+WAV: header Zapper (PS2)\n"); + return 1; + } + + /* Zapper: One Wicked Cricket! (GC)[2005] */ + if ( read_32bitBE(0x04,streamHeader) == 0x00000900 && + read_32bitBE(0x24,streamHeader) == read_32bitBE(0xB0,streamHeader) && /* sample rate repeat */ + read_32bitBE(0x28,streamHeader) == 0x10 && + read_32bitLE(0xc0,streamHeader) == header_size /* variable LE size */ + ) { + strwav->num_samples = read_32bitBE(0x20,streamHeader); + strwav->sample_rate = read_32bitBE(0x24,streamHeader); + strwav->flags = read_32bitBE(0x2c,streamHeader); + strwav->loop_start = read_32bitBE(0xd8,streamHeader); + strwav->loop_end = read_32bitBE(0xdc,streamHeader); + + strwav->channels = read_32bitBE(0x70,streamHeader) * read_32bitBE(0x88,streamHeader); /* tracks of Nch */ + strwav->loop_flag = strwav->flags & 0x01; + strwav->interleave = strwav->channels > 2 ? 0x8000 : 0x10000; + + strwav->dsps_table = 0xe0; + strwav->codec = DSP; + ;VGM_LOG("STR+WAV: header Zapper (GC)\n"); + return 1; + } + + /* Pac-Man World 3 (GC)[2005] */ + /* SpongeBob SquarePants: Creature from the Krusty Krab (GC)[2006] */ + if ( read_32bitBE(0x04,streamHeader) == 0x00000800 && + read_32bitBE(0x24,streamHeader) == read_32bitBE(0xb0,streamHeader) && /* sample rate repeat */ + read_32bitBE(0x24,streamHeader) == read_32bitBE(read_32bitBE(0xf0,streamHeader)+0x08,streamHeader) && /* sample rate vs 1st DSP header */ + read_32bitBE(0x28,streamHeader) == 0x10 && + read_32bitBE(0xc0,streamHeader)*0x04 + read_32bitBE(0xc4,streamHeader) == read_32bitBE(0xe0,streamHeader) && /* main size */ + (read_32bitBE(0xe0,streamHeader) + read_32bitBE(0xe4,streamHeader)*0x40 == header_size || /* main size + extradata 1 (config? PMW3 cs2.wav) */ + read_32bitBE(0xe0,streamHeader) + read_32bitBE(0xe4,streamHeader)*0x08 == header_size) /* main size + extradata 2 (ids? SBSP 0_0_mu_hr.wav) */ + ) { + strwav->num_samples = read_32bitBE(0x20,streamHeader); + strwav->sample_rate = read_32bitBE(0x24,streamHeader); + strwav->flags = read_32bitBE(0x2c,streamHeader); + strwav->loop_start = read_32bitBE(0xd8,streamHeader); + strwav->loop_end = read_32bitBE(0xdc,streamHeader); + + strwav->channels = read_32bitBE(0x70,streamHeader) * read_32bitBE(0x88,streamHeader); /* tracks of Nch */ + strwav->loop_flag = strwav->flags & 0x01; + strwav->interleave = strwav->channels > 2 ? 0x8000 : 0x10000; + + strwav->dsps_table = 0xf0; + strwav->codec = DSP; + ;VGM_LOG("STR+WAV: header SpongeBob SquarePants (GC)\n"); + return 1; + } + + /* SpongeBob SquarePants: Creature from the Krusty Krab (PS2)[2006] */ + if ( read_32bitBE(0x04,streamHeader) == 0x00000800 && + read_32bitLE(0x08,streamHeader) == 0x00000000 && + read_32bitLE(0x0c,streamHeader) != header_size && /* some ID */ + (header_size == 0x74 + read_32bitLE(0x64,streamHeader)*0x04 || + header_size == 0x78 + read_32bitLE(0x64,streamHeader)*0x04) + ) { + strwav->loop_start = read_32bitLE(0x24,streamHeader); //not ok? + strwav->num_samples = read_32bitLE(0x30,streamHeader); + strwav->loop_end = read_32bitLE(0x34,streamHeader); + strwav->sample_rate = read_32bitLE(0x38,streamHeader); + strwav->flags = read_32bitLE(0x3c,streamHeader); + + strwav->channels = read_32bitLE(0x64,streamHeader); /* tracks of 1ch */ + strwav->loop_flag = strwav->flags & 0x01; + strwav->interleave = strwav->channels > 4 ? 0x4000 : 0x8000; + + strwav->codec = PSX; + ;VGM_LOG("STR+WAV: header SpongeBob SquarePants (PS2)\n"); + return 1; + } + + /* Tak and the Guardians of Gross (PS2)[2008] */ + if ( read_32bitBE(0x04,streamHeader) == 0x00000800 && + read_32bitLE(0x08,streamHeader) != 0x00000000 && + read_32bitLE(0x0c,streamHeader) == header_size && /* ~0x80+0x04*ch */ + read_32bitLE(0x0c,streamHeader) == 0x80 + read_32bitLE(0x70,streamHeader)*0x04 + ) { + strwav->loop_start = read_32bitLE(0x24,streamHeader); //not ok? + strwav->num_samples = read_32bitLE(0x30,streamHeader); + strwav->loop_end = read_32bitLE(0x34,streamHeader); + strwav->sample_rate = read_32bitLE(0x38,streamHeader); + strwav->flags = read_32bitLE(0x3c,streamHeader); + + strwav->channels = read_32bitLE(0x70,streamHeader); /* tracks of 1ch */ + strwav->loop_flag = strwav->flags & 0x01; + strwav->interleave = strwav->channels > 4 ? 0x4000 : 0x8000; + + strwav->codec = PSX; + ;VGM_LOG("STR+WAV: header Tak (PS2)\n"); + return 1; + } + + /* Tak and the Guardians of Gross (Wii)[2008] */ + /* The House of the Dead: Overkill (Wii)[2009] (not Blitz but still the same format) */ + if ((read_32bitBE(0x04,streamHeader) == 0x00000800 || + read_32bitBE(0x04,streamHeader) == 0x00000700) && /* rare? */ + read_32bitLE(0x08,streamHeader) != 0x00000000 && + read_32bitBE(0x0c,streamHeader) == header_size && /* variable per DSP header */ + read_32bitBE(0x38,streamHeader) == read_32bitBE(read_32bitBE(0x7c,streamHeader)+0x38,streamHeader) /* sample rate vs 1st DSP header */ + ) { + strwav->loop_start = 0; //read_32bitLE(0x24,streamHeader); //not ok? + strwav->num_samples = read_32bitBE(0x30,streamHeader); + strwav->loop_end = read_32bitBE(0x34,streamHeader); + strwav->sample_rate = read_32bitBE(0x38,streamHeader); + strwav->flags = read_32bitBE(0x3c,streamHeader); + + strwav->channels = read_32bitBE(0x70,streamHeader); /* tracks of 1ch */ + strwav->loop_flag = strwav->flags & 0x01; + strwav->interleave = strwav->channels > 4 ? 0x4000 : 0x8000; + + strwav->coefs_table = 0x7c; + strwav->codec = DSP; + ;VGM_LOG("STR+WAV: header Tak (Wii)\n"); + return 1; + } + + + /* unknown */ + goto fail; + +fail: + return 0; +} diff --git a/src/meta/wii_str.c b/src/meta/wii_str.c deleted file mode 100644 index 460a5bad..00000000 --- a/src/meta/wii_str.c +++ /dev/null @@ -1,90 +0,0 @@ -#include "meta.h" -#include "../util.h" - -VGMSTREAM * init_vgmstream_wii_str(STREAMFILE *streamFile) { - - VGMSTREAM * vgmstream = NULL; - STREAMFILE * infileSTH = NULL; - char filename[PATH_LIMIT]; - - char * filenameSTH = NULL; - - int i, j, channel_count, loop_flag; - - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("str",filename_extension(filename))) goto fail; - - /* check for .MIH file */ - filenameSTH=(char *)malloc(strlen(filename)+1); - - if (!filenameSTH) goto fail; - - strcpy(filenameSTH,filename); - strcpy(filenameSTH+strlen(filenameSTH)-3,"sth"); - - infileSTH = streamFile->open(streamFile,filenameSTH,STREAMFILE_DEFAULT_BUFFER_SIZE); - - /* STH File is necessary, so we can't confuse those file */ - /* with others .STR file as it is a very common extension */ - if (!infileSTH) goto fail; - - if(read_32bitLE(0x2C,infileSTH)!=0) - goto fail; - - channel_count = read_32bitBE(0x70,infileSTH); - - if(channel_count==1) - loop_flag = (read_32bitBE(0xD4,infileSTH)==0x00740000); - else - loop_flag = (read_32bitBE(0x124,infileSTH)==0x00740000); - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); - if (!vgmstream) goto fail; - - /* fill in the vital statistics */ - vgmstream->channels = channel_count; - vgmstream->sample_rate = read_32bitBE(0x38,infileSTH); - - vgmstream->interleave_block_size=0x8000; - vgmstream->num_samples=read_32bitBE(0x34,infileSTH); - - vgmstream->coding_type = coding_NGC_DSP; - vgmstream->layout_type = layout_interleave; - - vgmstream->meta_type = meta_WII_STR; - - if(loop_flag) { - vgmstream->loop_start_sample = 0; - vgmstream->loop_end_sample = vgmstream->num_samples; - } - - - /* open the file for reading by each channel */ - { - 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+=(off_t)(vgmstream->interleave_block_size*i); - - for(j=0; j<16; j++) { - vgmstream->ch[i].adpcm_coef[j]=read_16bitBE(0xAC+(j*2)+(i*0x50),infileSTH); - } - } - } - - close_streamfile(infileSTH); infileSTH=NULL; - - return vgmstream; - - /* clean up anything we may have opened */ -fail: - if (infileSTH) close_streamfile(infileSTH); - if (filenameSTH) {free(filenameSTH); filenameSTH=NULL;} - if (vgmstream) close_vgmstream(vgmstream); - return NULL; -} diff --git a/src/vgmstream.c b/src/vgmstream.c index b2799f7e..a1a456a6 100644 --- a/src/vgmstream.c +++ b/src/vgmstream.c @@ -52,7 +52,6 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_raw, init_vgmstream_ps2_vag, init_vgmstream_psx_gms, - init_vgmstream_ps2_str, init_vgmstream_ps2_ild, init_vgmstream_ps2_pnb, init_vgmstream_xbox_wavm, @@ -212,7 +211,6 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_ps2_tk5, init_vgmstream_ps2_vsf_tta, init_vgmstream_ads, - init_vgmstream_wii_str, init_vgmstream_ps2_mcg, init_vgmstream_zsd, init_vgmstream_ps2_vgs, @@ -266,9 +264,6 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_ngc_dsp_mpds, init_vgmstream_dsp_str_ig, init_vgmstream_ea_swvr, - init_vgmstream_ngc_dsp_sth_str1, - init_vgmstream_ngc_dsp_sth_str2, - init_vgmstream_ngc_dsp_sth_str3, init_vgmstream_ps2_b1s, init_vgmstream_ps2_wad, init_vgmstream_dsp_xiii, @@ -424,6 +419,7 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_cks, init_vgmstream_ckb, init_vgmstream_wv6, + init_vgmstream_str_wav, init_vgmstream_txth, /* should go at the end (lower priority) */ #ifdef VGM_USE_FFMPEG diff --git a/src/vgmstream.h b/src/vgmstream.h index f976b0ab..80b2c70c 100644 --- a/src/vgmstream.h +++ b/src/vgmstream.h @@ -341,7 +341,7 @@ typedef enum { meta_PS2_VAGm, /* VAGp Mono File */ meta_PS2_pGAV, /* VAGp with Little Endian Header */ meta_PSX_GMS, /* GMS File (used in PS1 & PS2) [no header_id] */ - meta_PS2_STR, /* Pacman STR+STH files */ + meta_STR_WAV, /* Blitz Games STR+WAV files */ meta_PS2_ILD, /* ILD File */ meta_PS2_PNB, /* PsychoNauts Bgm File */ meta_PS2_VAGs, /* VAG Stereo from Kingdom Hearts */ @@ -505,7 +505,6 @@ typedef enum { meta_PS2_GBTS, /* Pop'n'Music 9 Audio File */ meta_NGC_DSP_IADP, /* Gamecube Interleave DSP */ meta_PS2_TK5, /* Tekken 5 Stream Files */ - meta_WII_STR, /* House of The Dead Overkill STR+STH */ meta_PS2_MCG, /* Gunvari MCG Files (was name .GCM on disk) */ meta_ZSD, /* Dragon Booster ZSD */ meta_RedSpark, /* "RedSpark" RSD (MadWorld) */ @@ -545,7 +544,6 @@ typedef enum { meta_NGC_DSP_MPDS, /* Big Air Freestyle, Terminator 3 */ meta_DSP_STR_IG, /* Micro Machines, Superman Superman: Shadow of Apokolis */ meta_EA_SWVR, /* Future Cop L.A.P.D., Freekstyle */ - meta_NGC_DSP_STH_STR, /* SpongeBob Squarepants (NGC), Taz Wanted (NGC), Cubix (NGC), Tak (WII)*/ meta_PS2_B1S, /* 7 Wonders of the ancient world */ meta_PS2_WAD, /* The golden Compass */ meta_DSP_XIII, /* XIII, possibly more (Ubisoft header???) */