diff --git a/src/coding/coding.h b/src/coding/coding.h index 6ddaf784..9545218b 100644 --- a/src/coding/coding.h +++ b/src/coding/coding.h @@ -37,7 +37,8 @@ void decode_apple_ima4(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelsp void decode_fsb_ima(VGMSTREAM* vgmstream, VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); void decode_wwise_ima(VGMSTREAM* vgmstream, VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); void decode_awc_ima(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); -void decode_ubi_ima(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, int codec_config); +void decode_ubi_ima(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); +void decode_ubi_sce_ima(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); void decode_h4m_ima(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, uint16_t frame_format); void decode_cd_ima(VGMSTREAMCHANNEL* stream, sample_t* 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 4721bbdb..a0b4599f 100644 --- a/src/coding/ima_decoder.c +++ b/src/coding/ima_decoder.c @@ -1065,9 +1065,8 @@ void decode_awc_ima(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspa /* DVI stereo/mono with some mini header and sample output */ -void decode_ubi_ima(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, int codec_config) { +void decode_ubi_ima(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { int i, sample_count = 0; - int has_header = (codec_config & 0x80) == 0; int32_t hist1 = stream->adpcm_history1_32; int step_index = stream->adpcm_step_index; @@ -1075,7 +1074,7 @@ void decode_ubi_ima(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspa //internal interleave //header in the beginning of the stream - if (has_header && stream->channel_start_offset == stream->offset) { + if (stream->channel_start_offset == stream->offset) { int version, big_endian, header_samples, max_samples_to_do; int16_t (*read_16bit)(off_t,STREAMFILE*) = NULL; off_t offset = stream->offset; @@ -1108,16 +1107,10 @@ void decode_ubi_ima(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspa } } - if (has_header) { - first_sample -= 10; //todo fix hack (needed to adjust nibble offset below) + first_sample -= 10; //todo fix hack (needed to adjust nibble offset below) - if (step_index < 0) step_index = 0; - if (step_index > 88) step_index = 88; - } else { - if (step_index < 0) step_index = 0; - if (step_index > 89) step_index = 89; - } - + if (step_index < 0) step_index = 0; + if (step_index > 88) step_index = 88; for (i = first_sample; i < first_sample + samples_to_do; i++, sample_count += channelspacing) { off_t byte_offset = channelspacing == 1 ? @@ -1131,12 +1124,39 @@ void decode_ubi_ima(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspa outbuf[sample_count] = (short)(hist1); /* all samples are written */ } - //external interleave + stream->adpcm_history1_32 = hist1; + stream->adpcm_step_index = step_index; +} + +/* standard IMA but with a tweak for Ubi's encoder bug with step index (see blocked_ubi_sce.c) */ +void decode_ubi_sce_ima(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { + int i, sample_count = 0; + + int32_t hist1 = stream->adpcm_history1_32; + int step_index = stream->adpcm_step_index; + + //internal interleave + + if (step_index < 0) step_index = 0; + if (step_index > 89) step_index = 89; + + for (i = first_sample; i < first_sample + samples_to_do; i++, sample_count += channelspacing) { + off_t byte_offset = channelspacing == 1 ? + stream->offset + i/2 : /* mono mode */ + stream->offset + i; /* stereo mode */ + int nibble_shift = channelspacing == 1 ? + (!(i%2) ? 4:0) : /* mono mode (high first) */ + (channel==0 ? 4:0); /* stereo mode (high=L,low=R) */ + + std_ima_expand_nibble(stream, byte_offset,nibble_shift, &hist1, &step_index); + outbuf[sample_count] = (short)(hist1); /* all samples are written */ + } stream->adpcm_history1_32 = hist1; stream->adpcm_step_index = step_index; } + /* IMA with variable frame formats controlled by the block layout. The original code uses * tables mapping all standard IMA combinations (to optimize calculations), but decodes the same. * Based on HCS's and Nisto's reverse engineering in h4m_audio_decode. */ diff --git a/src/decode.c b/src/decode.c index b44407a4..14a2fc7a 100644 --- a/src/decode.c +++ b/src/decode.c @@ -355,6 +355,7 @@ int get_vgmstream_samples_per_frame(VGMSTREAM* vgmstream) { case coding_SNDS_IMA: case coding_OTNS_IMA: case coding_UBI_IMA: + case coding_UBI_SCE_IMA: case coding_OKI16: case coding_OKI4S: case coding_MTF_IMA: @@ -580,6 +581,8 @@ int get_vgmstream_frame_size(VGMSTREAM* vgmstream) { return 0; //todo: 0x01? case coding_UBI_IMA: /* variable (PCM then IMA) */ return 0; + case coding_UBI_SCE_IMA: + return 0; case coding_XBOX_IMA: //todo should be 0x48 when stereo, but blocked/interleave layout don't understand stereo codecs return 0x24; //vgmstream->channels==1 ? 0x24 : 0x48; @@ -1145,8 +1148,13 @@ void decode_vgmstream(VGMSTREAM* vgmstream, int samples_written, int samples_to_ case coding_UBI_IMA: for (ch = 0; ch < vgmstream->channels; ch++) { decode_ubi_ima(&vgmstream->ch[ch], buffer+ch, - vgmstream->channels, vgmstream->samples_into_block, samples_to_do, ch, - vgmstream->codec_config); + vgmstream->channels, vgmstream->samples_into_block, samples_to_do, ch); + } + break; + case coding_UBI_SCE_IMA: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_ubi_sce_ima(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do, ch); } break; case coding_H4M_IMA: diff --git a/src/formats.c b/src/formats.c index 295bf2c9..d446b535 100644 --- a/src/formats.c +++ b/src/formats.c @@ -749,6 +749,7 @@ static const coding_info coding_info_list[] = { {coding_REF_IMA, "Reflections 4-bit IMA ADPCM"}, {coding_AWC_IMA, "Rockstar AWC 4-bit IMA ADPCM"}, {coding_UBI_IMA, "Ubisoft 4-bit IMA ADPCM"}, + {coding_UBI_SCE_IMA, "Ubisoft 4-bit SCE IMA ADPCM"}, {coding_H4M_IMA, "Hudson HVQM4 4-bit IMA ADPCM"}, {coding_CD_IMA, "Crystal Dynamics 4-bit IMA ADPCM"}, diff --git a/src/layout/blocked_ubi_sce.c b/src/layout/blocked_ubi_sce.c index 640aca39..318438ab 100644 --- a/src/layout/blocked_ubi_sce.c +++ b/src/layout/blocked_ubi_sce.c @@ -1,4 +1,5 @@ #include "layout.h" +#include "../coding/coding.h" #include "../vgmstream.h" /* weird mix of Ubi ADPCM format with Ubi IMA, found in Splinter Cell Essentials (PSP) */ @@ -18,8 +19,6 @@ void block_update_ubi_sce(off_t block_offset, VGMSTREAM* vgmstream) { * (higher bit has a special meaning) */ - vgmstream->codec_config |= 0x80; /* flag for decoder, ugly I know */ - if ((vgmstream->codec_config & 1) == 0) { header_size = 0x34; /* read header in first subframe */ } @@ -34,8 +33,8 @@ void block_update_ubi_sce(off_t block_offset, VGMSTREAM* vgmstream) { padding_size = 0x01; vgmstream->current_block_offset = block_offset; - vgmstream->current_block_size = subframe_size; vgmstream->next_block_offset = block_offset + header_size * vgmstream->channels + subframe_size + padding_size; + vgmstream->current_block_samples = ima_bytes_to_samples(subframe_size, channels); for (i = 0; i < vgmstream->channels; i++) { vgmstream->ch[i].offset = block_offset + header_size * channels; @@ -44,11 +43,11 @@ void block_update_ubi_sce(off_t block_offset, VGMSTREAM* vgmstream) { vgmstream->ch[i].adpcm_step_index = read_32bitLE(block_offset + header_size * i + 0x04, sf); vgmstream->ch[i].adpcm_history1_32 = read_32bitLE(block_offset + header_size * i + 0x08, sf); - /* First step is always 0x500, not sure if it's a bug or a feature but the game just takes it as is and - * ends up reading 0 from out-of-bounds memory area which causes a pop at the start. Yikes. - * It gets clampled later so the rest of the sound plays ok. - * We put 89 here as our special index which contains 0 to simulate this. - */ + /* First step is always 0x500, not sure if it's a bug or a feature but the game just takes it as is and + * ends up reading 0 from out-of-bounds memory area which causes a pop at the start. Yikes. + * It gets clampled later so the rest of the sound plays ok. + * We put 89 here as our special index which contains 0 to simulate this. + */ if (vgmstream->ch[i].adpcm_step_index == 0x500) { vgmstream->ch[i].adpcm_step_index = 89; } diff --git a/src/meta/ubi_sb.c b/src/meta/ubi_sb.c index c0202420..6a054624 100644 --- a/src/meta/ubi_sb.c +++ b/src/meta/ubi_sb.c @@ -984,7 +984,7 @@ static VGMSTREAM* init_vgmstream_ubi_sb_base(ubi_sb_header* sb, STREAMFILE* sf_h break; case UBI_IMA_SCE: - vgmstream->coding_type = coding_UBI_IMA; + vgmstream->coding_type = coding_UBI_SCE_IMA; vgmstream->layout_type = layout_blocked_ubi_sce; vgmstream->full_block_size = read_32bitLE(0x18, sf_data); diff --git a/src/vgmstream.h b/src/vgmstream.h index 2403be28..915b30e4 100644 --- a/src/vgmstream.h +++ b/src/vgmstream.h @@ -141,6 +141,7 @@ typedef enum { coding_REF_IMA, /* Reflections IMA ADPCM */ coding_AWC_IMA, /* Rockstar AWC IMA ADPCM */ coding_UBI_IMA, /* Ubisoft IMA ADPCM */ + coding_UBI_SCE_IMA, /* Ubisoft SCE IMA ADPCM */ coding_H4M_IMA, /* H4M IMA ADPCM (stereo or mono, high nibble first) */ coding_MTF_IMA, /* Capcom MT Framework IMA ADPCM */ coding_CD_IMA, /* Crystal Dynamics IMA ADPCM */