mirror of
https://github.com/vgmstream/vgmstream.git
synced 2025-02-08 15:28:17 +01:00
Fix old Wwise .xma, loop end +1, truncated DSP [Too Human (X360)]
This commit is contained in:
parent
63365162df
commit
35b708df94
101
src/meta/wwise.c
101
src/meta/wwise.c
@ -47,20 +47,22 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) {
|
|||||||
int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL;
|
int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL;
|
||||||
int16_t (*read_16bit)(off_t,STREAMFILE*) = NULL;
|
int16_t (*read_16bit)(off_t,STREAMFILE*) = NULL;
|
||||||
|
|
||||||
/* basic checks */
|
/* checks */
|
||||||
/* .wem (Wwise Encoded Media) is "newer Wwise", used after the 2011.2 SDK (~july)
|
/* .wem: newer "Wwise Encoded Media" used after the 2011.2 SDK (~july 2011)
|
||||||
* .wav (ex. Shadowrun X360) and .ogg (ex. KOF XII X360), .xma (ex. Tron Evolution X360) are used in older Wwise */
|
* .wav: older ADPCM files [Punch Out!! (Wii)]
|
||||||
if (!check_extensions(streamFile,"wem,wav,lwav,ogg,logg,xma")) goto fail;
|
* .xma: older XMA files [Too Human (X360), Tron Evolution (X360)]
|
||||||
|
* .ogg: older Vorbis files [The King of Fighters XII (X360)] */
|
||||||
if ((read_32bitBE(0x00,streamFile) != 0x52494646) && /* "RIFF" (LE) */
|
if (!check_extensions(streamFile,"wem,wav,lwav,ogg,logg,xma"))
|
||||||
(read_32bitBE(0x00,streamFile) != 0x52494658)) /* "RIFX" (BE) */
|
|
||||||
goto fail;
|
|
||||||
if ((read_32bitBE(0x08,streamFile) != 0x57415645) && /* "WAVE" */
|
|
||||||
(read_32bitBE(0x08,streamFile) != 0x58574D41)) /* "XWMA" */
|
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
||||||
|
if (read_32bitBE(0x00,streamFile) != 0x52494646 && /* "RIFF" (LE) */
|
||||||
|
read_32bitBE(0x00,streamFile) != 0x52494658) /* "RIFX" (BE) */
|
||||||
|
goto fail;
|
||||||
|
if (read_32bitBE(0x08,streamFile) != 0x57415645 && /* "WAVE" */
|
||||||
|
read_32bitBE(0x08,streamFile) != 0x58574D41) /* "XWMA" */
|
||||||
|
goto fail;
|
||||||
|
|
||||||
ww.big_endian = read_32bitBE(0x00,streamFile) == 0x52494658;/* RIFX */
|
ww.big_endian = read_32bitBE(0x00,streamFile) == 0x52494658; /* RIFX */
|
||||||
if (ww.big_endian) { /* Wwise honors machine's endianness (PC=RIFF, X360=RIFX --unlike XMA) */
|
if (ww.big_endian) { /* Wwise honors machine's endianness (PC=RIFF, X360=RIFX --unlike XMA) */
|
||||||
read_32bit = read_32bitBE;
|
read_32bit = read_32bitBE;
|
||||||
read_16bit = read_16bitBE;
|
read_16bit = read_16bitBE;
|
||||||
@ -94,10 +96,9 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) {
|
|||||||
if (find_chunk(streamFile, 0x66616374,first_offset,0, &fact_offset,&fact_size, 0, 0)) { /* "fact" */
|
if (find_chunk(streamFile, 0x66616374,first_offset,0, &fact_offset,&fact_size, 0, 0)) { /* "fact" */
|
||||||
if (fact_size == 0x10 && read_32bitBE(fact_offset+0x04, streamFile) == 0x4C794E20) /* "LyN " */
|
if (fact_size == 0x10 && read_32bitBE(fact_offset+0x04, streamFile) == 0x4C794E20) /* "LyN " */
|
||||||
goto fail; /* parsed elsewhere */
|
goto fail; /* parsed elsewhere */
|
||||||
|
}
|
||||||
/* Wwise doesn't use "fact", though */
|
/* Wwise doesn't use "fact", though */
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* parse format (roughly spec-compliant but some massaging is needed) */
|
/* parse format (roughly spec-compliant but some massaging is needed) */
|
||||||
{
|
{
|
||||||
@ -105,19 +106,26 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) {
|
|||||||
size_t loop_size;
|
size_t loop_size;
|
||||||
|
|
||||||
/* find basic chunks */
|
/* find basic chunks */
|
||||||
if (!find_chunk(streamFile, 0x666d7420,first_offset,0, &ww.fmt_offset,&ww.fmt_size, ww.big_endian, 0)) goto fail; /*"fmt "*/
|
if (read_32bitBE(0x0c, streamFile) == 0x584D4132) { /* "XMA2" with no "fmt" [Too Human (X360)] */
|
||||||
if (!find_chunk(streamFile, 0x64617461,first_offset,0, &ww.data_offset,&ww.data_size, ww.big_endian, 0)) goto fail; /*"data"*/
|
ww.format = 0x0165; /* signal for below */
|
||||||
|
}
|
||||||
/* base fmt */
|
else {
|
||||||
if (ww.fmt_size < 0x12) goto fail;
|
if (!find_chunk(streamFile, 0x666d7420,first_offset,0, &ww.fmt_offset,&ww.fmt_size, ww.big_endian, 0)) /* "fmt " */
|
||||||
|
goto fail;
|
||||||
|
if (ww.fmt_size < 0x12)
|
||||||
|
goto fail;
|
||||||
ww.format = (uint16_t)read_16bit(ww.fmt_offset+0x00,streamFile);
|
ww.format = (uint16_t)read_16bit(ww.fmt_offset+0x00,streamFile);
|
||||||
|
}
|
||||||
|
|
||||||
if (ww.format == 0x0165) { /* pseudo-XMA2WAVEFORMAT (always "fmt"+"XMA2", unlike .xma that may only have "XMA2") */
|
|
||||||
if (!find_chunk(streamFile, 0x584D4132,first_offset,0, &ww.chunk_offset,NULL, ww.big_endian, 0))
|
if (ww.format == 0x0165) {
|
||||||
|
/* pseudo-XMA2WAVEFORMAT ("fmt"+"XMA2" or just "XMA2) */
|
||||||
|
if (!find_chunk(streamFile, 0x584D4132,first_offset,0, &ww.chunk_offset,NULL, ww.big_endian, 0)) /* "XMA2" */
|
||||||
goto fail;
|
goto fail;
|
||||||
xma2_parse_xma2_chunk(streamFile, ww.chunk_offset,&ww.channels,&ww.sample_rate, &ww.loop_flag, &ww.num_samples, &ww.loop_start_sample, &ww.loop_end_sample);
|
xma2_parse_xma2_chunk(streamFile, ww.chunk_offset,&ww.channels,&ww.sample_rate, &ww.loop_flag, &ww.num_samples, &ww.loop_start_sample, &ww.loop_end_sample);
|
||||||
}
|
}
|
||||||
else { /* pseudo-WAVEFORMATEX */
|
else {
|
||||||
|
/* pseudo-WAVEFORMATEX */
|
||||||
ww.channels = read_16bit(ww.fmt_offset+0x02,streamFile);
|
ww.channels = read_16bit(ww.fmt_offset+0x02,streamFile);
|
||||||
ww.sample_rate = read_32bit(ww.fmt_offset+0x04,streamFile);
|
ww.sample_rate = read_32bit(ww.fmt_offset+0x04,streamFile);
|
||||||
ww.average_bps = read_32bit(ww.fmt_offset+0x08,streamFile);/* bytes per sec */
|
ww.average_bps = read_32bit(ww.fmt_offset+0x08,streamFile);/* bytes per sec */
|
||||||
@ -128,7 +136,7 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) {
|
|||||||
if (ww.extra_size >= 0x06) { /* always present (actual RIFFs only have it in WAVEFORMATEXTENSIBLE) */
|
if (ww.extra_size >= 0x06) { /* always present (actual RIFFs only have it in WAVEFORMATEXTENSIBLE) */
|
||||||
/* mostly WAVEFORMATEXTENSIBLE's bitmask (see AkSpeakerConfig.h) */
|
/* mostly WAVEFORMATEXTENSIBLE's bitmask (see AkSpeakerConfig.h) */
|
||||||
ww.channel_layout = read_32bit(ww.fmt_offset+0x14,streamFile);
|
ww.channel_layout = read_32bit(ww.fmt_offset+0x14,streamFile);
|
||||||
/* latest games have a pseudo-format instead to handle more cases:
|
/* later games (+2018?) have a pseudo-format instead to handle more cases:
|
||||||
* - 8b: uNumChannels
|
* - 8b: uNumChannels
|
||||||
* - 4b: eConfigType (0=none, 1=standard, 2=ambisonic)
|
* - 4b: eConfigType (0=none, 1=standard, 2=ambisonic)
|
||||||
* - 19b: uChannelMask */
|
* - 19b: uChannelMask */
|
||||||
@ -138,40 +146,42 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* find loop info */
|
/* find loop info ("XMA2" chunks already read them) */
|
||||||
if (ww.format == 0x0166) { /* XMA2WAVEFORMATEX */
|
if (ww.format == 0x0166) { /* XMA2WAVEFORMATEX in fmt */
|
||||||
ww.chunk_offset = ww.fmt_offset;
|
ww.chunk_offset = ww.fmt_offset;
|
||||||
xma2_parse_fmt_chunk_extra(streamFile, ww.chunk_offset, &ww.loop_flag, &ww.num_samples, &ww.loop_start_sample, &ww.loop_end_sample, ww.big_endian);
|
xma2_parse_fmt_chunk_extra(streamFile, ww.chunk_offset, &ww.loop_flag, &ww.num_samples, &ww.loop_start_sample, &ww.loop_end_sample, ww.big_endian);
|
||||||
}
|
}
|
||||||
else if (find_chunk(streamFile, 0x736D706C,first_offset,0, &loop_offset,&loop_size, ww.big_endian, 0)) { /*"smpl", common */
|
else if (find_chunk(streamFile, 0x736D706C,first_offset,0, &loop_offset,&loop_size, ww.big_endian, 0)) { /* "smpl", common */
|
||||||
if (loop_size >= 0x34
|
if (loop_size >= 0x34
|
||||||
&& read_32bit(loop_offset+0x1c, streamFile)==1 /*loop count*/
|
&& read_32bit(loop_offset+0x1c, streamFile)==1 /* loop count */
|
||||||
&& read_32bit(loop_offset+0x24+4, streamFile)==0) {
|
&& read_32bit(loop_offset+0x24+4, streamFile)==0) {
|
||||||
ww.loop_flag = 1;
|
ww.loop_flag = 1;
|
||||||
ww.loop_start_sample = read_32bit(loop_offset+0x24+0x8, streamFile);
|
ww.loop_start_sample = read_32bit(loop_offset+0x24+0x8, streamFile);
|
||||||
ww.loop_end_sample = read_32bit(loop_offset+0x24+0xc,streamFile);
|
ww.loop_end_sample = read_32bit(loop_offset+0x24+0xc, streamFile) + 1; /* like standard RIFF */
|
||||||
//todo fix repeat looping
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//else if (find_chunk(streamFile, 0x4C495354,first_offset,0, &loop_offset,&loop_size, ww.big_endian, 0)) { /*"LIST", common */
|
//else if (find_chunk(streamFile, 0x4C495354,first_offset,0, &loop_offset,&loop_size, ww.big_endian, 0)) { /*"LIST", common */
|
||||||
// /* usually contains "cue"s with sample positions for events (ex. Platinum Games) but no real looping info */
|
// /* usually contains "cue"s with sample positions for events (ex. Platinum Games) but no real looping info */
|
||||||
//}
|
//}
|
||||||
|
|
||||||
/* other Wwise specific: */
|
/* other Wwise specific chunks:
|
||||||
//"JUNK": optional padding for aligment (0-size JUNK exists too)
|
* "JUNK": optional padding for aligment (0-size JUNK exists too)
|
||||||
//"akd ": seem to store extra info for Wwise editor (wave peaks/loudness/HDR envelope?)
|
* "akd ": seem to store extra info for Wwise editor (wave peaks/loudness/HDR envelope?)
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (!find_chunk(streamFile, 0x64617461,first_offset,0, &ww.data_offset,&ww.data_size, ww.big_endian, 0)) /* "data" */
|
||||||
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* format to codec */
|
/* format to codec */
|
||||||
switch(ww.format) {
|
switch(ww.format) {
|
||||||
case 0x0001: ww.codec = PCM; break; /* older Wwise */
|
case 0x0001: ww.codec = PCM; break; /* older Wwise */
|
||||||
case 0x0002: ww.codec = IMA; break; /* newer Wwise (conflicts with MSADPCM, probably means "platform's ADPCM") */
|
case 0x0002: ww.codec = IMA; break; /* newer Wwise (conflicts with MSADPCM, probably means "platform's ADPCM") */
|
||||||
//case 0x0011: ww.codec = IMA; break; /* older Wwise (used?) */
|
case 0x0069: ww.codec = IMA; break; /* older Wwise [Spiderman Web of Shadows (X360), LotR Conquest (PC)] */
|
||||||
case 0x0069: ww.codec = IMA; break; /* older Wwise (Spiderman Web of Shadows X360, LotR Conquest PC) */
|
|
||||||
case 0x0161: ww.codec = XWMA; break; /* WMAv2 */
|
case 0x0161: ww.codec = XWMA; break; /* WMAv2 */
|
||||||
case 0x0162: ww.codec = XWMA; break; /* WMAPro */
|
case 0x0162: ww.codec = XWMA; break; /* WMAPro */
|
||||||
case 0x0165: ww.codec = XMA2; break; /* always with the "XMA2" chunk, Wwise doesn't use XMA1 */
|
case 0x0165: ww.codec = XMA2; break; /* XMA2-chunk XMA (Wwise doesn't use XMA1) */
|
||||||
case 0x0166: ww.codec = XMA2; break;
|
case 0x0166: ww.codec = XMA2; break; /* fmt-chunk XMA */
|
||||||
case 0xAAC0: ww.codec = AAC; break;
|
case 0xAAC0: ww.codec = AAC; break;
|
||||||
case 0xFFF0: ww.codec = DSP; break;
|
case 0xFFF0: ww.codec = DSP; break;
|
||||||
case 0xFFFB: ww.codec = HEVAG; break;
|
case 0xFFFB: ww.codec = HEVAG; break;
|
||||||
@ -213,7 +223,7 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) {
|
|||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ww.codec == PCM || ww.codec == IMA || ww.codec == VORBIS || ww.codec == XMA2 || ww.codec == OPUSNX || ww.codec == OPUS) {
|
if (ww.codec == PCM || ww.codec == IMA || ww.codec == DSP || ww.codec == VORBIS || ww.codec == XMA2 || ww.codec == OPUSNX || ww.codec == OPUS) {
|
||||||
ww.truncated = 1; /* only seen those, probably all exist */
|
ww.truncated = 1; /* only seen those, probably all exist */
|
||||||
} else {
|
} else {
|
||||||
VGM_LOG("WWISE: wrong size, maybe truncated\n");
|
VGM_LOG("WWISE: wrong size, maybe truncated\n");
|
||||||
@ -264,7 +274,6 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) {
|
|||||||
vgmstream->interleave_block_size = ww.block_align / ww.channels;
|
vgmstream->interleave_block_size = ww.block_align / ww.channels;
|
||||||
vgmstream->codec_endian = ww.big_endian;
|
vgmstream->codec_endian = ww.big_endian;
|
||||||
|
|
||||||
/* enough to get real samples */
|
|
||||||
if (ww.truncated) {
|
if (ww.truncated) {
|
||||||
ww.data_size = ww.file_size - ww.data_offset;
|
ww.data_size = ww.file_size - ww.data_offset;
|
||||||
}
|
}
|
||||||
@ -286,12 +295,12 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) {
|
|||||||
if (ww.block_align != 0 || ww.bits_per_sample != 0) goto fail; /* always 0 for Worbis */
|
if (ww.block_align != 0 || ww.bits_per_sample != 0) goto fail; /* always 0 for Worbis */
|
||||||
|
|
||||||
/* autodetect format (fields are mostly common, see the end of the file) */
|
/* autodetect format (fields are mostly common, see the end of the file) */
|
||||||
if (find_chunk(streamFile, 0x766F7262,first_offset,0, &vorb_offset,&vorb_size, ww.big_endian, 0)) { /*"vorb"*/
|
if (find_chunk(streamFile, 0x766F7262,first_offset,0, &vorb_offset,&vorb_size, ww.big_endian, 0)) { /* "vorb" */
|
||||||
/* older Wwise (~<2012) */
|
/* older Wwise (~<2012) */
|
||||||
|
|
||||||
switch(vorb_size) {
|
switch(vorb_size) {
|
||||||
case 0x2C: /* earliest (~2009), [UFC Undisputed 2009 (PS3), some EVE Online Apocrypha (PC)?] */
|
case 0x2C: /* earliest (~2009) [The Lord of the Rings: Conquest (PC)] */
|
||||||
case 0x28: /* early (~2009) [The Lord of the Rings: Conquest (PC)] */
|
case 0x28: /* early (~2009) [UFC Undisputed 2009 (PS3), some EVE Online Apocrypha (PC)] */
|
||||||
data_offsets = 0x18;
|
data_offsets = 0x18;
|
||||||
block_offsets = 0; /* no need, full headers are present */
|
block_offsets = 0; /* no need, full headers are present */
|
||||||
cfg.header_type = WWV_TYPE_8;
|
cfg.header_type = WWV_TYPE_8;
|
||||||
@ -299,8 +308,8 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) {
|
|||||||
cfg.setup_type = WWV_HEADER_TRIAD;
|
cfg.setup_type = WWV_HEADER_TRIAD;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x34: /* common (2010~2011) */
|
case 0x34: /* common (2010~2011) [The King of Fighters XII (PS3), Assassin's Creed II (X360)] */
|
||||||
case 0x32: /* very rare (mid 2011) [Saints Row the 3rd (PC)] */
|
case 0x32: /* rare (mid 2011) [Saints Row the 3rd (PC)] */
|
||||||
data_offsets = 0x18;
|
data_offsets = 0x18;
|
||||||
block_offsets = 0x30;
|
block_offsets = 0x30;
|
||||||
cfg.header_type = WWV_TYPE_6;
|
cfg.header_type = WWV_TYPE_6;
|
||||||
@ -377,8 +386,6 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) {
|
|||||||
cfg.setup_type = is_wem ? WWV_AOTUV603_CODEBOOKS : WWV_EXTERNAL_CODEBOOKS; /* aoTuV came along .wem */
|
cfg.setup_type = is_wem ? WWV_AOTUV603_CODEBOOKS : WWV_EXTERNAL_CODEBOOKS; /* aoTuV came along .wem */
|
||||||
break;
|
break;
|
||||||
|
|
||||||
//case 0x2a: /* Rocksmith 2011 (X360)? */
|
|
||||||
//non mod packets? TYPE_06? (possibly detectable by checking setup's granule, should be 0)
|
|
||||||
default:
|
default:
|
||||||
VGM_LOG("WWISE: unknown extra size 0x%x\n", vorb_size);
|
VGM_LOG("WWISE: unknown extra size 0x%x\n", vorb_size);
|
||||||
goto fail;
|
goto fail;
|
||||||
@ -449,6 +456,11 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) {
|
|||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ww.truncated) {
|
||||||
|
ww.data_size = ww.file_size - ww.data_offset;
|
||||||
|
vgmstream->num_samples = dsp_bytes_to_samples(ww.data_size, ww.channels);
|
||||||
|
}
|
||||||
|
|
||||||
/* for some reason all(?) DSP .wem do full loops (even mono/jingles/etc) but
|
/* for some reason all(?) DSP .wem do full loops (even mono/jingles/etc) but
|
||||||
* several tracks do loop like this, so disable it for short-ish tracks */
|
* several tracks do loop like this, so disable it for short-ish tracks */
|
||||||
if (ww.loop_flag && vgmstream->loop_start_sample == 0 &&
|
if (ww.loop_flag && vgmstream->loop_start_sample == 0 &&
|
||||||
@ -457,7 +469,6 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* get coefs and default history */
|
/* get coefs and default history */
|
||||||
dsp_read_coefs(vgmstream,streamFile,wiih_offset, 0x2e, ww.big_endian);
|
dsp_read_coefs(vgmstream,streamFile,wiih_offset, 0x2e, ww.big_endian);
|
||||||
for (i=0; i < ww.channels; i++) {
|
for (i=0; i < ww.channels; i++) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user