diff --git a/src/coding/coding.h b/src/coding/coding.h index c5c4c8f0..a2158fc7 100644 --- a/src/coding/coding.h +++ b/src/coding/coding.h @@ -35,7 +35,7 @@ void decode_apple_ima4(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channel 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); +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_h4m_ima(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, uint16_t frame_format); size_t ima_bytes_to_samples(size_t bytes, int channels); size_t ms_ima_bytes_to_samples(size_t bytes, int block_align, int channels); diff --git a/src/coding/ima_decoder.c b/src/coding/ima_decoder.c index ee8f4c50..7825db65 100644 --- a/src/coding/ima_decoder.c +++ b/src/coding/ima_decoder.c @@ -1009,8 +1009,9 @@ 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) { +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) { 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; @@ -1018,7 +1019,7 @@ void decode_ubi_ima(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspa //internal interleave //header in the beginning of the stream - if (stream->channel_start_offset == stream->offset) { + if (has_header && 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; @@ -1051,8 +1052,12 @@ 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; for (i = first_sample; i < first_sample + samples_to_do; i++, sample_count += channelspacing) { off_t byte_offset = channelspacing == 1 ? diff --git a/src/formats.c b/src/formats.c index abc3442d..32319cb5 100644 --- a/src/formats.c +++ b/src/formats.c @@ -727,7 +727,6 @@ 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_H4M_IMA, "Hudson HVQM4 4-bit IMA ADPCM"}, {coding_MSADPCM, "Microsoft 4-bit ADPCM"}, @@ -850,6 +849,7 @@ static const layout_info layout_info_list[] = { {layout_blocked_xa_aiff, "blocked (XA AIFF)"}, {layout_blocked_vs_square, "blocked (Square VS)"}, {layout_blocked_vid1, "blocked (VID1)"}, + {layout_blocked_ubi_sce, "blocked (Ubi SCE)"}, }; static const meta_info meta_info_list[] = { diff --git a/src/layout/blocked.c b/src/layout/blocked.c index 6d804c6d..32335399 100644 --- a/src/layout/blocked.c +++ b/src/layout/blocked.c @@ -208,6 +208,9 @@ void block_update(off_t block_offset, VGMSTREAM * vgmstream) { case layout_blocked_vid1: block_update_vid1(block_offset,vgmstream); break; + case layout_blocked_ubi_sce: + block_update_ubi_sce(block_offset,vgmstream); + break; default: /* not a blocked layout */ break; } diff --git a/src/layout/blocked_ubi_sce.c b/src/layout/blocked_ubi_sce.c new file mode 100644 index 00000000..7c848fd9 --- /dev/null +++ b/src/layout/blocked_ubi_sce.c @@ -0,0 +1,57 @@ +#include "layout.h" +#include "../vgmstream.h" + +/* weird mix of Ubi ADPCM format with Ubi IMA, found in Splinter Cell Essentials (PSP) */ +void block_update_ubi_sce(off_t block_offset, VGMSTREAM* vgmstream) { + STREAMFILE* sf = vgmstream->ch[0].streamfile; + int i, channels; + size_t header_size, frame_size, subframe_size, padding_size; + + /* format mimics Ubi ADPCM's: + * - 0x34/38 header with frame size (ex. 0x600), pre-read in meta + * xN frames: + * - 0x34 channel header per channel, with ADPCM config + * - subframe (ex. 0x300) + padding byte + * - subframe (ex. 0x300) + padding byte + * + * to skip the padding byte we'll detect subframes using codec_config as a counter + * (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 */ + } + else { + header_size = 0x00; + } + vgmstream->codec_config ^= 1; /* swap counter bit */ + + channels = vgmstream->channels; + frame_size = vgmstream->full_block_size; + subframe_size = frame_size / 2; + 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; + + for (i = 0; i < vgmstream->channels; i++) { + vgmstream->ch[i].offset = block_offset + header_size * channels; + + if (header_size > 0) { + 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); + + /* TODO figure out + * First step seems to always be a special value for the decoder, unsure of meaning. + * 0 = too quiet and max = 88 = waveform starts a bit off and clicky. First hist is usually +-1, + * other frames look, fine not sure what are they aiming for. + */ + if (vgmstream->ch[i].adpcm_step_index == 0x500) { + vgmstream->ch[i].adpcm_step_index = 88; + } + } + } +} diff --git a/src/layout/layout.h b/src/layout/layout.h index b498761b..488f3b19 100644 --- a/src/layout/layout.h +++ b/src/layout/layout.h @@ -47,6 +47,7 @@ void block_update_h4m(off_t block_offset, VGMSTREAM * vgmstream); void block_update_xa_aiff(off_t block_offset, VGMSTREAM * vgmstream); void block_update_vs_square(off_t block_offset, VGMSTREAM * vgmstream); void block_update_vid1(off_t block_offset, VGMSTREAM* vgmstream); +void block_update_ubi_sce(off_t block_offset, VGMSTREAM* vgmstream); /* other layouts */ void render_vgmstream_interleave(sample_t * buffer, int32_t sample_count, VGMSTREAM * vgmstream); diff --git a/src/libvgmstream.vcproj b/src/libvgmstream.vcproj index fbb5dc1f..fb2b2030 100644 --- a/src/libvgmstream.vcproj +++ b/src/libvgmstream.vcproj @@ -2390,10 +2390,14 @@ RelativePath=".\layout\blocked_thp.c" > - - + + + + diff --git a/src/libvgmstream.vcxproj b/src/libvgmstream.vcxproj index 3b59378f..2724dc54 100644 --- a/src/libvgmstream.vcxproj +++ b/src/libvgmstream.vcxproj @@ -173,6 +173,7 @@ + diff --git a/src/libvgmstream.vcxproj.filters b/src/libvgmstream.vcxproj.filters index 9f42b812..ffcad061 100644 --- a/src/libvgmstream.vcxproj.filters +++ b/src/libvgmstream.vcxproj.filters @@ -1492,6 +1492,9 @@ layout\Source Files + + layout\Source Files + coding\Source Files diff --git a/src/meta/ubi_sb.c b/src/meta/ubi_sb.c index ab058e12..396dc382 100644 --- a/src/meta/ubi_sb.c +++ b/src/meta/ubi_sb.c @@ -7,7 +7,7 @@ #define SB_MAX_LAYER_COUNT 16 /* arbitrary max */ #define SB_MAX_CHAIN_COUNT 256 /* +150 exist in Tonic Trouble */ -typedef enum { UBI_IMA, UBI_ADPCM, RAW_PCM, RAW_PSX, RAW_DSP, RAW_XBOX, FMT_VAG, FMT_AT3, RAW_AT3, FMT_XMA1, RAW_XMA1, FMT_OGG, FMT_CWAV, FMT_APM, FMT_MPDX } ubi_sb_codec; +typedef enum { UBI_IMA, UBI_ADPCM, RAW_PCM, RAW_PSX, RAW_DSP, RAW_XBOX, FMT_VAG, FMT_AT3, RAW_AT3, FMT_XMA1, RAW_XMA1, FMT_OGG, FMT_CWAV, FMT_APM, FMT_MPDX, UBI_IMA_SCE } ubi_sb_codec; typedef enum { UBI_PC, UBI_PS2, UBI_XBOX, UBI_GC, UBI_X360, UBI_PSP, UBI_PS3, UBI_WII, UBI_3DS } ubi_sb_platform; typedef enum { UBI_NONE = 0, UBI_AUDIO, UBI_LAYER, UBI_SEQUENCE, UBI_SILENCE } ubi_sb_type; @@ -496,6 +496,16 @@ static VGMSTREAM * init_vgmstream_ubi_sb_base(ubi_sb_header *sb, STREAMFILE *str vgmstream->layout_type = layout_none; break; + case UBI_IMA_SCE: + vgmstream->coding_type = coding_UBI_IMA; + vgmstream->layout_type = layout_blocked_ubi_sce; + vgmstream->full_block_size = read_32bitLE(0x18, streamData); + + /* this "codec" is an ugly hack of IMA w/ Ubi ADPCM's frame format, surely to + * shoehorn a simpler codec into the existing code when porting the game */ + start_offset += 0x08 + 0x30; /* skip Ubi ADPCM header */ + break; + case UBI_ADPCM: /* custom Ubi 4/6-bit ADPCM used in early games: * - Splinter Cell (PC): 4-bit w/ 2ch (music), 6-bit w/ 1ch (sfx) @@ -1553,10 +1563,9 @@ static int parse_stream_codec(ubi_sb_header * sb) { case UBI_PS3: sb->codec = RAW_PSX; /* PS3 */ break; - case UBI_PSP: - /* TODO: IMA using Ubisoft ADPCM frame layout [Splinter Cell: Essentials (PSP)] */ - VGM_LOG("UBI SB: Unimplemented custom IMA codec.\n"); - goto fail; + case UBI_PSP: /* Splinter Cell: Essentials (PSP) */ + sb->codec = UBI_IMA_SCE; + break; default: sb->codec = UBI_ADPCM; break; diff --git a/src/vgmstream.c b/src/vgmstream.c index b9370f3d..a504c4c1 100644 --- a/src/vgmstream.c +++ b/src/vgmstream.c @@ -1109,6 +1109,7 @@ void render_vgmstream(sample_t * buffer, int32_t sample_count, VGMSTREAM * vgmst case layout_blocked_xa_aiff: case layout_blocked_vs_square: case layout_blocked_vid1: + case layout_blocked_ubi_sce: render_vgmstream_blocked(buffer,sample_count,vgmstream); break; case layout_segmented: @@ -1947,7 +1948,7 @@ 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+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do, ch); + vgmstream->channels,vgmstream->samples_into_block,samples_to_do, ch, vgmstream->codec_config); } break; case coding_H4M_IMA: