diff --git a/Frameworks/vgmstream/vgmstream.xcodeproj/project.pbxproj b/Frameworks/vgmstream/vgmstream.xcodeproj/project.pbxproj index 4464c4916..8cdba6015 100644 --- a/Frameworks/vgmstream/vgmstream.xcodeproj/project.pbxproj +++ b/Frameworks/vgmstream/vgmstream.xcodeproj/project.pbxproj @@ -368,6 +368,7 @@ 83EDE5D81A70951A005F5D84 /* mca.c in Sources */ = {isa = PBXBuildFile; fileRef = 83EDE5D61A70951A005F5D84 /* mca.c */; }; 83EDE5D91A70951A005F5D84 /* btsnd.c in Sources */ = {isa = PBXBuildFile; fileRef = 83EDE5D71A70951A005F5D84 /* btsnd.c */; }; 83F5F8831908D0A400C8E65F /* fsb5.c in Sources */ = {isa = PBXBuildFile; fileRef = 83F5F8821908D0A400C8E65F /* fsb5.c */; }; + 83FF0EBC1E93282100C58054 /* wwise.c in Sources */ = {isa = PBXBuildFile; fileRef = 83FF0EBB1E93282100C58054 /* wwise.c */; }; /* End PBXBuildFile section */ /* Begin PBXBuildRule section */ @@ -838,6 +839,7 @@ 83EDE5D61A70951A005F5D84 /* mca.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mca.c; sourceTree = ""; }; 83EDE5D71A70951A005F5D84 /* btsnd.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = btsnd.c; sourceTree = ""; }; 83F5F8821908D0A400C8E65F /* fsb5.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = fsb5.c; sourceTree = ""; }; + 83FF0EBB1E93282100C58054 /* wwise.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = wwise.c; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -1075,6 +1077,7 @@ 836F6E2718BDC2180095E648 /* meta */ = { isa = PBXGroup; children = ( + 83FF0EBB1E93282100C58054 /* wwise.c */, 83AB8C731E8072A100086084 /* nub_vag.c */, 83AB8C741E8072A100086084 /* x360_ast.c */, 83299FCE1E7660C7003A3242 /* bik.c */, @@ -1552,6 +1555,7 @@ 836F6FA118BDC2190095E648 /* myspd.c in Sources */, 836F6FD718BDC2190095E648 /* ps2_filp.c in Sources */, 836F703418BDC2190095E648 /* stx.c in Sources */, + 83FF0EBC1E93282100C58054 /* wwise.c in Sources */, 836F6F7018BDC2190095E648 /* apple_caff.c in Sources */, 836F700018BDC2190095E648 /* ps2_svag.c in Sources */, 836F6FB618BDC2190095E648 /* ngc_sck_dsp.c in Sources */, diff --git a/Frameworks/vgmstream/vgmstream/src/formats.c b/Frameworks/vgmstream/vgmstream/src/formats.c index bbb1b51b8..dc8aada8e 100644 --- a/Frameworks/vgmstream/vgmstream/src/formats.c +++ b/Frameworks/vgmstream/vgmstream/src/formats.c @@ -298,6 +298,7 @@ static const char* extension_list[] = { //"wav", //common "wavm", "wb", + "wem", "wii", "wmus", "wp2", @@ -835,6 +836,7 @@ static const meta_info meta_info_list[] = { {meta_X360_PASX, "Namco PASX header"}, {meta_XMA_RIFF, "Microsoft XMA RIFF header"}, {meta_X360_AST, "Capcom AST header"}, + {meta_WWISE_RIFF, "Audiokinetic Wwise RIFF header"}, #ifdef VGM_USE_VORBIS {meta_OGG_VORBIS, "Ogg Vorbis"}, diff --git a/Frameworks/vgmstream/vgmstream/src/meta/Makefile.unix.am b/Frameworks/vgmstream/vgmstream/src/meta/Makefile.unix.am index 91ae2ea4f..f1912b7d6 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/Makefile.unix.am +++ b/Frameworks/vgmstream/vgmstream/src/meta/Makefile.unix.am @@ -1,10 +1,10 @@ noinst_LTLIBRARIES = libmeta.la -AM_CFLAGS = -Wall @CFLAGS@ -I$(top_builddir) -I$(top_srcdir) +AM_CFLAGS = -Wall @CFLAGS@ -DVAR_ARRAYS -I$(top_builddir) -I$(top_srcdir) AM_MAKEFLAGS=-f Makefile.unix -libmeta_la_LDFLAGS = -libmeta_la_SOURCES = +libmeta_la_LDFLAGS = +libmeta_la_SOURCES = libmeta_la_SOURCES += Cstr.c libmeta_la_SOURCES += adx_header.c libmeta_la_SOURCES += afc_header.c @@ -56,6 +56,7 @@ libmeta_la_SOURCES += xss.c libmeta_la_SOURCES += ps2_sl3.c libmeta_la_SOURCES += ps2_aus.c libmeta_la_SOURCES += fsb.c +libmeta_la_SOURCES += fsb5.c libmeta_la_SOURCES += rsd.c libmeta_la_SOURCES += rwx.c libmeta_la_SOURCES += xwb.c @@ -208,9 +209,9 @@ libmeta_la_SOURCES += sqex_scd.c libmeta_la_SOURCES += ngc_nst_dsp.c libmeta_la_SOURCES += baf.c libmeta_la_SOURCES += ps3_msf.c -libmeta_la_SOURCES += nub.c +libmeta_la_SOURCES += nub_vag.c libmeta_la_SOURCES += ps3_past.c -libmeta_la_SOURCES += ps3_sgh_sgb.c +libmeta_la_SOURCES += sgxd.c libmeta_la_SOURCES += ngca.c libmeta_la_SOURCES += wii_ras.c libmeta_la_SOURCES += ps2_spm.c @@ -218,7 +219,7 @@ libmeta_la_SOURCES += x360_tra.c libmeta_la_SOURCES += ps2_iab.c libmeta_la_SOURCES += ps2_strlr.c libmeta_la_SOURCES += lsf.c -libmeta_la_SOURCES += ps3_vawx.c +libmeta_la_SOURCES += vawx.c libmeta_la_SOURCES += pc_snds.c libmeta_la_SOURCES += ps2_wmus.c libmeta_la_SOURCES += mattel_hyperscan.c @@ -235,7 +236,24 @@ libmeta_la_SOURCES += ps2_hsf.c libmeta_la_SOURCES += ps3_ivag.c libmeta_la_SOURCES += ps2_2pfs.c libmeta_la_SOURCES += ubi_ckd.c -libmeta_la_SOURCES += otm.c -libmeta_la_SOURCES += bcstm.c +libmeta_la_SOURCES += otm.c +libmeta_la_SOURCES += bcstm.c +libmeta_la_SOURCES += bfwav.c +libmeta_la_SOURCES += bfstm.c +libmeta_la_SOURCES += g1l.c +libmeta_la_SOURCES += ps2_vbk.c +libmeta_la_SOURCES += mca.c +libmeta_la_SOURCES += btsnd.c +libmeta_la_SOURCES += hca.c +libmeta_la_SOURCES += ps2_svag_snk.c +libmeta_la_SOURCES += mp4.c +libmeta_la_SOURCES += xma.c +libmeta_la_SOURCES += ps2_vds_vdm.c +libmeta_la_SOURCES += x360_cxs.c +libmeta_la_SOURCES += dsp_adx.c +libmeta_la_SOURCES += bik.c +libmeta_la_SOURCES += akb.c +libmeta_la_SOURCES += x360_ast.c +libmeta_la_SOURCES += wwise.c EXTRA_DIST = meta.h diff --git a/Frameworks/vgmstream/vgmstream/src/meta/fsb5.c b/Frameworks/vgmstream/vgmstream/src/meta/fsb5.c index f1cf586dc..efc0ca21b 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/fsb5.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/fsb5.c @@ -175,6 +175,11 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) { /* fill in the vital statistics */ vgmstream->sample_rate = SampleRate; vgmstream->num_streams = TotalStreams; + vgmstream->num_samples = NumSamples; + if (LoopFlag) { + vgmstream->loop_start_sample = LoopStart; + vgmstream->loop_end_sample = LoopEnd; + } vgmstream->meta_type = meta_FSB5; switch (CodingID) { @@ -227,7 +232,10 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) { goto fail; case 0x09: /* FMOD_SOUND_FORMAT_HEVAG */ - goto fail; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x10; + vgmstream->coding_type = coding_HEVAG; + break; #ifdef VGM_USE_FFMPEG case 0x0A: {/* FMOD_SOUND_FORMAT_XMA */ @@ -294,12 +302,6 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) { goto fail; } - vgmstream->num_samples = NumSamples; - if (LoopFlag) { - vgmstream->loop_start_sample = LoopStart; - vgmstream->loop_end_sample = LoopEnd; - } - if (!vgmstream_open_stream(vgmstream,streamFile,StartOffset)) goto fail; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/mca.c b/Frameworks/vgmstream/vgmstream/src/meta/mca.c index c74025d4d..bdb6ddc23 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/mca.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/mca.c @@ -32,6 +32,9 @@ VGMSTREAM * init_vgmstream_mca(STREAMFILE *streamFile) { vgmstream->loop_start_sample = read_32bitLE(0x14, streamFile); vgmstream->loop_end_sample = read_32bitLE(0x18, streamFile); + if (vgmstream->loop_end_sample > vgmstream->num_samples) /* some MH3U songs, somehow */ + vgmstream->loop_end_sample = vgmstream->num_samples; + vgmstream->coding_type = coding_NGC_DSP; vgmstream->layout_type = channel_count == 1 ? layout_none : layout_interleave; vgmstream->meta_type = meta_MCA; @@ -55,7 +58,7 @@ VGMSTREAM * init_vgmstream_mca(STREAMFILE *streamFile) { coef_shift = read_16bitLE(0x28, streamFile); coef_start = head_size - coef_spacing * channel_count; - start_offset = head_size; + start_offset = get_streamfile_size(streamFile) - data_size; /* usually head_size but not for some MH3U songs */ coef_offset = coef_start + coef_shift * 0x14; } else { /* v5: Ace Attourney 6, Monster Hunter Generations, v6+? */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/meta.h b/Frameworks/vgmstream/vgmstream/src/meta/meta.h index 1848a7c9e..02c323431 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/meta.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/meta.h @@ -674,4 +674,6 @@ VGMSTREAM * init_vgmstream_akb2_multi(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_x360_ast(STREAMFILE *streamFile); +VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile); + #endif /*_META_H*/ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ps3_xvag.c b/Frameworks/vgmstream/vgmstream/src/meta/ps3_xvag.c index da257c6f7..adb6c8436 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ps3_xvag.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ps3_xvag.c @@ -33,7 +33,7 @@ VGMSTREAM * init_vgmstream_ps3_xvag(STREAMFILE *streamFile) { /* 0x08: flags? (&0x01=big endian?) 0x0a: version (chunk sizes vary) */ /* "fmat": base format */ - if (!find_chunk(streamFile, 0x666D6174,first_offset,0, &chunk_offset,NULL, !little_endian)) goto fail; /*"fmat"*/ + if (!find_chunk(streamFile, 0x666D6174,first_offset,0, &chunk_offset,NULL, !little_endian, 1)) goto fail; /*"fmat"*/ channel_count = read_32bit(chunk_offset+0x00,streamFile); codec = read_32bit(chunk_offset+0x04,streamFile); num_samples = read_32bit(chunk_offset+0x08,streamFile); @@ -88,7 +88,7 @@ VGMSTREAM * init_vgmstream_ps3_xvag(STREAMFILE *streamFile) { /* "mpin": mpeg info */ /* 0x00/04: mpeg version/layer? other: unknown or repeats of "fmat" */ - if (!find_chunk(streamFile, 0x6D70696E,first_offset,0, &chunk_offset,NULL, !little_endian)) goto fail; /*"mpin"*/ + if (!find_chunk(streamFile, 0x6D70696E,first_offset,0, &chunk_offset,NULL, !little_endian, 1)) goto fail; /*"mpin"*/ fixed_frame_size = read_32bit(chunk_offset+0x1c,streamFile); mpeg_data = init_mpeg_codec_data_interleaved(streamFile, start_offset, &mpeg_coding_type, vgmstream->channels, fixed_frame_size, 0); diff --git a/Frameworks/vgmstream/vgmstream/src/meta/riff.c b/Frameworks/vgmstream/vgmstream/src/meta/riff.c index 065c19596..4f22cbdec 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/riff.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/riff.c @@ -168,10 +168,6 @@ int read_fmt(int big_endian, fmt->coding_type = coding_NGC_DSP; fmt->interleave = 8; break; - case 0xFFF0: /* */ - fmt->coding_type = coding_NGC_DSP; - fmt->interleave = 8; - break; #ifdef VGM_USE_FFMPEG case 0x270: /* ATRAC3 */ #if defined(VGM_USE_FFMPEG) && !defined(VGM_USE_MAIATRAC3PLUS) @@ -395,6 +391,7 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { ((data_size % fmt.block_size) ? (data_size % fmt.block_size - 4 * fmt.channel_count) * 2 / fmt.channel_count : 0); break; case coding_NGC_DSP: + //sample_count = data_size / fmt.channel_count / 8 * 14; /* expected from the "fact" chunk */ break; #ifdef VGM_USE_FFMPEG case coding_FFmpeg: @@ -607,8 +604,6 @@ VGMSTREAM * init_vgmstream_rifx(STREAMFILE *streamFile) { int sample_count = 0; //int fact_sample_count = -1; off_t start_offset = -1; - off_t wiih_offset = -1; - uint32_t wiih_size = 0; int loop_flag = 0; off_t loop_start_offset = -1; @@ -692,10 +687,6 @@ VGMSTREAM * init_vgmstream_rifx(STREAMFILE *streamFile) { if (chunk_size != 4) break; //fact_sample_count = read_32bitBE(current_chunk+8, streamFile); break; - case 0x57696948: /* WiiH */ - wiih_size = read_32bitBE(current_chunk+4, streamFile); - wiih_offset = current_chunk+8; - break; default: /* ignorance is bliss */ break; @@ -707,17 +698,6 @@ VGMSTREAM * init_vgmstream_rifx(STREAMFILE *streamFile) { if (!FormatChunkFound || !DataChunkFound) goto fail; - if (wiih_offset < 0 && fmt.coding_type == coding_MSADPCM && - fmt.size == 0x1c + fmt.channel_count * 0x2e + 2) { - - /* Epic Mickey 2 */ - - wiih_offset = fmt.offset + 8 + 0x1c; - wiih_size = fmt.channel_count * 0x2e; - fmt.coding_type = coding_NGC_DSP; - fmt.interleave = 8; - } - switch (fmt.coding_type) { case coding_PCM16BE: sample_count = data_size/2/fmt.channel_count; @@ -726,17 +706,8 @@ VGMSTREAM * init_vgmstream_rifx(STREAMFILE *streamFile) { sample_count = data_size/fmt.channel_count; break; case coding_NGC_DSP: - /* the only way of getting DSP info right now */ - if (wiih_offset < 0 || wiih_size != 0x2e*fmt.channel_count) - goto fail; - + //sample_count = data_size / fmt.channel_count / 8 * 14; /* expected from the "fact" chunk */ break; -#if 0 - /* found in RE:ORC, looks like it should be MS_IMA instead */ - case coding_MSADPCM: - sample_count = msadpcm_bytes_to_samples(data_size, fmt.block_size, fmt.channel_count); - break; -#endif default: goto fail; } @@ -794,17 +765,6 @@ VGMSTREAM * init_vgmstream_rifx(STREAMFILE *streamFile) { vgmstream->meta_type = meta_RIFX_WAVE; } - /* read from WiiH */ - if (wiih_offset >= 0) { - int i,j; - for (i=0;ich[i].adpcm_coef[j] = read_16bitBE(wiih_offset + i * 0x2e + j * 2,streamFile); - vgmstream->ch[i].adpcm_history1_16 = read_16bitBE(wiih_offset + i * 0x2e + 0x24,streamFile); - vgmstream->ch[i].adpcm_history2_16 = read_16bitBE(wiih_offset + i * 0x2e + 0x26,streamFile); - } - } - /* open the file, set up each channel */ { int i; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/wwise.c b/Frameworks/vgmstream/vgmstream/src/meta/wwise.c new file mode 100644 index 000000000..c76b49d43 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/wwise.c @@ -0,0 +1,355 @@ +#include "meta.h" +#include "../util.h" +#include "../coding/coding.h" + + +/* Wwise uses a custom RIFF/RIFX header, non-standard enough that it's parsed it here. + * There is some repetition from other metas, but not enough to bother me. + * + * Some info: https://www.audiokinetic.com/en/library/edge/ + */ +typedef enum { PCM, IMA, VORBIS, DSP, XMA2, XWMA, AAC, HEVAG, ATRAC9 } wwise_codec; +typedef struct { + int big_endian; + size_t file_size; + + /* chunks references */ + off_t fmt_offset; + size_t fmt_size; + off_t data_offset; + size_t data_size; + + /* standard fmt stuff */ + wwise_codec codec; + int format; + int channels; + int sample_rate; + int block_align; + int bit_per_sample; + size_t extra_size; + + int loop_flag; + uint32_t num_samples; + uint32_t loop_start_sample; + uint32_t loop_end_sample; +} wwise_header; + + +/* Wwise - Audiokinetic Wwise (Wave Works Interactive Sound Engine) middleware */ +VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + wwise_header ww; + off_t start_offset, first_offset = 0xc; + int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL; + int16_t (*read_16bit)(off_t,STREAMFILE*) = NULL; + + /* basic checks */ + /* .wem (Wwise Encoded Media) is "newer Wwise", used after the 2011.2 SDK (~july) + * .wav (ex. Shadowrun X360) and .ogg (ex. KOF XII X360) are used only in older Wwise */ + if (!check_extensions(streamFile,"wem,wav,lwav,ogg,logg")) goto fail; /* .xma may be possible? */ + + if ((read_32bitBE(0x00,streamFile) != 0x52494646) && /* "RIFF" (LE) */ + (read_32bitBE(0x00,streamFile) != 0x52494658)) /* "RIFX" (BE) */ + goto fail; + if ((read_32bitBE(0x08,streamFile) != 0x57415645)) /* "WAVE" */ + goto fail; + + memset(&ww,0,sizeof(wwise_header)); + + ww.big_endian = read_32bitBE(0x00,streamFile) == 0x52494658;/* RIFX */ + if (ww.big_endian) { /* Wwise honors machine's endianness (PC=RIFF, X360=RIFX --unlike XMA) */ + read_32bit = read_32bitBE; + read_16bit = read_16bitBE; + } else { + read_32bit = read_32bitLE; + read_16bit = read_16bitLE; + } + + ww.file_size = streamFile->get_size(streamFile); + +#if 0 + /* sometimes uses a RIFF size that doesn't count chunk/sizes, or just wrong...? */ + if (4+4+read_32bit(0x04,streamFile) != ww.file_size) { + VGM_LOG("WWISE: bad riff size (real=0x%x vs riff=0x%x)\n", 4+4+read_32bit(0x04,streamFile), ww.file_size); + goto fail; + } +#endif + + + /* parse WAVEFORMATEX (roughly spec-compliant but some massaging is needed) */ + { + off_t loop_offset; + size_t loop_size; + + /* 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 (!find_chunk(streamFile, 0x64617461,first_offset,0, &ww.data_offset,&ww.data_size, ww.big_endian, 0)) goto fail; /*"data"*/ + + if (ww.data_size > ww.file_size) { + VGM_LOG("WWISE: bad data size (real=0x%x > riff=0x%x)\n", ww.data_size, ww.file_size); + goto fail; + } + + /* base fmt */ + if (ww.fmt_size < 0x12) goto fail; + ww.format = (uint16_t)read_16bit(ww.fmt_offset+0x00,streamFile); + ww.channels = read_16bit(ww.fmt_offset+0x02,streamFile); + ww.sample_rate = read_32bit(ww.fmt_offset+0x04,streamFile); + /* 0x08: average samples per second */ + ww.block_align = (uint16_t)read_16bit(ww.fmt_offset+0x0c,streamFile); + ww.bit_per_sample = (uint16_t)read_16bit(ww.fmt_offset+0x0e,streamFile); + if (ww.fmt_size > 0x10 && ww.format != 0x0165 && ww.format != 0x0166) /* ignore XMAWAVEFORMAT */ + ww.extra_size = (uint16_t)read_16bit(ww.fmt_offset+0x10,streamFile); +#if 0 + /* channel bitmask, see AkSpeakerConfig.h (ex. 1ch uses FRONT_CENTER 0x4, 2ch FRONT_LEFT 0x1 | FRONT_RIGHT 0x2) */ + if (ww.extra_size >= 6) + ww.channel_config = read_32bit(ww.fmt_offset+0x14,streamFile); +#endif + + /* find loop info (both used in early and late Wwise and seem to follow the spec) */ + if (find_chunk(streamFile, 0x736D706C,first_offset,0, &loop_offset,&loop_size, ww.big_endian, 0)) { /*"smpl"*/ + if (loop_size >= 0x34 + && read_32bit(loop_offset+0x1c, streamFile)==1 /*loop count*/ + && read_32bit(loop_offset+0x24+4, streamFile)==0) { + ww.loop_flag = 1; + ww.loop_start_sample = read_32bit(loop_offset+0x24+0x8, streamFile); + ww.loop_end_sample = read_32bit(loop_offset+0x24+0xc,streamFile); + //todo fix repeat looping + } + } else if (find_chunk(streamFile, 0x4C495354,first_offset,0, &loop_offset,&loop_size, ww.big_endian, 0)) { /*"LIST"*/ + //todo parse "adtl" (does it ever contain loop info in Wwise?) + } + + /* other Wwise specific: */ + //"JUNK": optional padding so that raw data starts in an offset multiple of 0x10 (0-size JUNK exists) + //"akd ": unknown (IMA/PCM; "audiokinetic data"?) + } + + /* format to codec */ + switch(ww.format) { + case 0x0001: ww.codec = PCM; break; /* older Wwise */ + case 0x0002: ww.codec = IMA; break; /* newer Wwise (conflicts with MSADPCM) */ + case 0x0011: ww.codec = IMA; break; /* older Wwise */ + case 0x0069: ww.codec = IMA; break; /* older Wwise */ + case 0x0165: ww.codec = XMA2; break; + case 0x0166: ww.codec = XMA2; break; + case 0xAAC0: ww.codec = AAC; break; + case 0xFFF0: ww.codec = DSP; break; + case 0xFFFB: ww.codec = HEVAG; break; + case 0xFFFC: ww.codec = ATRAC9; break; + case 0xFFFE: ww.codec = PCM; break; /* newer Wwise ("PCM for Wwise Authoring") (conflicts with WAVEFORMATEXTENSIBLE) */ + case 0xFFFF: ww.codec = VORBIS; break; + default: + VGM_LOG("WWISE: unknown codec 0x%x \n", ww.format); + goto fail; + } + /* fix for newer Wwise DSP with coefs: Epic Mickey 2 (Wii), Batman Arkham Origins Blackgate (3DS) */ + if (ww.format == 0x0002 && ww.extra_size == 0x0c + ww.channels * 0x2e) { + ww.codec = DSP; + } + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(ww.channels,ww.loop_flag); + if (!vgmstream) goto fail; + + vgmstream->sample_rate = ww.sample_rate; + vgmstream->num_samples = ww.num_samples; + vgmstream->loop_start_sample = ww.loop_start_sample; + vgmstream->loop_end_sample = ww.loop_end_sample; + vgmstream->meta_type = meta_WWISE_RIFF; + + start_offset = ww.data_offset; + + switch(ww.codec) { + case PCM: /* common */ + /* normally riff.c has priority but it's needed when .wem is used */ + if (ww.bit_per_sample != 16) goto fail; + + vgmstream->coding_type = (ww.big_endian ? coding_PCM16BE : coding_PCM16LE); + vgmstream->layout_type = ww.channels > 1 ? layout_interleave : layout_none; + vgmstream->interleave_block_size = 0x02; + + vgmstream->num_samples = ww.data_size / ww.channels / (ww.bit_per_sample/8); + break; + + case IMA: /* common */ + /* slightly modified MS-IMA. + * Original research by hcs in ima_rejigger (https://github.com/hcs64/vgm_ripping/tree/master/demux/ima_rejigger5) */ +#if 0 + if (ww.bit_per_sample != 4) goto fail; + vgmstream->coding_type = coding_WWISE_IMA; + vgmstream->layout_type = layout_none; + vgmstream->interleave_block_size = ww.block_align; + + vgmstream->num_samples = (ww.data_size / ww.block_align) * (ww.block_align - 4 * vgmstream->channels) * 2 /vgmstream->channels; + break; +#endif + VGM_LOG("WWISE: IMA found (unsupported)\n"); + goto fail; + +#ifdef VGM_USE_VORBIS + case VORBIS: { /* common */ + /* Wwise uses custom Vorbis, which changed over time (config must be detected to pass to the decoder). + * Original research by hcs in ww2ogg (https://github.com/hcs64/ww2ogg) */ +#if 0 + off_t vorb_offset; + size_t vorb_size; + + int packet_header_type = 0; /* 1 = size 8 (4-granule + 4-size), 2 = size 6 (4-granule + 2-size) or 3 = size 2 (2-size) */ + int packet_type = 0; /* 1 = standard, 2 = modified vorbis packets */ + int setup_type = 0; /* 1: triad, 2 = inline codebooks, 3 = external codebooks, 4 = external aoTuV codebooks */ + int blocksize_0_pow = 0, blocksize_1_pow = 0; + + if (ww.block_align != 0 || ww.bit_per_sample != 0) goto fail; + + /* autodetect format */ + if (find_chunk(streamFile, 0x766F7262,first_offset,0, &vorb_offset,&vorb_size, ww.big_endian, 0)) { /*"vorb"*/ + /* older Wwise (~2011) */ + switch (vorb_size) { + case 0x28: + case 0x2A: + case 0x2C: + case 0x32: + case 0x34: + default: goto fail; + } + + } + else { + /* newer Wwise (~2012+) */ + if (ww.extra_size != 0x30) goto fail; //todo some 0x2a exist + + packet_header_type = 3; /* size 2 */ + packet_type = 1; //todo mod packets false on certain configs + setup_type = 4; /* aoTuV */ + + /* 0x12 (2): unk (00,10,18) not related to seek table*/ + /* 0x14 (4): channel config */ + vgmstream->num_samples = read_32bit(ww.fmt_offset + 0x18, streamFile); + /* 0x20 (4): config, 0xcb/0xd9/etc */ + /* 0x24 (4): ? samples? */ + /* 0x28 (4): seek table size (setup packet offset within data) */ + /* 0x2c (4): setup size (first audio packet offset within data) */ + /* 0x30 (2): ? */ + /* 0x32 (2): ? */ + /* 0x34 (4): ? */ + /* 0x38 (4): ? */ + /* 0x3c (4): uid */ //todo same as external crc32? + blocksize_0_pow = read_8bit(ww.fmt_offset + 0x40, streamFile); + blocksize_1_pow = read_8bit(ww.fmt_offset + 0x41, streamFile); + + goto fail; + } + + + vgmstream->codec_data = init_wwise_vorbis_codec_data(streamFile, start_offset, vgmstream->channels, vgmstream->sample_rate);//pass endianness too + if (!vgmstream->codec_data) goto fail; + vgmstream->coding_type = coding_wwise_vorbis; + vgmstream->layout_type = layout_none; + break; +#endif + VGM_LOG("WWISE: VORBIS found (unsupported)\n"); + goto fail; + } +#endif + + case DSP: { /* Wii/3DS/WiiU */ + off_t wiih_offset; + size_t wiih_size; + int i; + + if (ww.bit_per_sample != 4) goto fail; + + vgmstream->coding_type = coding_NGC_DSP; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x8; /* ww.block_align = 0x8 in older Wwise, samples per block in newer Wwise */ + + /* find coef position */ + if (find_chunk(streamFile, 0x57696948,first_offset,0, &wiih_offset,&wiih_size, ww.big_endian, 0)) { /*"WiiH"*/ /* older Wwise */ + vgmstream->num_samples = ww.data_size / ww.channels / 8 * 14; + if (wiih_size != 0x2e * ww.channels) goto fail; + } + else if (ww.extra_size == 0x0c + ww.channels * 0x2e) { /* newer Wwise */ + vgmstream->num_samples = read_32bit(ww.fmt_offset + 0x18, streamFile); + wiih_offset = ww.fmt_offset + 0x1c; + wiih_size = 0x2e * ww.channels; + } + else { + goto fail; + } + + /* get coefs and default history */ + dsp_read_coefs(vgmstream,streamFile,wiih_offset, 0x2e, ww.big_endian); + for (i=0; i < ww.channels; i++) { + vgmstream->ch[i].adpcm_history1_16 = read_16bitBE(wiih_offset + i * 0x2e + 0x24,streamFile); + vgmstream->ch[i].adpcm_history2_16 = read_16bitBE(wiih_offset + i * 0x2e + 0x26,streamFile); + } + + break; + } + +#ifdef VGM_USE_FFMPEG + case XMA2: { /* X360/XBone */ + //chunks: + //"XMA2", "seek": same as the official ones + //"XMAc": Wwise extension, XMA2 physical loop regions (loop_start_b, loop_end_b, loop_subframe_data) + + VGM_LOG("WWISE: XMA2 found (unsupported)\n"); + goto fail; + } + + case XWMA: /* X360 */ + VGM_LOG("WWISE: XWMA found (unsupported)\n"); + goto fail; + + case AAC: { /* iOS/Mac */ + ffmpeg_codec_data * ffmpeg_data = NULL; + if (ww.block_align != 0 || ww.bit_per_sample != 0) goto fail; + + /* extra: size 0x12, unknown values */ + + ffmpeg_data = init_ffmpeg_offset(streamFile, ww.data_offset,ww.data_size); + if (!ffmpeg_data) goto fail; + vgmstream->codec_data = ffmpeg_data; + vgmstream->coding_type = coding_FFmpeg; + vgmstream->layout_type = layout_none; + + vgmstream->num_samples = ffmpeg_data->totalSamples; + break; + } +#endif + case HEVAG: /* PSV */ + /* changed values, another bizarre Wwise quirk */ + //ww.block_align /* unknown (1ch=2, 2ch=4) */ + //ww.bit_per_sample; /* probably interleave (0x10) */ + //if (ww.bit_per_sample != 4) goto fail; + + if (ww.big_endian) goto fail; + + /* extra_data: size 0x06, @0x00: samples per block (28), @0x04: channel config */ + + vgmstream->coding_type = coding_HEVAG; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x10; + + vgmstream->num_samples = ww.data_size * 28 / 16 / ww.channels; + break; + + case ATRAC9: /* PSV/PS4 */ + VGM_LOG("WWISE: ATRAC9 found (unsupported)\n"); + goto fail; + + default: + goto fail; + } + + + if ( !vgmstream_open_stream(vgmstream,streamFile,start_offset) ) + goto fail; + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/streamfile.c b/Frameworks/vgmstream/vgmstream/src/streamfile.c index c5e4b784e..f1125edac 100644 --- a/Frameworks/vgmstream/vgmstream/src/streamfile.c +++ b/Frameworks/vgmstream/vgmstream/src/streamfile.c @@ -491,12 +491,12 @@ int check_extensions(STREAMFILE *streamFile, const char * cmp_exts) { * returns 0 on failure */ int find_chunk_be(STREAMFILE *streamFile, uint32_t chunk_id, off_t start_offset, int full_chunk_size, off_t *out_chunk_offset, size_t *out_chunk_size) { - return find_chunk(streamFile, chunk_id, start_offset, full_chunk_size, out_chunk_offset, out_chunk_size, 1); + return find_chunk(streamFile, chunk_id, start_offset, full_chunk_size, out_chunk_offset, out_chunk_size, 1, 0); } int find_chunk_le(STREAMFILE *streamFile, uint32_t chunk_id, off_t start_offset, int full_chunk_size, off_t *out_chunk_offset, size_t *out_chunk_size) { - return find_chunk(streamFile, chunk_id, start_offset, full_chunk_size, out_chunk_offset, out_chunk_size, 0); + return find_chunk(streamFile, chunk_id, start_offset, full_chunk_size, out_chunk_offset, out_chunk_size, 0, 0); } -int find_chunk(STREAMFILE *streamFile, uint32_t chunk_id, off_t start_offset, int full_chunk_size, off_t *out_chunk_offset, size_t *out_chunk_size, int size_big_endian) { +int find_chunk(STREAMFILE *streamFile, uint32_t chunk_id, off_t start_offset, int full_chunk_size, off_t *out_chunk_offset, size_t *out_chunk_size, int size_big_endian, int zero_size_end) { size_t filesize; off_t current_chunk = start_offset; @@ -514,8 +514,8 @@ int find_chunk(STREAMFILE *streamFile, uint32_t chunk_id, off_t start_offset, in return 1; } - /* end chunk with 0 size, seen in some custom formats */ - if (chunk_size == 0) + /* empty chunk with 0 size, seen in some formats (XVAG uses it as end marker, Wwise doesn't) */ + if (chunk_size == 0 && zero_size_end) return 0; current_chunk += full_chunk_size ? chunk_size : 4+4+chunk_size; diff --git a/Frameworks/vgmstream/vgmstream/src/streamfile.h b/Frameworks/vgmstream/vgmstream/src/streamfile.h index 5e804e610..3ac824bd0 100644 --- a/Frameworks/vgmstream/vgmstream/src/streamfile.h +++ b/Frameworks/vgmstream/vgmstream/src/streamfile.h @@ -158,5 +158,5 @@ int check_extensions(STREAMFILE *streamFile, const char * cmp_exts); int find_chunk_be(STREAMFILE *streamFile, uint32_t chunk_id, off_t start_offset, int full_chunk_size, off_t *out_chunk_offset, size_t *out_chunk_size); int find_chunk_le(STREAMFILE *streamFile, uint32_t chunk_id, off_t start_offset, int full_chunk_size, off_t *out_chunk_offset, size_t *out_chunk_size); -int find_chunk(STREAMFILE *streamFile, uint32_t chunk_id, off_t start_offset, int full_chunk_size, off_t *out_chunk_offset, size_t *out_chunk_size, int size_big_endian); +int find_chunk(STREAMFILE *streamFile, uint32_t chunk_id, off_t start_offset, int full_chunk_size, off_t *out_chunk_offset, size_t *out_chunk_size, int size_big_endian, int zero_size_end); #endif diff --git a/Frameworks/vgmstream/vgmstream/src/vgmstream.c b/Frameworks/vgmstream/vgmstream/src/vgmstream.c index aace4f61e..f46c124f3 100644 --- a/Frameworks/vgmstream/vgmstream/src/vgmstream.c +++ b/Frameworks/vgmstream/vgmstream/src/vgmstream.c @@ -341,6 +341,7 @@ VGMSTREAM * (*init_vgmstream_fcns[])(STREAMFILE *streamFile) = { init_vgmstream_akb_multi, init_vgmstream_akb2_multi, init_vgmstream_x360_ast, + init_vgmstream_wwise, #ifdef VGM_USE_FFMPEG init_vgmstream_xma, diff --git a/Frameworks/vgmstream/vgmstream/src/vgmstream.h b/Frameworks/vgmstream/vgmstream/src/vgmstream.h index 8345cb149..7d20d6c33 100644 --- a/Frameworks/vgmstream/vgmstream/src/vgmstream.h +++ b/Frameworks/vgmstream/vgmstream/src/vgmstream.h @@ -607,6 +607,7 @@ typedef enum { meta_X360_PASX, /* Namco PASX (Soul Calibur II HD X360) */ meta_XMA_RIFF, /* Microsoft RIFF XMA */ meta_X360_AST, /* Dead Rising (X360) */ + meta_WWISE_RIFF, /* Audiokinetic Wwise RIFF/RIFX */ #ifdef VGM_USE_VORBIS meta_OGG_VORBIS, /* Ogg Vorbis */