From ad755b85c6137bbbcd1cda28db39e43e1a43137e Mon Sep 17 00:00:00 2001 From: bnnm Date: Wed, 28 Mar 2018 22:58:25 +0200 Subject: [PATCH] Fix mono/interleave/PCM16 .spsd [Giga Wing 2, Virtua Tennis 2 (Naomi)] --- src/coding/coding.h | 1 + src/coding/yamaha_decoder.c | 7 +++- src/formats.c | 2 +- src/meta/naomi_spsd.c | 83 +++++++++++++++++++++++++++++++------ 4 files changed, 78 insertions(+), 15 deletions(-) diff --git a/src/coding/coding.h b/src/coding/coding.h index 2e48f1c8..308abdb9 100644 --- a/src/coding/coding.h +++ b/src/coding/coding.h @@ -119,6 +119,7 @@ long msadpcm_bytes_to_samples(long bytes, int block_size, int channels); void decode_aica(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, int is_stereo); void decode_yamaha(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); void decode_yamaha_nxap(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); +size_t aica_bytes_to_samples(size_t bytes, int channels); size_t yamaha_bytes_to_samples(size_t bytes, int channels); /* nds_procyon_decoder */ diff --git a/src/coding/yamaha_decoder.c b/src/coding/yamaha_decoder.c index e6b2b165..15b1e9af 100644 --- a/src/coding/yamaha_decoder.c +++ b/src/coding/yamaha_decoder.c @@ -15,7 +15,7 @@ static const int scale_delta[16] = { }; -/* Yamaha AICA ADPCM, as seen in Naomi/Dreamcast. Possibly like RIFF codec 0x20 or used in older arcade sound chips. */ +/* raw Yamaha ADPCM a.k.a AICA as it's mainly used in Naomi/Dreamcast (also in RIFF and older arcade sound chips). */ void decode_aica(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, int is_stereo) { int i, sample_count; @@ -145,6 +145,11 @@ void decode_yamaha_nxap(VGMSTREAMCHANNEL * stream, sample * outbuf, int channels stream->adpcm_step_index = step_size; } +size_t aica_bytes_to_samples(size_t bytes, int channels) { + /* 2 samples per byte (2 nibbles) in stereo or mono config */ + return bytes * 2 / channels; +} + size_t yamaha_bytes_to_samples(size_t bytes, int channels) { int block_align = 0x40; diff --git a/src/formats.c b/src/formats.c index ee8c1ebc..b4ed2e70 100644 --- a/src/formats.c +++ b/src/formats.c @@ -751,7 +751,7 @@ static const meta_info meta_info_list[] = { {meta_RSD6AT3P, "Radical RSD6/AT3+ header"}, {meta_RSD6WMA, "Radical RSD6/WMA header"}, {meta_DC_ASD, "ASD Header"}, - {meta_NAOMI_SPSD, "SPSD Header"}, + {meta_NAOMI_SPSD, "Naomi SPSD header"}, {meta_FFXI_BGW, "BGW BGMStream header"}, {meta_FFXI_SPW, "SPW SeWave header"}, {meta_PS2_ASS, "ASS Header"}, diff --git a/src/meta/naomi_spsd.c b/src/meta/naomi_spsd.c index 8480510e..5780774b 100644 --- a/src/meta/naomi_spsd.c +++ b/src/meta/naomi_spsd.c @@ -1,48 +1,105 @@ #include "meta.h" -#include "../util.h" +#include "../coding/coding.h" -/* SPSD - Naomi GD-ROM streams [Guilty Gear X (Naomi), Crazy Taxi (Naomi), Virtua Tennis 2 (Naomi)] */ +/* SPSD - Naomi (arcade) streams [Guilty Gear X (Naomi), Crazy Taxi (Naomi), Virtua Tennis 2 (Naomi)] */ VGMSTREAM * init_vgmstream_naomi_spsd(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; off_t start_offset; size_t data_size; - int loop_flag, channel_count; + int loop_flag, channel_count, codec, flags, index; /* checks */ + /* .spsd: header id */ if (!check_extensions(streamFile, "spsd")) goto fail; if (read_32bitBE(0x00,streamFile) != 0x53505344) /* "SPSD" */ goto fail; - loop_flag = 0; - channel_count = 2; + if (read_32bitBE(0x04,streamFile) != 0x01010004 && /* standard version */ + read_32bitBE(0x04,streamFile) != 0x00010004) /* uncommon version [Crazy Taxi (Naomi)] */ + goto fail; + + codec = read_8bit(0x08,streamFile); + flags = read_8bit(0x09,streamFile); + index = read_16bitLE(0x0a,streamFile); + data_size = read_32bitLE(0x0c,streamFile); + //if (data_size + start_offset != get_streamfile_size(streamFile)) + // goto fail; /* some rips out there have incorrect padding */ + + //todo with 0x80 seems 0x2c is a loop_start_sample but must be adjusted to +1 block? (uncommon flag though) + loop_flag = (flags & 0x80); + channel_count = ((flags & 0x01) || (flags & 0x02)) ? 2 : 1; /* 0x02 is rare but looks normal (Virtua Tennis 2) */ start_offset = 0x40; - data_size = get_streamfile_size(streamFile) - start_offset; + /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; vgmstream->sample_rate = (uint16_t)read_16bitLE(0x2A,streamFile); - vgmstream->num_samples = read_32bitLE(0x0C,streamFile); vgmstream->meta_type = meta_NAOMI_SPSD; - switch (read_8bit(0x08,streamFile)) { + switch (codec) { + case 0x00: /* [Virtua Tennis 2 (Naomi), Club Kart - European Session (Naomi)] */ + vgmstream->coding_type = coding_PCM16LE; + vgmstream->num_samples = pcm_bytes_to_samples(data_size,channel_count,16); + vgmstream->loop_start_sample = read_32bitLE(0x2c,streamFile) + pcm_bytes_to_samples(0x2000*channel_count,channel_count,16); + vgmstream->loop_end_sample = vgmstream->num_samples; + break; + case 0x01: /* [Virtua Tennis 2 (Naomi)] */ vgmstream->coding_type = coding_PCM8; + vgmstream->num_samples = pcm_bytes_to_samples(data_size,channel_count,8); + vgmstream->loop_start_sample = read_32bitLE(0x2c,streamFile) + pcm_bytes_to_samples(0x2000*channel_count,channel_count,8); + vgmstream->loop_end_sample = vgmstream->num_samples; + break; - case 0x03: + + case 0x03: /* standard */ vgmstream->coding_type = coding_AICA_int; + vgmstream->num_samples = aica_bytes_to_samples(data_size,channel_count); + vgmstream->loop_start_sample = /*read_32bitLE(0x2c,streamFile) +*/ aica_bytes_to_samples(0x2000*channel_count,channel_count); + vgmstream->loop_end_sample = vgmstream->num_samples; break; + default: goto fail; } - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = 0x2000; - if (vgmstream->interleave_block_size) - vgmstream->interleave_last_block_size = (data_size % (vgmstream->interleave_block_size*vgmstream->channels)) / vgmstream->channels; + + /* interleave index, maybe */ + switch(index) { + case 0x0000: + if (channel_count != 1) goto fail; + vgmstream->layout_type = layout_none; + break; + + case 0x000d: + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x2000; + if (vgmstream->interleave_block_size) + vgmstream->interleave_last_block_size = (data_size % (vgmstream->interleave_block_size*vgmstream->channels)) / vgmstream->channels; + break; + + case 0x00ff: + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = data_size / channel_count; + break; + + default: + goto fail; + } + + /* todo seems to decode slightly incorrectly in after certain data (loop section start?) + * may depend on values in 0x20 or 0x2c [ex. Marvel vs Capcom 2 (Naomi)] + * at 0x30(4*ch) is some config per channel but doesn't seem to affect ADPCM (found with PCM too) */ + { + int i; + for (i = 0; i < channel_count; i++) { + vgmstream->ch[i].adpcm_step_index = 0x7f; + } + } if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))