From 19f69e14b72f299dfdb110dc86d3249c49ea02d2 Mon Sep 17 00:00:00 2001 From: bnnm Date: Thu, 6 Sep 2018 20:25:04 +0200 Subject: [PATCH] Add ALP IMA decoder [Lego Racers (PC)] --- src/coding/coding.h | 1 + src/coding/ima_decoder.c | 41 ++++++++++++++++++++++++++++++++++++++++ src/formats.c | 1 + src/meta/tun.c | 7 ++----- src/vgmstream.c | 9 +++++++++ src/vgmstream.h | 8 ++++++-- 6 files changed, 60 insertions(+), 7 deletions(-) diff --git a/src/coding/coding.h b/src/coding/coding.h index 25f96f1d..3ca4e0d1 100644 --- a/src/coding/coding.h +++ b/src/coding/coding.h @@ -20,6 +20,7 @@ void decode_3ds_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspaci void decode_snds_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); 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_alp_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); diff --git a/src/coding/ima_decoder.c b/src/coding/ima_decoder.c index 70d73660..b7c3015b 100644 --- a/src/coding/ima_decoder.c +++ b/src/coding/ima_decoder.c @@ -188,6 +188,25 @@ static void wv6_ima_expand_nibble(VGMSTREAMCHANNEL * stream, off_t byte_offset, if (*step_index > 88) *step_index=88; } +/* Lego Racers (PC) .TUN variation, reverse engineered from the .exe */ +static void alp_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) >> 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 */ /* ************************************ */ @@ -310,6 +329,28 @@ void decode_wv6_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspaci stream->adpcm_step_index = step_index; } +/* ALT IMA, DVI IMA with custom nibble expand */ +void decode_alp_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 + + alp_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 ae27df00..cc52f7df 100644 --- a/src/formats.c +++ b/src/formats.c @@ -539,6 +539,7 @@ static const coding_info coding_info_list[] = { {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_ALP_IMA, "High Voltage ALP 4-bit IMA ADPCM"}, {coding_MS_IMA, "Microsoft 4-bit IMA ADPCM"}, {coding_XBOX_IMA, "XBOX 4-bit IMA ADPCM"}, diff --git a/src/meta/tun.c b/src/meta/tun.c index cf270679..b49e9ba9 100644 --- a/src/meta/tun.c +++ b/src/meta/tun.c @@ -7,11 +7,9 @@ VGMSTREAM * init_vgmstream_tun(STREAMFILE *streamFile) { off_t start_offset; int loop_flag, channel_count; - /* check extension */ + /* checks */ if ( !check_extensions(streamFile,"tun") ) goto fail; - - /* check header */ if (read_32bitBE(0x00,streamFile) != 0x414C5020) /* "ALP " */ goto fail; @@ -28,12 +26,11 @@ VGMSTREAM * init_vgmstream_tun(STREAMFILE *streamFile) { vgmstream->sample_rate = 22050; vgmstream->num_samples = ima_bytes_to_samples(get_streamfile_size(streamFile) - 0x10, channel_count); - vgmstream->coding_type = coding_DVI_IMA_int; + vgmstream->coding_type = coding_ALP_IMA; vgmstream->layout_type = layout_interleave; vgmstream->interleave_block_size = 0x01; vgmstream->meta_type = meta_TUN; - /* open the file for reading */ if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) ) goto fail; return vgmstream; diff --git a/src/vgmstream.c b/src/vgmstream.c index 49d94bbe..2119077f 100644 --- a/src/vgmstream.c +++ b/src/vgmstream.c @@ -1112,6 +1112,7 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) { case coding_DVI_IMA_int: case coding_3DS_IMA: case coding_WV6_IMA: + case coding_ALP_IMA: return 2; case coding_XBOX_IMA: case coding_XBOX_IMA_mch: @@ -1283,6 +1284,7 @@ int get_vgmstream_frame_size(VGMSTREAM * vgmstream) { case coding_DVI_IMA_int: case coding_3DS_IMA: case coding_WV6_IMA: + case coding_ALP_IMA: return 0x01; case coding_MS_IMA: case coding_RAD_IMA: @@ -1745,6 +1747,13 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to vgmstream->channels,vgmstream->samples_into_block,samples_to_do); } break; + case coding_ALP_IMA: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_alp_ima(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do); + } + break; + case coding_APPLE_IMA4: for (ch = 0; ch < vgmstream->channels; ch++) { decode_apple_ima4(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, diff --git a/src/vgmstream.h b/src/vgmstream.h index dd8bb379..92701228 100644 --- a/src/vgmstream.h +++ b/src/vgmstream.h @@ -118,6 +118,7 @@ typedef enum { 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_ALP_IMA, /* High Voltage ALP 4-bit IMA ADPCM */ coding_MS_IMA, /* Microsoft IMA ADPCM */ coding_XBOX_IMA, /* XBOX IMA ADPCM */ @@ -768,15 +769,18 @@ typedef struct { layout_t layout_type; /* type of layout for data */ meta_t meta_type; /* how we know the metadata */ - /* subsongs and internal config */ + /* subsongs */ int num_streams; /* for multi-stream formats (0=not set/one stream, 1=one stream) */ int stream_index; /* selected stream (also 1-based) */ char stream_name[STREAM_NAME_SIZE]; /* name of the current stream (info), if the file stores it and it's filled */ size_t stream_size; /* info to properly calculate bitrate in case of subsongs */ + /* config */ + int allow_dual_stereo; /* search for dual stereo (file_L.ext + file_R.ext = single stereo file) */ uint32_t channel_mask; /* to silence crossfading subsongs/layers */ int channel_mappings_on; /* channel mappings are active */ int channel_mappings[32]; /* swap channel "i" with "[i]" */ - int allow_dual_stereo; /* search for dual stereo (file_L.ext + file_R.ext = single stereo file) */ + double config_loops; /* appropriate number of loops (config request for players) */ + int config_nofade; /* continue normally after target loop count (config request for players) */ /* looping */ int loop_flag; /* is this stream looped? */