From 1222c96268f7dd03cf541e9a39ae5672fecf50e3 Mon Sep 17 00:00:00 2001 From: Chris Moeller Date: Sun, 19 Oct 2014 14:09:45 -0700 Subject: [PATCH] Updated vgmstream to support BFSTM files --- .../vgmstream.xcodeproj/project.pbxproj | 4 + .../vgmstream/vgmstream/src/meta/bfstm.c | 182 ++++++++++++++++++ .../vgmstream/vgmstream/src/meta/bfwav.c | 11 +- .../vgmstream/vgmstream/src/meta/meta.h | 2 + .../vgmstream/vgmstream/src/vgmstream.c | 7 + .../vgmstream/vgmstream/src/vgmstream.h | 1 + Plugins/vgmstream/vgmstream/VGMDecoder.m | 2 +- 7 files changed, 205 insertions(+), 4 deletions(-) create mode 100644 Frameworks/vgmstream/vgmstream/src/meta/bfstm.c diff --git a/Frameworks/vgmstream/vgmstream.xcodeproj/project.pbxproj b/Frameworks/vgmstream/vgmstream.xcodeproj/project.pbxproj index 3a4b5b709..f46a98d38 100644 --- a/Frameworks/vgmstream/vgmstream.xcodeproj/project.pbxproj +++ b/Frameworks/vgmstream/vgmstream.xcodeproj/project.pbxproj @@ -323,6 +323,7 @@ 836F705818BDC2190095E648 /* vgmstream.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6F1C18BDC2190095E648 /* vgmstream.c */; }; 836F705918BDC2190095E648 /* vgmstream.h in Headers */ = {isa = PBXBuildFile; fileRef = 836F6F1D18BDC2190095E648 /* vgmstream.h */; settings = {ATTRIBUTES = (Public, ); }; }; 83A5F75F198DF021009AF94C /* bfwav.c in Sources */ = {isa = PBXBuildFile; fileRef = 83A5F75E198DF021009AF94C /* bfwav.c */; }; + 83BAFB6C19F45EB3005DAB60 /* bfstm.c in Sources */ = {isa = PBXBuildFile; fileRef = 83BAFB6B19F45EB3005DAB60 /* bfstm.c */; }; 83F5F8831908D0A400C8E65F /* fsb5.c in Sources */ = {isa = PBXBuildFile; fileRef = 83F5F8821908D0A400C8E65F /* fsb5.c */; }; /* End PBXBuildFile section */ @@ -709,6 +710,7 @@ 836F6F1C18BDC2190095E648 /* vgmstream.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = vgmstream.c; sourceTree = ""; }; 836F6F1D18BDC2190095E648 /* vgmstream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = vgmstream.h; sourceTree = ""; }; 83A5F75E198DF021009AF94C /* bfwav.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = bfwav.c; sourceTree = ""; }; + 83BAFB6B19F45EB3005DAB60 /* bfstm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = bfstm.c; sourceTree = ""; }; 83F5F8821908D0A400C8E65F /* fsb5.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = fsb5.c; sourceTree = ""; }; /* End PBXFileReference section */ @@ -895,6 +897,7 @@ 836F6E2718BDC2180095E648 /* meta */ = { isa = PBXGroup; children = ( + 83BAFB6B19F45EB3005DAB60 /* bfstm.c */, 83A5F75E198DF021009AF94C /* bfwav.c */, 83F5F8821908D0A400C8E65F /* fsb5.c */, 836F6E2918BDC2180095E648 /* 2dx9.c */, @@ -1420,6 +1423,7 @@ 836F6F4018BDC2190095E648 /* bdsp_blocked.c in Sources */, 836F700418BDC2190095E648 /* ps2_vas.c in Sources */, 836F6F9818BDC2190095E648 /* mattel_hyperscan.c in Sources */, + 83BAFB6C19F45EB3005DAB60 /* bfstm.c in Sources */, 836F704418BDC2190095E648 /* ws_aud.c in Sources */, 836F6FBC18BDC2190095E648 /* nub.c in Sources */, 836F6F6D18BDC2190095E648 /* aifc.c in Sources */, diff --git a/Frameworks/vgmstream/vgmstream/src/meta/bfstm.c b/Frameworks/vgmstream/vgmstream/src/meta/bfstm.c new file mode 100644 index 000000000..119c921e2 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/bfstm.c @@ -0,0 +1,182 @@ +#include "meta.h" +#include "../util.h" + +VGMSTREAM * init_vgmstream_bfstm(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + char filename[PATH_LIMIT]; + + coding_t coding_type; + + off_t head_offset; + off_t seek_offset; + off_t data_offset; + int codec_number; + int channel_count; + int loop_flag; + int ima = 0; + off_t start_offset; + + /* check extension, case insensitive */ + streamFile->get_name(streamFile, filename, sizeof(filename)); + if (strcasecmp("bfstm", filename_extension(filename))) + goto fail; + + + /* check header */ + if ((uint32_t)read_32bitBE(0, streamFile) != 0x4653544D) /* "FSTM" */ + goto fail; + if ((uint32_t)read_32bitBE(4, streamFile) != 0xFEFF0040) + goto fail; + + int founddata = 0; + off_t tempoffset1 = 0x8; + + while (!(founddata)) + { + if ((uint32_t)read_32bitBE(tempoffset1, streamFile) == 0x40020000) + { + data_offset = read_32bitBE(tempoffset1 + 4, streamFile); + founddata++; + break; + } + tempoffset1++; + } + + /* get head offset, check */ + head_offset = read_32bitBE(0x18, streamFile); + + if ((uint32_t)read_32bitBE(head_offset, streamFile) != 0x494E464F) /* "INFO" */ + goto fail; + + seek_offset = read_32bitBE(0x24, streamFile); + + + /* check type details */ + codec_number = read_8bit(head_offset + 0x20, streamFile); + loop_flag = read_8bit(head_offset + 0x21, streamFile); + channel_count = read_8bit(head_offset + 0x22, streamFile); + + switch (codec_number) { + case 0: + coding_type = coding_PCM8; + break; + case 1: + coding_type = coding_PCM16BE; + break; + case 2: + coding_type = coding_NGC_DSP; + break; + default: + goto fail; + } + + if (channel_count < 1) goto fail; + + /* build the VGMSTREAM */ + + vgmstream = allocate_vgmstream(channel_count, loop_flag); + if (!vgmstream) goto fail; + + /* fill in the vital statistics */ + vgmstream->num_samples = read_32bitBE(head_offset + 0x2c, streamFile); + vgmstream->sample_rate = (uint16_t)read_16bitBE(head_offset + 0x26, streamFile); + /* channels and loop flag are set by allocate_vgmstream */ + if (ima) //Shift the loop points back slightly to avoid stupid pops in some IMA streams due to DC offsetting + { + vgmstream->loop_start_sample = read_32bitBE(head_offset + 0x28, streamFile); + if (vgmstream->loop_start_sample > 10000) + { + vgmstream->loop_start_sample -= 5000; + vgmstream->loop_end_sample = vgmstream->num_samples - 5000; + } + else + vgmstream->loop_end_sample = vgmstream->num_samples; + } + else + { + vgmstream->loop_start_sample = read_32bitBE(head_offset + 0x28, streamFile); + vgmstream->loop_end_sample = vgmstream->num_samples; + } + + vgmstream->coding_type = coding_type; + if (channel_count == 1) + vgmstream->layout_type = layout_none; + else + { + if (ima) + vgmstream->layout_type = layout_interleave; + else + vgmstream->layout_type = layout_interleave_shortblock; + } + vgmstream->meta_type = meta_FSTM; + + if (ima) + vgmstream->interleave_block_size = 0x200; + else { + vgmstream->interleave_block_size = read_32bitBE(head_offset + 0x34, streamFile); + vgmstream->interleave_smallblock_size = read_32bitBE(head_offset + 0x44, streamFile); + } + + if (vgmstream->coding_type == coding_NGC_DSP) { + off_t coef_offset; + off_t tempoffset2 = head_offset; + int foundcoef = 0; + int i, j; + int coef_spacing = 0x2E; + + while (!(foundcoef)) + { + if ((uint32_t)read_32bitBE(tempoffset2, streamFile) == 0x41020000) + { + coef_offset = read_32bitBE(tempoffset2 + 4, streamFile) + tempoffset2 + (channel_count * 8) - 4 - head_offset; + foundcoef++; + break; + } + tempoffset2++; + } + + for (j = 0; jchannels; j++) { + for (i = 0; i<16; i++) { + vgmstream->ch[j].adpcm_coef[i] = read_16bitBE(head_offset + coef_offset + j*coef_spacing + i * 2, streamFile); + } + } + } + + if (ima) // No SEEK (ADPC) header, so just start where the SEEK header is supposed to be. + start_offset = seek_offset; + else if (vgmstream->coding_type == coding_NGC_DSP) + start_offset = data_offset + 0x20; + else // No SEEK header and not IMA, so just start after the DATA header + start_offset = 0x120; + + + + /* open the file for reading by each channel */ + { + int i; + for (i = 0; ilayout_type == layout_interleave_shortblock) + vgmstream->ch[i].streamfile = streamFile->open(streamFile, filename, + vgmstream->interleave_block_size); + else if (vgmstream->layout_type == layout_interleave) + vgmstream->ch[i].streamfile = streamFile->open(streamFile, filename, + STREAMFILE_DEFAULT_BUFFER_SIZE); + else + vgmstream->ch[i].streamfile = streamFile->open(streamFile, filename, + 0x1000); + + if (!vgmstream->ch[i].streamfile) goto fail; + + vgmstream->ch[i].channel_start_offset = + vgmstream->ch[i].offset = + start_offset + i*vgmstream->interleave_block_size; + } + } + + return vgmstream; + + /* clean up anything we may have opened */ +fail: + if (vgmstream) close_vgmstream(vgmstream); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/bfwav.c b/Frameworks/vgmstream/vgmstream/src/meta/bfwav.c index 402b11eb5..3399860e5 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/bfwav.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/bfwav.c @@ -17,6 +17,7 @@ VGMSTREAM * init_vgmstream_bfwav(STREAMFILE *streamFile) { int big_endian = 1; int ima = 0; + int nsmbu_flag = 0; int32_t(*read_32bit)(off_t, STREAMFILE*) = NULL; int16_t(*read_16bit)(off_t, STREAMFILE*) = NULL; read_16bit = read_16bitBE; @@ -24,9 +25,11 @@ VGMSTREAM * init_vgmstream_bfwav(STREAMFILE *streamFile) { /* check extension, case insensitive */ streamFile->get_name(streamFile, filename, sizeof(filename)); - if (strcasecmp("bfwav", filename_extension(filename))) - goto fail; - + if (strcasecmp("bfwav", filename_extension(filename))) { + if (strcasecmp("bfwavnsmbu",filename_extension(filename))) goto fail; + else nsmbu_flag = 1; + } + /* check header */ if ((uint32_t)read_32bitBE(0, streamFile) != 0x46574156) /* "FWAV" */ goto fail; @@ -75,6 +78,8 @@ VGMSTREAM * init_vgmstream_bfwav(STREAMFILE *streamFile) { /* fill in the vital statistics */ vgmstream->num_samples = read_32bit(head_offset + 0x14, streamFile); vgmstream->sample_rate = (uint16_t)read_16bit(head_offset + 0xE, streamFile); + if (nsmbu_flag) + vgmstream->sample_rate /= 2; /* channels and loop flag are set by allocate_vgmstream */ vgmstream->loop_start_sample = read_32bit(head_offset + 0x10, streamFile); diff --git a/Frameworks/vgmstream/vgmstream/src/meta/meta.h b/Frameworks/vgmstream/vgmstream/src/meta/meta.h index 8dfe2162d..7ceb8e21a 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/meta.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/meta.h @@ -636,6 +636,8 @@ VGMSTREAM * init_vgmstream_otm(STREAMFILE* streamFile); VGMSTREAM * init_vgmstream_bcstm(STREAMFILE* streamFile); +VGMSTREAM * init_vgmstream_bfstm(STREAMFILE* streamFile); + VGMSTREAM * init_vgmstream_bfwav(STREAMFILE* streamFile); #endif diff --git a/Frameworks/vgmstream/vgmstream/src/vgmstream.c b/Frameworks/vgmstream/vgmstream/src/vgmstream.c index 6c6dc3f4d..e046adb5a 100644 --- a/Frameworks/vgmstream/vgmstream/src/vgmstream.c +++ b/Frameworks/vgmstream/vgmstream/src/vgmstream.c @@ -18,6 +18,7 @@ VGMSTREAM * (*init_vgmstream_fcns[])(STREAMFILE *streamFile) = { init_vgmstream_adx, init_vgmstream_brstm, init_vgmstream_bfwav, + init_vgmstream_bfstm, init_vgmstream_nds_strm, init_vgmstream_agsc, init_vgmstream_ngc_adpdtk, @@ -2192,6 +2193,9 @@ void describe_vgmstream(VGMSTREAM * vgmstream, char * desc, int length) { break; case meta_CWAV: snprintf(temp,TEMPSIZE,"Nintendo CWAV header"); + break; + case meta_FWAV: + snprintf(temp,TEMPSIZE,"Nintendo FWAV header"); break; case meta_PSX_XA: snprintf(temp,TEMPSIZE,"RIFF/CDXA header"); @@ -3072,6 +3076,9 @@ void describe_vgmstream(VGMSTREAM * vgmstream, char * desc, int length) { case meta_CSTM: snprintf(temp,TEMPSIZE,"Nintendo 3DS CSTM Header"); break; + case meta_FSTM: + snprintf(temp,TEMPSIZE,"Nintendo Wii U FSTM Header"); + break; case meta_3DS_IDSP: snprintf(temp,TEMPSIZE,"Nintendo 3DS IDSP Header"); break; diff --git a/Frameworks/vgmstream/vgmstream/src/vgmstream.h b/Frameworks/vgmstream/vgmstream/src/vgmstream.h index 4be6821a8..381fd314b 100644 --- a/Frameworks/vgmstream/vgmstream/src/vgmstream.h +++ b/Frameworks/vgmstream/vgmstream/src/vgmstream.h @@ -580,6 +580,7 @@ typedef enum { meta_PS2_VBK, meta_OTM, // Otomedius (Arcade) meta_CSTM, // Nintendo 3DS CSTM + meta_FSTM, // Nintendo Wii U FSTM meta_3DS_IDSP, // Nintendo 3DS IDSP #ifdef VGM_USE_MP4V2 meta_MP4, diff --git a/Plugins/vgmstream/vgmstream/VGMDecoder.m b/Plugins/vgmstream/vgmstream/VGMDecoder.m index ab13e1a25..b1c9ec104 100644 --- a/Plugins/vgmstream/vgmstream/VGMDecoder.m +++ b/Plugins/vgmstream/vgmstream/VGMDecoder.m @@ -212,7 +212,7 @@ err1: + (NSArray *)fileTypes { - return [NSArray arrayWithObjects:@"2dx9", @"aaap", @"aax", @"acm", @"adp", @"adpcm", @"ads", @"adx", @"afc", @"agsc", @"ahx",@"aifc", @"aiff", @"aix", @"amts", @"as4", @"asd", @"asf", @"asr", @"ass", @"ast", @"aud", @"aus", @"baf", @"baka", @"bar", @"bcstm", @"bcwav", @"bfwav", @"bg00", @"bgw", @"bh2pcm", @"bmdx", @"bns", @"bnsf", @"bo2", @"brstm", @"caf", @"capdsp", @"ccc", @"cfn", @"cnk", @"dcs", @"dcsw", @"ddsp", @"de2", @"dmsg", @"dsp", @"dvi", @"dxh", @"eam", @"emff", @"enth", @"fag", @"filp", @"fsb", @"gca", @"gcm", @"gcsw", @"gcw", @"genh", @"gms", @"gsp", @"hgc1", @"his", @"hps", @"hwas", @"idsp", @"idvi", @"ikm", @"ild", @"int", @"isd", @"ish", @"ivaud", @"ivb", @"joe", @"kces", @"kcey", @"khv", @"kraw", @"leg", @"logg", @"lps", @"lsf", @"lwav", @"matx", @"mcg", @"mi4", @"mib", @"mic", @"mihb", @"mpdsp", @"msa", @"mss", @"msvp", @"mus", @"musc", @"musx", @"mwv", @"myspd", @"ndp", @"npsf", @"nwa", @"omu", @"otm", @"p3d", @"pcm", @"pdt", @"pnb", @"pos", @"psh", @"psw", @"raw", @"rkv", @"rnd", @"rrds", @"rsd", @"rsf", @"rstm", @"rwar", @"rwav", @"rws", @"rwsd", @"rwx", @"rxw", @"s14", @"sab", @"sad", @"sap", @"sc", @"scd", @"sd9", @"sdt", @"seg", @"sfl", @"sfs", @"sl3", @"sli", @"smp", @"smpl", @"snd", @"sng", @"sns", @"spd", @"sps", @"spsd", @"spt", @"spw", @"ss2", @"ss7", @"ssm", @"sss", @"ster", @"sth", @"stm", @"stma", @"str", @"strm", @"sts", @"stx", @"svag", @"svs", @"swav", @"swd", @"tec", @"thp", @"tk5", @"tydsp", @"um3", @"vag", @"vas", @"vgs", @"vig", @"vjdsp", @"voi", @"vpk", @"vs", @"vsf", @"waa", @"wac", @"wad", @"wam", @"was", @"wavm", @"wb", @"wii", @"wp2", @"wsd", @"wsi", @"wvs", @"xa", @"xa2", @"xa30", @"xmu", @"xss", @"xvas", @"xwav", @"xwb", @"ydsp", @"ymf", @"zsd", @"zwdsp", nil]; + return [NSArray arrayWithObjects:@"2dx9", @"aaap", @"aax", @"acm", @"adp", @"adpcm", @"ads", @"adx", @"afc", @"agsc", @"ahx",@"aifc", @"aiff", @"aix", @"amts", @"as4", @"asd", @"asf", @"asr", @"ass", @"ast", @"aud", @"aus", @"baf", @"baka", @"bar", @"bcstm", @"bcwav", @"bfstm", @"bfwav", @"bg00", @"bgw", @"bh2pcm", @"bmdx", @"bns", @"bnsf", @"bo2", @"brstm", @"caf", @"capdsp", @"ccc", @"cfn", @"cnk", @"dcs", @"dcsw", @"ddsp", @"de2", @"dmsg", @"dsp", @"dvi", @"dxh", @"eam", @"emff", @"enth", @"fag", @"filp", @"fsb", @"gca", @"gcm", @"gcsw", @"gcw", @"genh", @"gms", @"gsp", @"hgc1", @"his", @"hps", @"hwas", @"idsp", @"idvi", @"ikm", @"ild", @"int", @"isd", @"ish", @"ivaud", @"ivb", @"joe", @"kces", @"kcey", @"khv", @"kraw", @"leg", @"logg", @"lps", @"lsf", @"lwav", @"matx", @"mcg", @"mi4", @"mib", @"mic", @"mihb", @"mpdsp", @"msa", @"mss", @"msvp", @"mus", @"musc", @"musx", @"mwv", @"myspd", @"ndp", @"npsf", @"nwa", @"omu", @"otm", @"p3d", @"pcm", @"pdt", @"pnb", @"pos", @"psh", @"psw", @"raw", @"rkv", @"rnd", @"rrds", @"rsd", @"rsf", @"rstm", @"rwar", @"rwav", @"rws", @"rwsd", @"rwx", @"rxw", @"s14", @"sab", @"sad", @"sap", @"sc", @"scd", @"sd9", @"sdt", @"seg", @"sfl", @"sfs", @"sl3", @"sli", @"smp", @"smpl", @"snd", @"sng", @"sns", @"spd", @"sps", @"spsd", @"spt", @"spw", @"ss2", @"ss7", @"ssm", @"sss", @"ster", @"sth", @"stm", @"stma", @"str", @"strm", @"sts", @"stx", @"svag", @"svs", @"swav", @"swd", @"tec", @"thp", @"tk5", @"tydsp", @"um3", @"vag", @"vas", @"vgs", @"vig", @"vjdsp", @"voi", @"vpk", @"vs", @"vsf", @"waa", @"wac", @"wad", @"wam", @"was", @"wavm", @"wb", @"wii", @"wp2", @"wsd", @"wsi", @"wvs", @"xa", @"xa2", @"xa30", @"xmu", @"xss", @"xvas", @"xwav", @"xwb", @"ydsp", @"ymf", @"zsd", @"zwdsp", nil]; } + (NSArray *)mimeTypes