Parse "wsmp" loop chunk, rarely found in Xbox games [Dynasty Warriors 5]

This commit is contained in:
bnnm 2018-02-23 23:30:55 +01:00
parent 3579858ef4
commit aca3dc5f2d
3 changed files with 61 additions and 33 deletions

View File

@ -700,6 +700,7 @@ static const meta_info meta_info_list[] = {
{meta_PS2_PSH, "Dawn of Mana - Seiken Densetsu 4 PSH Header"}, {meta_PS2_PSH, "Dawn of Mana - Seiken Densetsu 4 PSH Header"},
{meta_RIFF_WAVE_labl, "RIFF WAVE header with loop markers"}, {meta_RIFF_WAVE_labl, "RIFF WAVE header with loop markers"},
{meta_RIFF_WAVE_smpl, "RIFF WAVE header with sample looping info"}, {meta_RIFF_WAVE_smpl, "RIFF WAVE header with sample looping info"},
{meta_RIFF_WAVE_wsmp, "RIFF WAVE header with wsmp looping info"},
{meta_RIFX_WAVE, "RIFX WAVE header"}, {meta_RIFX_WAVE, "RIFX WAVE header"},
{meta_RIFX_WAVE_smpl, "RIFX WAVE header with sample looping info"}, {meta_RIFX_WAVE_smpl, "RIFX WAVE header with sample looping info"},
{meta_XNB, "Microsoft XNA Game Studio 4.0 header"}, {meta_XNB, "Microsoft XNA Game Studio 4.0 header"},

View File

@ -232,10 +232,9 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) {
int fact_sample_skip = 0; int fact_sample_skip = 0;
int loop_flag = 0; int loop_flag = 0;
long loop_start_ms = -1; int loop_start_sample = 0, loop_end_sample = 0;
long loop_end_ms = -1; long loop_start_ms = -1, loop_end_ms = -1;
off_t loop_start_offset = -1; off_t loop_start_offset = -1, loop_end_offset = -1;
off_t loop_end_offset = -1;
int FormatChunkFound = 0, DataChunkFound = 0, JunkFound = 0; int FormatChunkFound = 0, DataChunkFound = 0, JunkFound = 0;
@ -296,12 +295,11 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) {
if (fmt.codec == 0x6771 && chunk_type == 0x64617461) /* Liar-soft again */ if (fmt.codec == 0x6771 && chunk_type == 0x64617461) /* Liar-soft again */
chunk_size += (chunk_size%2) ? 0x01 : 0x00; chunk_size += (chunk_size%2) ? 0x01 : 0x00;
if (current_chunk+8+chunk_size > file_size) goto fail; if (current_chunk+0x08+chunk_size > file_size) goto fail;
switch(chunk_type) { switch(chunk_type) {
case 0x666d7420: /* "fmt " */ case 0x666d7420: /* "fmt " */
/* only one per file */ if (FormatChunkFound) goto fail; /* only one per file */
if (FormatChunkFound) goto fail;
FormatChunkFound = 1; FormatChunkFound = 1;
if (-1 == read_fmt(0, /* big endian == false*/ if (-1 == read_fmt(0, /* big endian == false*/
@ -311,20 +309,20 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) {
sns, sns,
mwv)) mwv))
goto fail; goto fail;
break; break;
case 0x64617461: /* data */
/* at most one per file */ case 0x64617461: /* "data" */
if (DataChunkFound) goto fail; if (DataChunkFound) goto fail; /* only one per file */
DataChunkFound = 1; DataChunkFound = 1;
start_offset = current_chunk + 8; start_offset = current_chunk + 0x08;
data_size = chunk_size; data_size = chunk_size;
break; break;
case 0x4C495354: /* LIST */
case 0x4C495354: /* "LIST" */
/* what lurks within?? */ /* what lurks within?? */
switch (read_32bitBE(current_chunk + 8, streamFile)) { switch (read_32bitBE(current_chunk+0x08, streamFile)) {
case 0x6164746C: /* adtl */ case 0x6164746C: /* "adtl" */
/* yay, atdl is its own little world */ /* yay, atdl is its own little world */
parse_adtl(current_chunk + 8, chunk_size, parse_adtl(current_chunk + 8, chunk_size,
streamFile, streamFile,
@ -334,28 +332,40 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) {
break; break;
} }
break; break;
case 0x736D706C: /* smpl */
/* check loop count and loop info */ case 0x736D706C: /* "smpl" (RIFFMIDISample + MIDILoop chunk) */
if (read_32bitLE(current_chunk+0x24, streamFile)==1) { /* check loop count/loop info (most common) *///todo double check values
if (read_32bitLE(current_chunk+0x2c+4, streamFile)==0) { /* 0x00: manufacturer id, 0x04: product id, 0x08: sample period, 0x0c: unity node,
* 0x10: pitch fraction, 0x14: SMPTE format, 0x18: SMPTE offset, 0x1c: loop count, 0x20: sampler data */
if (read_32bitLE(current_chunk+0x08+0x1c, streamFile)==1) {
/* 0x24: cue point id, 0x28: type (0=forward, 1=alternating, 2=backward)
* 0x2c: start, 0x30: end, 0x34: fraction, 0x38: play count */
if (read_32bitLE(current_chunk+0x08+0x28, streamFile)==0) {
loop_flag = 1; loop_flag = 1;
loop_start_offset = read_32bitLE(current_chunk+0x2c+8, streamFile); loop_start_offset = read_32bitLE(current_chunk+0x08+0x2c, streamFile);
loop_end_offset = read_32bitLE(current_chunk+0x2c+0xc,streamFile); loop_end_offset = read_32bitLE(current_chunk+0x08+0x30, streamFile);
} }
} }
break; break;
case 0x70666c74: /* pflt */
if (!mwv) break; /* ignore if not in an mwv */
mwv_pflt_offset = current_chunk; /* predictor filters */ case 0x77736D70: /* "wsmp" (RIFFDLSSample + DLSLoop chunk) */
/* check loop count/info (found in some Xbox games: Halo (non-looping), Dynasty Warriors 3, Crimson Sea) */
/* 0x00: size, 0x04: unity note, 0x06: fine tune, 0x08: gain, 0x10: loop count */
if (chunk_size >= 0x24
&& read_32bitLE(current_chunk+0x08+0x00, streamFile) == 0x14
&& read_32bitLE(current_chunk+0x08+0x10, streamFile) > 0
&& read_32bitLE(current_chunk+0x08+0x14, streamFile) == 0x10) {
/* 0x14: size, 0x18: loop type (0=forward, 1=release), 0x1c: loop start, 0x20: loop length */
if (read_32bitLE(current_chunk+0x08+0x18, streamFile)==0) {
loop_flag = 1;
loop_start_sample = read_32bitLE(current_chunk+0x08+0x1c, streamFile);
loop_end_sample = read_32bitLE(current_chunk+0x08+0x20, streamFile);
loop_end_sample += loop_start_sample;
}
}
break; break;
case 0x6374726c: /* ctrl */
if (!mwv) break;
loop_flag = read_32bitLE(current_chunk+0x08, streamFile); case 0x66616374: /* "fact" */
mwv_ctrl_offset = current_chunk;
break;
case 0x66616374: /* fact */
if (chunk_size == 0x04) { /* standard, usually found with ADPCM */ if (chunk_size == 0x04) { /* standard, usually found with ADPCM */
fact_sample_count = read_32bitLE(current_chunk+0x08, streamFile); fact_sample_count = read_32bitLE(current_chunk+0x08, streamFile);
} else if (sns && chunk_size == 0x10) { } else if (sns && chunk_size == 0x10) {
@ -367,11 +377,22 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) {
fact_sample_count = read_32bitLE(current_chunk+0x08, streamFile); fact_sample_count = read_32bitLE(current_chunk+0x08, streamFile);
fact_sample_skip = read_32bitLE(current_chunk+0x10, streamFile); fact_sample_skip = read_32bitLE(current_chunk+0x10, streamFile);
} }
break; break;
case 0x4A554E4B: /* JUNK */
case 0x70666c74: /* "pflt" (.mwv extension) */
if (!mwv) break; /* ignore if not in an mwv */
mwv_pflt_offset = current_chunk; /* predictor filters */
break;
case 0x6374726c: /* "ctrl" (.mwv extension) */
if (!mwv) break;
loop_flag = read_32bitLE(current_chunk+0x08, streamFile);
mwv_ctrl_offset = current_chunk;
break;
case 0x4A554E4B: /* "JUNK" */
JunkFound = 1; JunkFound = 1;
break; break;
default: default:
/* ignorance is bliss */ /* ignorance is bliss */
break; break;
@ -383,7 +404,7 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) {
if (!FormatChunkFound || !DataChunkFound) goto fail; if (!FormatChunkFound || !DataChunkFound) goto fail;
//todo improve detection using fmt sizes/values as Wwise's don't match the RIFF standard //todo improve detection using fmt sizes/values as Wwise's don't match the RIFF standard
/* JUNK is an optional Wwise chunk, and Wwise hijacks the MSADPCM/MS_IMA/XBOX IMA ids (how nice). /* JUNK is an optional Wwise chunk, and Wwise hijacks the MSADPCM/MS_IMA/XBOX IMA ids (how nice).
* To ensure their stuff is parsed in wwise.c we reject their JUNK, which they put almost always. * To ensure their stuff is parsed in wwise.c we reject their JUNK, which they put almost always.
* As JUNK is legal (if unusual) we only reject those codecs. * As JUNK is legal (if unusual) we only reject those codecs.
@ -579,6 +600,11 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) {
vgmstream->loop_end_sample = loop_end_offset; vgmstream->loop_end_sample = loop_end_offset;
vgmstream->meta_type = meta_RIFF_WAVE_smpl; vgmstream->meta_type = meta_RIFF_WAVE_smpl;
} }
else if (loop_start_sample >= 0) {
vgmstream->loop_start_sample = loop_start_sample;
vgmstream->loop_end_sample = loop_end_sample;
vgmstream->meta_type = meta_RIFF_WAVE_wsmp;
}
else if (mwv && mwv_ctrl_offset != -1) { else if (mwv && mwv_ctrl_offset != -1) {
vgmstream->loop_start_sample = read_32bitLE(mwv_ctrl_offset+12, streamFile); vgmstream->loop_start_sample = read_32bitLE(mwv_ctrl_offset+12, streamFile);
vgmstream->loop_end_sample = vgmstream->num_samples; vgmstream->loop_end_sample = vgmstream->num_samples;

View File

@ -481,6 +481,7 @@ typedef enum {
meta_RIFF_WAVE_POS, /* .wav + .pos for looping (Ys Complete PC) */ meta_RIFF_WAVE_POS, /* .wav + .pos for looping (Ys Complete PC) */
meta_RIFF_WAVE_labl, /* RIFF w/ loop Markers in LIST-adtl-labl */ meta_RIFF_WAVE_labl, /* RIFF w/ loop Markers in LIST-adtl-labl */
meta_RIFF_WAVE_smpl, /* RIFF w/ loop data in smpl chunk */ meta_RIFF_WAVE_smpl, /* RIFF w/ loop data in smpl chunk */
meta_RIFF_WAVE_wsmp, /* RIFF w/ loop data in wsmp chunk */
meta_RIFF_WAVE_MWV, /* .mwv RIFF w/ loop data in ctrl chunk pflt */ meta_RIFF_WAVE_MWV, /* .mwv RIFF w/ loop data in ctrl chunk pflt */
meta_RIFF_WAVE_SNS, /* .sns RIFF */ meta_RIFF_WAVE_SNS, /* .sns RIFF */
meta_RIFX_WAVE, /* RIFX, for big-endian WAVs */ meta_RIFX_WAVE, /* RIFX, for big-endian WAVs */