diff --git a/src/meta/meta.h b/src/meta/meta.h index 9de531ec..73abf1b2 100644 --- a/src/meta/meta.h +++ b/src/meta/meta.h @@ -513,4 +513,6 @@ VGMSTREAM * init_vgmstream_gh3_bar(STREAMFILE* streamFile); VGMSTREAM * init_vgmstream_ffw(STREAMFILE* streamFile); +VGMSTREAM * init_vgmstream_dsp_dspw(STREAMFILE* streamFile); + #endif diff --git a/src/meta/ngc_dsp_std.c b/src/meta/ngc_dsp_std.c index 81dcf6cf..5c16db8c 100644 --- a/src/meta/ngc_dsp_std.c +++ b/src/meta/ngc_dsp_std.c @@ -2228,3 +2228,119 @@ fail: if (vgmstream) close_vgmstream(vgmstream); return NULL; } + + +/* .dspw files, two DSP files stuck together */ +/* found in Sengoku Basara 3 Wii */ +VGMSTREAM * init_vgmstream_dsp_dspw(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + char filename[260]; + + off_t channel_1_start, channel_2_start, channel_1_size, channel_2_size; + + struct dsp_header ch0_header,ch1_header; + + /* check extension, case insensitive */ + streamFile->get_name(streamFile,filename,sizeof(filename)); + if (strcasecmp("dspw",filename_extension(filename))) goto fail; + + if (read_32bitBE(0x0,streamFile) != 0x44535057) // DSPW + goto fail; + + /* read .dspw header */ + channel_1_start = 0x20; + channel_2_start = (get_streamfile_size(streamFile)/2)+0x10; + + /* get DSP headers */ + if (read_dsp_header(&ch0_header, channel_1_start, streamFile)) goto fail; + if (read_dsp_header(&ch1_header, channel_2_start, streamFile)) goto fail; + + /* check initial predictor/scale */ + if (ch0_header.initial_ps != + (uint8_t)read_8bit(channel_1_start + 0x60, streamFile)) + goto fail; + + if (ch1_header.initial_ps != + (uint8_t)read_8bit(channel_2_start + 0x60, streamFile)) + goto fail; + + /* check type==0 and gain==0 */ + if (ch0_header.format || ch0_header.gain || + ch1_header.format || ch1_header.gain) + goto fail; + + /* check for agreement */ + if ( + ch0_header.sample_count != ch1_header.sample_count || + ch0_header.nibble_count != ch1_header.nibble_count || + ch0_header.sample_rate != ch1_header.sample_rate || + ch0_header.loop_flag != ch1_header.loop_flag || + ch0_header.loop_start_offset != ch1_header.loop_start_offset || + ch0_header.loop_end_offset != ch1_header.loop_end_offset + ) goto fail; + + if (ch0_header.loop_flag) { + off_t loop_off; + /* check loop predictor/scale */ + loop_off = ch0_header.loop_start_offset/16*8; + if (ch0_header.loop_ps != + (uint8_t)read_8bit(channel_1_start+0x60+loop_off,streamFile)) + goto fail; + if (ch1_header.loop_ps != + (uint8_t)read_8bit(channel_2_start+0x60+loop_off,streamFile)) + goto fail; + } + + /* build the VGMSTREAM */ + + vgmstream = allocate_vgmstream(2,ch0_header.loop_flag); + if (!vgmstream) goto fail; + + /* fill in the vital statistics */ + vgmstream->num_samples = ch0_header.sample_count; + vgmstream->sample_rate = ch0_header.sample_rate; + + vgmstream->loop_start_sample = dsp_nibbles_to_samples( + ch0_header.loop_start_offset); + vgmstream->loop_end_sample = dsp_nibbles_to_samples( + ch0_header.loop_end_offset)+1; + + vgmstream->coding_type = coding_NGC_DSP; + vgmstream->layout_type = layout_none; + vgmstream->meta_type = meta_DSP_DSPW; + + /* coeffs */ + { + int i; + for (i=0;i<16;i++) { + vgmstream->ch[0].adpcm_coef[i] = ch0_header.coef[i]; + vgmstream->ch[1].adpcm_coef[i] = ch1_header.coef[i]; + } + } + + /* initial history */ + /* always 0 that I've ever seen, but for completeness... */ + vgmstream->ch[0].adpcm_history1_16 = ch0_header.initial_hist1; + vgmstream->ch[0].adpcm_history2_16 = ch0_header.initial_hist2; + vgmstream->ch[1].adpcm_history1_16 = ch1_header.initial_hist1; + vgmstream->ch[1].adpcm_history2_16 = ch1_header.initial_hist2; + + /* open the file for reading */ + vgmstream->ch[0].streamfile = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); + if (!vgmstream->ch[0].streamfile) goto fail; + + vgmstream->ch[1].streamfile = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); + if (!vgmstream->ch[1].streamfile) goto fail; + + vgmstream->ch[0].channel_start_offset = + vgmstream->ch[0].offset=channel_1_start+0x60; + vgmstream->ch[1].channel_start_offset = + vgmstream->ch[1].offset=channel_2_start+0x60; + + return vgmstream; + +fail: + /* clean up anything we may have opened */ + if (vgmstream) close_vgmstream(vgmstream); + return NULL; +} \ No newline at end of file diff --git a/src/vgmstream.c b/src/vgmstream.c index 9a5130ba..a0a130cc 100644 --- a/src/vgmstream.c +++ b/src/vgmstream.c @@ -279,6 +279,7 @@ VGMSTREAM * (*init_vgmstream_fcns[])(STREAMFILE *streamFile) = { init_vgmstream_ps2_xau, init_vgmstream_gh3_bar, init_vgmstream_ffw, + init_vgmstream_dsp_dspw, }; #define INIT_VGMSTREAM_FCNS (sizeof(init_vgmstream_fcns)/sizeof(init_vgmstream_fcns[0])) @@ -2636,6 +2637,9 @@ void describe_vgmstream(VGMSTREAM * vgmstream, char * desc, int length) { break; case meta_FFW: snprintf(temp,TEMPSIZE,"Freedom Fighters BGM header"); + break; + case meta_DSP_DSPW: + snprintf(temp,TEMPSIZE,"DSPW dsp header"); break; default: snprintf(temp,TEMPSIZE,"THEY SHOULD HAVE SENT A POET"); diff --git a/src/vgmstream.h b/src/vgmstream.h index 62865699..f2060802 100644 --- a/src/vgmstream.h +++ b/src/vgmstream.h @@ -479,6 +479,7 @@ typedef enum { meta_PS2_XAU, // Spectral Force Chronicle meta_GH3_BAR, /* Guitar Hero III Mobile .bar */ meta_FFW, /* Freedom Fighters [NGC] */ + meta_DSP_DSPW, /* Sengoku Basara 3 [WII] */ } meta_t; typedef struct { diff --git a/winamp/in_vgmstream.c b/winamp/in_vgmstream.c index 548cecb7..160daf3e 100644 --- a/winamp/in_vgmstream.c +++ b/winamp/in_vgmstream.c @@ -126,6 +126,7 @@ char * extension_list[] = { "ddsp\0DDSP Audio File (*.DDSP)\0", "dmsg\0DMSG Audio File (*.DMSG)\0", "dsp\0DSP Audio File (*.DSP)\0", + "dspw\0DSPW Audio File (*.DSPW)\0", "dtk\0DTK Audio File (*.DTK)\0", "dvi\0DVI Audio File (*.DVI)\0", "dxh\0DXH Audio File (*.DXH)\0",