From a809a7c6289817865985d24d22e5eb044964c65e Mon Sep 17 00:00:00 2001 From: bnnm Date: Wed, 25 Jan 2017 20:25:39 +0100 Subject: [PATCH] Add Omikron: The Nomad Soul IMA ADPCM coding and meta --- src/coding/coding.h | 1 + src/coding/ima_decoder.c | 45 +++++++++++++++++++++++ src/formats.c | 4 ++- src/meta/meta.h | 3 +- src/meta/pc_adp.c | 77 +++++++++++++++++++++++++++------------- src/vgmstream.c | 13 ++++++- src/vgmstream.h | 2 ++ 7 files changed, 118 insertions(+), 27 deletions(-) diff --git a/src/coding/coding.h b/src/coding/coding.h index 8baa4dc3..3e787848 100644 --- a/src/coding/coding.h +++ b/src/coding/coding.h @@ -25,6 +25,7 @@ void decode_rad_ima(VGMSTREAM * vgmstream,VGMSTREAMCHANNEL * stream, sample * ou 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); /* ngc_dsp_decoder */ void decode_ngc_dsp(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); diff --git a/src/coding/ima_decoder.c b/src/coding/ima_decoder.c index f4224e58..4fea2412 100644 --- a/src/coding/ima_decoder.c +++ b/src/coding/ima_decoder.c @@ -650,3 +650,48 @@ void decode_snds_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspac stream->adpcm_history1_32 = hist1; stream->adpcm_step_index = step_index; } + +void decode_otns_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { + 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 + (vgmstream->channels==1 ? i/2 : i); //one nibble per channel if stereo + nibble_shift = (vgmstream->channels==1) ? //todo simplify + (i&1?0:4) : //high nibble first(?) + (channel==0?4:0); //low=ch0, high=ch1 (this is correct compared to vids) + + //OTNS nibble expansion (algorithm by aluigi, unsure if it's a known ADPCM codec) + sample_nibble = (read_8bit(byte_offset,stream->streamfile) >> nibble_shift)&0xf; + + sample_decoded = hist1; + step = ADPCMTable[step_index]; + delta = 0; + if(sample_nibble & 4) delta = step << 2; + if(sample_nibble & 2) delta += step << 1; + if(sample_nibble & 1) delta += step; + delta >>= 2; + if(sample_nibble & 8) + sample_decoded -= delta; + else + 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; + + outbuf[sample_count] = (short)(hist1); + } + + stream->adpcm_history1_32 = hist1; + stream->adpcm_step_index = step_index; +} diff --git a/src/formats.c b/src/formats.c index 5b388a19..076509aa 100644 --- a/src/formats.c +++ b/src/formats.c @@ -167,7 +167,7 @@ static const char* extension_list[] = { "ngca", "nps", "npsf", - "nus3bank", //todo not existing? + "nus3bank", "nwa", "oma", //FFmpeg, not parsed (ATRAC3/ATRAC3PLUS/MP3/LPCM/WMA) @@ -405,6 +405,7 @@ static const coding_info coding_info_list[] = { {coding_RAD_IMA_mono, "'Radical' 4-bit IMA ADPCM (mono)"}, {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_WS, "Westwood Studios ADPCM"}, {coding_ACM, "InterPlay ACM"}, {coding_NWA0, "NWA DPCM Level 0"}, @@ -787,6 +788,7 @@ static const meta_info meta_info_list[] = { {meta_HYPERSCAN_KVAG, "Mattel Hyperscan KVAG"}, {meta_IOS_PSND, "PSND Header"}, {meta_BOS_ADP, "ADP! header"}, + {meta_OTNS_ADP, "Omikron: The Nomad Soul ADP header"}, {meta_EB_SFX, "Excitebots .sfx header"}, {meta_EB_SF0, "assumed Excitebots .sf0 by extension"}, {meta_PS3_KLBS, "klBS Header"}, diff --git a/src/meta/meta.h b/src/meta/meta.h index c9f25671..18158b38 100644 --- a/src/meta/meta.h +++ b/src/meta/meta.h @@ -612,7 +612,8 @@ VGMSTREAM * init_vgmstream_hyperscan_kvag(STREAMFILE* streamFile); VGMSTREAM * init_vgmstream_ios_psnd(STREAMFILE* streamFile); -VGMSTREAM * init_vgmstream_bos_adp(STREAMFILE* streamFile); +VGMSTREAM * init_vgmstream_pc_adp_bos(STREAMFILE* streamFile); +VGMSTREAM * init_vgmstream_pc_adp_otns(STREAMFILE* streamFile); VGMSTREAM * init_vgmstream_eb_sfx(STREAMFILE* streamFile); diff --git a/src/meta/pc_adp.c b/src/meta/pc_adp.c index db6949d6..3d2db19c 100644 --- a/src/meta/pc_adp.c +++ b/src/meta/pc_adp.c @@ -1,17 +1,14 @@ #include "meta.h" #include "../util.h" -/* ADP (from Balls of Steel) */ -VGMSTREAM * init_vgmstream_bos_adp(STREAMFILE *streamFile) { +/* ADP - from Balls of Steel */ +VGMSTREAM * init_vgmstream_pc_adp_bos(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; off_t start_offset; int loop_flag = 0; int channel_count; - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("adp",filename_extension(filename))) goto fail; + if (!check_extensions(streamFile,"adp")) goto fail; /* check header */ if (read_32bitBE(0x00,streamFile) != 0x41445021) /* "ADP!" */ @@ -28,36 +25,68 @@ VGMSTREAM * init_vgmstream_bos_adp(STREAMFILE *streamFile) { start_offset = 0x18; vgmstream->channels = channel_count; vgmstream->sample_rate = read_32bitLE(0x0C,streamFile); - vgmstream->coding_type = coding_DVI_IMA; vgmstream->num_samples = read_32bitLE(0x04,streamFile); if (loop_flag) { vgmstream->loop_start_sample = read_32bitLE(0x08,streamFile); vgmstream->loop_end_sample = vgmstream->num_samples; } + vgmstream->coding_type = coding_DVI_IMA; vgmstream->layout_type = layout_none; vgmstream->meta_type = meta_BOS_ADP; - /* open the file for reading */ - { - STREAMFILE * file; - file = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); - if (!file) goto fail; - vgmstream->ch[0].streamfile = file; - - vgmstream->ch[0].channel_start_offset= - vgmstream->ch[0].offset=start_offset; - - // 0x10, 0x12 - both initial history? - //vgmstream->ch[0].adpcm_history1_32 = read_16bitLE(0x10,streamFile); - // 0x14 - initial step index? - //vgmstream->ch[0].adpcm_step_index = read_32bitLE(0x14,streamFile); - } + // 0x10, 0x12 - both initial history? + //vgmstream->ch[0].adpcm_history1_32 = read_16bitLE(0x10,streamFile); + // 0x14 - initial step index? + //vgmstream->ch[0].adpcm_step_index = read_32bitLE(0x14,streamFile); + if (!vgmstream_open_stream(vgmstream, streamFile, start_offset)) + goto fail; return vgmstream; - /* clean up anything we may have opened */ fail: - if (vgmstream) close_vgmstream(vgmstream); + close_vgmstream(vgmstream); + return NULL; +} + +/* ADP - from Omikron: The Nomad Soul (PC/DC) */ +VGMSTREAM * init_vgmstream_pc_adp_otns(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + off_t start_offset, datasize; + int loop_flag = 0, channel_count, stereo_flag; + + if (!check_extensions(streamFile,"adp")) goto fail; + + /* no ID, only a basic 0x10 header with filesize and nulls; do some extra checks */ + datasize = read_32bitLE(0x00,streamFile) & 0x00FFFFFF; /*24 bit*/ + if (datasize + 0x10 != streamFile->get_size(streamFile) + && read_32bitLE(0x04,streamFile) != 0 + && read_32bitLE(0x08,streamFile) != 0 + && read_32bitLE(0x10,streamFile) != 0) + goto fail; + + stereo_flag = read_8bit(0x03, streamFile); + if (stereo_flag > 1 || stereo_flag < 0) goto fail; + channel_count = stereo_flag ? 2 : 1; + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count,loop_flag); + if (!vgmstream) goto fail; + + start_offset = 0x10; + vgmstream->channels = channel_count; + vgmstream->sample_rate = 22050; + vgmstream->num_samples = channel_count== 1 ? datasize*2 : datasize; + + vgmstream->coding_type = coding_OTNS_IMA; + vgmstream->layout_type = layout_none; + vgmstream->meta_type = meta_OTNS_ADP; + + 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 3e2af74c..766bc44b 100644 --- a/src/vgmstream.c +++ b/src/vgmstream.c @@ -312,7 +312,8 @@ VGMSTREAM * (*init_vgmstream_fcns[])(STREAMFILE *streamFile) = { init_vgmstream_ps2_wmus, init_vgmstream_hyperscan_kvag, init_vgmstream_ios_psnd, - init_vgmstream_bos_adp, + init_vgmstream_pc_adp_bos, + init_vgmstream_pc_adp_otns, init_vgmstream_eb_sfx, init_vgmstream_eb_sf0, init_vgmstream_ps3_klbs, @@ -1022,6 +1023,7 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) { case coding_EACS_IMA: case coding_SNDS_IMA: case coding_IMA: + case coding_OTNS_IMA: return 1; case coding_INT_IMA: case coding_INT_DVI_IMA: @@ -1155,6 +1157,7 @@ int get_vgmstream_frame_size(VGMSTREAM * vgmstream) { case coding_IMA: case coding_G721: case coding_SNDS_IMA: + case coding_OTNS_IMA: return 0; case coding_NGC_AFC: return 9; @@ -1547,6 +1550,14 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to samples_to_do,chan); } break; + case coding_OTNS_IMA: + for (chan=0;chanchannels;chan++) { + decode_otns_ima(vgmstream, &vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, + vgmstream->channels,vgmstream->samples_into_block, + samples_to_do,chan); + } + break; + case coding_WS: for (chan=0;chanchannels;chan++) { decode_ws(vgmstream,chan,buffer+samples_written*vgmstream->channels+chan, diff --git a/src/vgmstream.h b/src/vgmstream.h index 049ec8a3..0449e0a8 100644 --- a/src/vgmstream.h +++ b/src/vgmstream.h @@ -119,6 +119,7 @@ typedef enum { coding_APPLE_IMA4, /* Apple Quicktime IMA4 */ coding_DAT4_IMA, /* Eurocom 'DAT4' IMA ADPCM */ coding_SNDS_IMA, /* Heavy Iron Studios .snds IMA ADPCM */ + coding_OTNS_IMA, /* Omikron The Nomad Soul IMA ADPCM */ coding_WS, /* Westwood Studios VBR ADPCM */ coding_MSADPCM, /* Microsoft ADPCM */ @@ -564,6 +565,7 @@ typedef enum { meta_HYPERSCAN_KVAG, // Hyperscan KVAG/BVG meta_IOS_PSND, // Crash Bandicoot Nitro Kart 2 (iOS) meta_BOS_ADP, // ADP! (Balls of Steel, PC) + meta_OTNS_ADP, // Omikron: The Nomad Soul .adp (PC/DC) meta_EB_SFX, // Excitebots .sfx meta_EB_SF0, // Excitebots .sf0 meta_PS3_KLBS, // L@VE ONCE (PS3)