Add .adpcm WIIADPCM [Need for Speed: Hot Pursuit (Wii)]

This commit is contained in:
bnnm 2020-12-19 14:52:04 +01:00
parent 18bac02900
commit 69a78228f8
5 changed files with 73 additions and 38 deletions

View File

@ -1321,6 +1321,7 @@ static const meta_info meta_info_list[] = {
{meta_DSP_SQEX, "Square Enix DSP header"}, {meta_DSP_SQEX, "Square Enix DSP header"},
{meta_DSP_WIIVOICE, "Koei Tecmo WiiVoice header"}, {meta_DSP_WIIVOICE, "Koei Tecmo WiiVoice header"},
{meta_SBK, "Team17 SBK header"}, {meta_SBK, "Team17 SBK header"},
{meta_DSP_WIIADPCM, "Exient WIIADPCM header"},
}; };
void get_vgmstream_coding_description(VGMSTREAM* vgmstream, char* out, size_t out_size) { void get_vgmstream_coding_description(VGMSTREAM* vgmstream, char* out, size_t out_size) {

View File

@ -56,6 +56,7 @@ VGMSTREAM* init_vgmstream_dsp_ds2(STREAMFILE* sf);
VGMSTREAM* init_vgmstream_dsp_itl(STREAMFILE* sf); VGMSTREAM* init_vgmstream_dsp_itl(STREAMFILE* sf);
VGMSTREAM* init_vgmstream_dsp_sqex(STREAMFILE* sf); VGMSTREAM* init_vgmstream_dsp_sqex(STREAMFILE* sf);
VGMSTREAM* init_vgmstream_dsp_wiivoice(STREAMFILE* sf); VGMSTREAM* init_vgmstream_dsp_wiivoice(STREAMFILE* sf);
VGMSTREAM* init_vgmstream_dsp_wiiadpcm(STREAMFILE* sf);
VGMSTREAM * init_vgmstream_csmp(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_csmp(STREAMFILE *streamFile);

View File

@ -7,19 +7,19 @@
* then this is actually how they are laid out in the file, albeit big-endian */ * then this is actually how they are laid out in the file, albeit big-endian */
struct dsp_header { struct dsp_header {
uint32_t sample_count; /* 0x00 */ uint32_t sample_count; /* 0x00 */
uint32_t nibble_count; /* 0x04 */ uint32_t nibble_count; /* 0x04 (includes frame headers) */
uint32_t sample_rate; /* 0x08 */ uint32_t sample_rate; /* 0x08 (generally 32/48kz but games like Wario World set 32028hz to adjust for GC's rate) */
uint16_t loop_flag; /* 0x0c */ uint16_t loop_flag; /* 0x0c */
uint16_t format; /* 0x0e */ uint16_t format; /* 0x0e (always 0 for ADPCM) */
uint32_t loop_start_offset; /* 0x10 */ uint32_t loop_start_offset; /* 0x10 */
uint32_t loop_end_offset; /* 0x14 */ uint32_t loop_end_offset; /* 0x14 */
uint32_t ca; /* 0x18 */ uint32_t ca; /* 0x18 (always 0) */
int16_t coef[16]; /* 0x1c (really 8x2) */ int16_t coef[16]; /* 0x1c (eight pairs) */
uint16_t gain; /* 0x3c */ uint16_t gain; /* 0x3c (always 0 for ADPCM) */
uint16_t initial_ps; /* 0x3e */ uint16_t initial_ps; /* 0x3e (predictor/scale in frame header) */
int16_t initial_hist1; /* 0x40 */ int16_t initial_hist1; /* 0x40 */
int16_t initial_hist2; /* 0x42 */ int16_t initial_hist2; /* 0x42 */
uint16_t loop_ps; /* 0x44 */ uint16_t loop_ps; /* 0x44 (predictor/scale in loop frame header) */
int16_t loop_hist1; /* 0x46 */ int16_t loop_hist1; /* 0x46 */
int16_t loop_hist2; /* 0x48 */ int16_t loop_hist2; /* 0x48 */
int16_t channel_count; /* 0x4a (DSPADPCM.exe ~v2.7 extension) */ int16_t channel_count; /* 0x4a (DSPADPCM.exe ~v2.7 extension) */
@ -29,34 +29,35 @@ struct dsp_header {
/* read the above struct; returns nonzero on failure */ /* read the above struct; returns nonzero on failure */
static int read_dsp_header_endian(struct dsp_header *header, off_t offset, STREAMFILE* sf, int big_endian) { static int read_dsp_header_endian(struct dsp_header *header, off_t offset, STREAMFILE* sf, int big_endian) {
int32_t (*get_32bit)(const uint8_t *) = big_endian ? get_32bitBE : get_32bitLE; uint32_t (*get_u32)(const uint8_t*) = big_endian ? get_u32be : get_u32le;
int16_t (*get_16bit)(const uint8_t *) = big_endian ? get_16bitBE : get_16bitLE; uint16_t (*get_u16)(const uint8_t*) = big_endian ? get_u16be : get_u16le;
int16_t (*get_s16)(const uint8_t*) = big_endian ? get_s16be : get_s16le;
int i; int i;
uint8_t buf[0x4e]; uint8_t buf[0x60];
if (offset > get_streamfile_size(sf)) if (offset > get_streamfile_size(sf))
return 1; return 1;
if (read_streamfile(buf, offset, 0x4e, sf) != 0x4e) if (read_streamfile(buf, offset, 0x60, sf) != 0x60)
return 1; return 1;
header->sample_count = get_32bit(buf+0x00); header->sample_count = get_u32(buf+0x00);
header->nibble_count = get_32bit(buf+0x04); header->nibble_count = get_u32(buf+0x04);
header->sample_rate = get_32bit(buf+0x08); header->sample_rate = get_u32(buf+0x08);
header->loop_flag = get_16bit(buf+0x0c); header->loop_flag = get_u16(buf+0x0c);
header->format = get_16bit(buf+0x0e); header->format = get_u16(buf+0x0e);
header->loop_start_offset = get_32bit(buf+0x10); header->loop_start_offset = get_u32(buf+0x10);
header->loop_end_offset = get_32bit(buf+0x14); header->loop_end_offset = get_u32(buf+0x14);
header->ca = get_32bit(buf+0x18); header->ca = get_u32(buf+0x18);
for (i=0; i < 16; i++) for (i=0; i < 16; i++)
header->coef[i] = get_16bit(buf+0x1c+i*0x02); header->coef[i] = get_s16(buf+0x1c+i*0x02);
header->gain = get_16bit(buf+0x3c); header->gain = get_u16(buf+0x3c);
header->initial_ps = get_16bit(buf+0x3e); header->initial_ps = get_u16(buf+0x3e);
header->initial_hist1 = get_16bit(buf+0x40); header->initial_hist1 = get_s16(buf+0x40);
header->initial_hist2 = get_16bit(buf+0x42); header->initial_hist2 = get_s16(buf+0x42);
header->loop_ps = get_16bit(buf+0x44); header->loop_ps = get_u16(buf+0x44);
header->loop_hist1 = get_16bit(buf+0x46); header->loop_hist1 = get_s16(buf+0x46);
header->loop_hist2 = get_16bit(buf+0x48); header->loop_hist2 = get_s16(buf+0x48);
header->channel_count = get_16bit(buf+0x4a); header->channel_count = get_s16(buf+0x4a);
header->block_size = get_16bit(buf+0x4c); header->block_size = get_s16(buf+0x4c);
return 0; return 0;
} }
static int read_dsp_header(struct dsp_header *header, off_t offset, STREAMFILE* file) { static int read_dsp_header(struct dsp_header *header, off_t offset, STREAMFILE* file) {
@ -156,7 +157,7 @@ static VGMSTREAM* init_vgmstream_dsp_common(STREAMFILE* sf, dsp_meta *dspm) {
for (i = 0; i < channels; i++) { for (i = 0; i < channels; i++) {
off_t channel_offset = dspm->start_offset + i*dspm->interleave; off_t channel_offset = dspm->start_offset + i*dspm->interleave;
if (ch_header[i].initial_ps != (uint8_t)read_8bit(channel_offset, sf)) if (ch_header[i].initial_ps != read_u8(channel_offset, sf))
goto fail; goto fail;
} }
} }
@ -169,15 +170,17 @@ static VGMSTREAM* init_vgmstream_dsp_common(STREAMFILE* sf, dsp_meta *dspm) {
for (i = 0; i < channels; i++) { for (i = 0; i < channels; i++) {
off_t loop_offset = ch_header[i].loop_start_offset; off_t loop_offset = ch_header[i].loop_start_offset;
loop_offset = loop_offset / 0x8 * 0x8; /* loop points to a nibble, but we need closest frame header */
if (dspm->interleave) { if (dspm->interleave) {
loop_offset = loop_offset / 16 * 8;
loop_offset = (loop_offset / dspm->interleave * dspm->interleave * channels) + (loop_offset % dspm->interleave); loop_offset = (loop_offset / dspm->interleave * dspm->interleave * channels) + (loop_offset % dspm->interleave);
} }
if (ch_header[i].loop_ps != (uint8_t)read_8bit(dspm->start_offset + i*dspm->interleave + loop_offset,sf)) if (ch_header[i].loop_ps != read_u8(dspm->start_offset + i*dspm->interleave + loop_offset,sf)) {
goto fail; goto fail;
} }
} }
}
/* all done, must be DSP */ /* all done, must be DSP */
@ -199,7 +202,7 @@ static VGMSTREAM* init_vgmstream_dsp_common(STREAMFILE* sf, dsp_meta *dspm) {
vgmstream->sample_rate = ch_header[0].sample_rate; vgmstream->sample_rate = ch_header[0].sample_rate;
vgmstream->num_samples = ch_header[0].sample_count; vgmstream->num_samples = ch_header[0].sample_count;
vgmstream->loop_start_sample = dsp_nibbles_to_samples(ch_header[0].loop_start_offset); vgmstream->loop_start_sample = dsp_nibbles_to_samples(ch_header[0].loop_start_offset);
vgmstream->loop_end_sample = dsp_nibbles_to_samples(ch_header[0].loop_end_offset)+1; vgmstream->loop_end_sample = dsp_nibbles_to_samples(ch_header[0].loop_end_offset) + 1;
vgmstream->meta_type = dspm->meta_type; vgmstream->meta_type = dspm->meta_type;
vgmstream->coding_type = coding_NGC_DSP; vgmstream->coding_type = coding_NGC_DSP;
@ -224,7 +227,7 @@ static VGMSTREAM* init_vgmstream_dsp_common(STREAMFILE* sf, dsp_meta *dspm) {
} }
} }
/* don't know why, but it does happen*/ /* don't know why, but it does happen */
if (dspm->fix_looping && vgmstream->loop_end_sample > vgmstream->num_samples) if (dspm->fix_looping && vgmstream->loop_end_sample > vgmstream->num_samples)
vgmstream->loop_end_sample = vgmstream->num_samples; vgmstream->loop_end_sample = vgmstream->num_samples;
@ -235,7 +238,7 @@ static VGMSTREAM* init_vgmstream_dsp_common(STREAMFILE* sf, dsp_meta *dspm) {
} }
if (!vgmstream_open_stream(vgmstream,sf,dspm->start_offset)) if (!vgmstream_open_stream(vgmstream, sf, dspm->start_offset))
goto fail; goto fail;
return vgmstream; return vgmstream;
@ -1094,8 +1097,6 @@ VGMSTREAM* init_vgmstream_dsp_sps_n1(STREAMFILE* sf) {
dspm.start_offset = dspm.header_offset + dspm.header_spacing*dspm.channel_count; dspm.start_offset = dspm.header_offset + dspm.header_spacing*dspm.channel_count;
dspm.interleave = 0; dspm.interleave = 0;
dspm.fix_loop_start = 1;
dspm.meta_type = meta_DSP_VAG; dspm.meta_type = meta_DSP_VAG;
return init_vgmstream_dsp_common(sf, &dspm); return init_vgmstream_dsp_common(sf, &dspm);
fail: fail:
@ -1306,3 +1307,33 @@ VGMSTREAM* init_vgmstream_dsp_wiivoice(STREAMFILE* sf) {
fail: fail:
return NULL; return NULL;
} }
/* WIIADPCM - Exient wrapper [Need for Speed: Hot Pursuit (Wii)] */
VGMSTREAM* init_vgmstream_dsp_wiiadpcm(STREAMFILE* sf) {
dsp_meta dspm = {0};
/* checks */
if (!check_extensions(sf, "adpcm"))
goto fail;
if (!is_id32be(0x00,sf, "WIIA") && !is_id32be(0x00,sf, "DPCM"))
goto fail;
dspm.interleave = read_u32be(0x08,sf); /* interleave offset */
if (dspm.interleave) {
dspm.interleave -= 0x10;
}
/* 0x0c: 0 when RAM (2 DSP headers), interleave size when stream (2 WIIADPCM headers) */
dspm.channel_count = (dspm.interleave ? 2 : 1);
dspm.max_channels = 2;
dspm.header_offset = 0x10;
dspm.header_spacing = dspm.interleave;
dspm.start_offset = dspm.header_offset + 0x60;
dspm.meta_type = meta_DSP_WIIADPCM;
return init_vgmstream_dsp_common(sf, &dspm);
fail:
return NULL;
}

View File

@ -513,6 +513,7 @@ VGMSTREAM* (*init_vgmstream_functions[])(STREAMFILE* sf) = {
init_vgmstream_cpk, init_vgmstream_cpk,
init_vgmstream_opus_nsopus, init_vgmstream_opus_nsopus,
init_vgmstream_sbk, init_vgmstream_sbk,
init_vgmstream_dsp_wiiadpcm,
/* lowest priority metas (should go after all metas, and TXTH should go before raw formats) */ /* lowest priority metas (should go after all metas, and TXTH should go before raw formats) */
init_vgmstream_txth, /* proper parsers should supersede TXTH, once added */ init_vgmstream_txth, /* proper parsers should supersede TXTH, once added */

View File

@ -742,6 +742,7 @@ typedef enum {
meta_DSP_SQEX, meta_DSP_SQEX,
meta_DSP_WIIVOICE, meta_DSP_WIIVOICE,
meta_SBK, meta_SBK,
meta_DSP_WIIADPCM,
} meta_t; } meta_t;
/* standard WAVEFORMATEXTENSIBLE speaker positions */ /* standard WAVEFORMATEXTENSIBLE speaker positions */