diff --git a/Frameworks/vgmstream/libvgmstream.xcodeproj/project.pbxproj b/Frameworks/vgmstream/libvgmstream.xcodeproj/project.pbxproj index 029bf332b..a6d01a7f1 100644 --- a/Frameworks/vgmstream/libvgmstream.xcodeproj/project.pbxproj +++ b/Frameworks/vgmstream/libvgmstream.xcodeproj/project.pbxproj @@ -551,7 +551,7 @@ 837CEB0223487F2C00E62A4A /* raw_int.c in Sources */ = {isa = PBXBuildFile; fileRef = 837CEAED23487F2C00E62A4A /* raw_int.c */; }; 837CEB0323487F2C00E62A4A /* xavs_streamfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 837CEAEE23487F2C00E62A4A /* xavs_streamfile.h */; }; 837CEB0423487F2C00E62A4A /* jstm.c in Sources */ = {isa = PBXBuildFile; fileRef = 837CEAEF23487F2C00E62A4A /* jstm.c */; }; - 837CEB0523487F2C00E62A4A /* sqex_sead_streamfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 837CEAF023487F2C00E62A4A /* sqex_sead_streamfile.h */; }; + 837CEB0523487F2C00E62A4A /* sqex_streamfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 837CEAF023487F2C00E62A4A /* sqex_streamfile.h */; }; 837CEB072348809400E62A4A /* seb.c in Sources */ = {isa = PBXBuildFile; fileRef = 837CEB062348809400E62A4A /* seb.c */; }; 8383A62C281203C60062E49E /* s3v.c in Sources */ = {isa = PBXBuildFile; fileRef = 8383A62B281203C50062E49E /* s3v.c */; }; 83852B0B2680247900378854 /* rxws.c in Sources */ = {isa = PBXBuildFile; fileRef = 83852B092680247900378854 /* rxws.c */; }; @@ -1376,7 +1376,7 @@ 837CEAED23487F2C00E62A4A /* raw_int.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = raw_int.c; sourceTree = ""; }; 837CEAEE23487F2C00E62A4A /* xavs_streamfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = xavs_streamfile.h; sourceTree = ""; }; 837CEAEF23487F2C00E62A4A /* jstm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = jstm.c; sourceTree = ""; }; - 837CEAF023487F2C00E62A4A /* sqex_sead_streamfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = sqex_sead_streamfile.h; sourceTree = ""; }; + 837CEAF023487F2C00E62A4A /* sqex_streamfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = sqex_streamfile.h; sourceTree = ""; }; 837CEB062348809400E62A4A /* seb.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = seb.c; sourceTree = ""; }; 8383A62B281203C50062E49E /* s3v.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = s3v.c; sourceTree = ""; }; 83852B092680247900378854 /* rxws.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = rxws.c; sourceTree = ""; }; @@ -2297,7 +2297,7 @@ 836F6EF318BDC2190095E648 /* spt_spd.c */, 834FE0D6215C79E9000A5D3D /* sqex_scd_sscf.c */, 836F6EF418BDC2190095E648 /* sqex_scd.c */, - 837CEAF023487F2C00E62A4A /* sqex_sead_streamfile.h */, + 837CEAF023487F2C00E62A4A /* sqex_streamfile.h */, 83A21F84201D8981000F04B9 /* sqex_sead.c */, 8339B322280FDF250076F74B /* sspf.c */, 8317C24826982CC1007DD0B8 /* sspr.c */, @@ -2571,7 +2571,7 @@ 83A8BAE625667AA8000F5F3F /* ps2_enth_streamfile.h in Headers */, 83256CE428666C620036D9C0 /* abi_align.h in Headers */, 8306B0D820984590000302D4 /* ea_eaac_streamfile.h in Headers */, - 837CEB0523487F2C00E62A4A /* sqex_sead_streamfile.h in Headers */, + 837CEB0523487F2C00E62A4A /* sqex_streamfile.h in Headers */, 83031ED9243C510500C3F3E0 /* ubi_lyn_streamfile.h in Headers */, 8373341D23F60C7B00DE14DC /* g7221_decoder_lib.h in Headers */, 8306B0E820984590000302D4 /* opus_interleave_streamfile.h in Headers */, diff --git a/Frameworks/vgmstream/vgmstream/src/coding/coding.h b/Frameworks/vgmstream/vgmstream/src/coding/coding.h index e55c65727..3c3fc216c 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/coding.h +++ b/Frameworks/vgmstream/vgmstream/src/coding/coding.h @@ -409,6 +409,7 @@ typedef struct { uint32_t setup_id; /* external setup */ int big_endian; /* flag */ + uint32_t stream_end; /* optional, to avoid overreading into next subsong or chunk */ /* Wwise Vorbis config */ wwise_setup_t setup_type; diff --git a/Frameworks/vgmstream/vgmstream/src/coding/g7221_decoder_lib.c b/Frameworks/vgmstream/vgmstream/src/coding/g7221_decoder_lib.c index fb5a51e9c..976737eeb 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/g7221_decoder_lib.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/g7221_decoder_lib.c @@ -1132,6 +1132,7 @@ struct g7221_handle { /* control */ int bit_rate; int frame_size; + int test_errors; /* AES setup/state */ s14aes_handle* aes; /* state */ @@ -1179,7 +1180,7 @@ int g7221_decode_frame(g7221_handle* handle, uint8_t* data, int16_t* out_samples * so we could avoid one extra buffer, but for clarity we'll leave as is */ /* unpack data into MLT spectrum coefs */ - res = unpack_frame(handle->bit_rate, data, handle->frame_size, &mag_shift, handle->mlt_coefs, &handle->random_value, encrypted); + res = unpack_frame(handle->bit_rate, data, handle->frame_size, &mag_shift, handle->mlt_coefs, &handle->random_value, handle->test_errors); if (res < 0) goto fail; /* convert coefs to samples using reverse (inverse) MLT */ @@ -1254,6 +1255,7 @@ int g7221_set_key(g7221_handle* handle, const uint8_t* key) { if (key == NULL) { s14aes_close(handle->aes); handle->aes = NULL; + handle->test_errors = 1; /* force? */ return 1; } @@ -1263,6 +1265,8 @@ int g7221_set_key(g7221_handle* handle, const uint8_t* key) { if (!handle->aes) goto fail; } + handle->test_errors = 1; + /* Base key is XORed probably against memdumps, as plain key would be part of the final AES key. However * roundkey is still in memdumps near AES state (~0x1310 from sbox table, that starts with 0x63,0x7c,0x77,0x7b...) * so it isn't too effective. XORing was originally done inside aes_expand_key during S14/S22 init. */ diff --git a/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_decoder.c index bf09b662c..cfcf12f19 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_decoder.c @@ -65,6 +65,9 @@ vorbis_custom_codec_data* init_vorbis_custom(STREAMFILE* sf, off_t start_offset, /* write output */ config->data_start_offset = data->config.data_start_offset; + if (!data->config.stream_end) { + data->config.stream_end = get_streamfile_size(sf); + } return data; @@ -78,19 +81,11 @@ fail: void decode_vorbis_custom(VGMSTREAM* vgmstream, sample_t* outbuf, int32_t samples_to_do, int channels) { VGMSTREAMCHANNEL *stream = &vgmstream->ch[0]; vorbis_custom_codec_data* data = vgmstream->codec_data; - size_t stream_size = get_streamfile_size(stream->streamfile); //data->op.packet = data->buffer;/* implicit from init */ int samples_done = 0; while (samples_done < samples_to_do) { - /* extra EOF check for edge cases */ - if (stream->offset >= stream_size) { - memset(outbuf + samples_done * channels, 0, (samples_to_do - samples_done) * sizeof(sample) * channels); - break; - } - - if (data->samples_full) { /* read more samples */ int samples_to_get; float **pcm; @@ -123,6 +118,12 @@ void decode_vorbis_custom(VGMSTREAM* vgmstream, sample_t* outbuf, int32_t sample else { /* read more data */ int ok, rc; + /* extra EOF check */ + if (stream->offset >= data->config.stream_end) { + /* may need to drain samples? (not a thing in vorbis due to packet types?) */ + goto decode_fail; + } + /* not actually needed, but feels nicer */ data->op.granulepos += samples_to_do; /* can be changed next if desired */ data->op.packetno++; diff --git a/Frameworks/vgmstream/vgmstream/src/formats.c b/Frameworks/vgmstream/vgmstream/src/formats.c index db21765a2..ec3b98ba2 100644 --- a/Frameworks/vgmstream/vgmstream/src/formats.c +++ b/Frameworks/vgmstream/vgmstream/src/formats.c @@ -25,6 +25,7 @@ static const char* extension_list[] = { "2dx9", "2pfs", "3do", + "3ds", //txth/reserved [F1 2011 (3DS)] "4", //for Game.com audio "8", //txth/reserved [Gungage (PS1)] "800", @@ -532,6 +533,7 @@ static const char* extension_list[] = { "sxd", "sxd2", "sxd3", + "szd", "szd1", "szd3", diff --git a/Frameworks/vgmstream/vgmstream/src/meta/adx_keys.h b/Frameworks/vgmstream/vgmstream/src/meta/adx_keys.h index e5cabccc3..90f00a88f 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/adx_keys.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/adx_keys.h @@ -259,7 +259,7 @@ static const adxkey_info adxkey9_list[] = { {0x0000,0x0000,0x0000, NULL,1991062320101111}, // 000712DC5250B6F7 /* Shin Megami Tensei V (Switch) */ - {0x000c,0x13b5,0x1fdb, NULL,0}, // guessed with VGAudio (possible key: 613B4FEE / 1631277038) + {0x0000,0x0000,0x0000, NULL,1731948526}, // 00000000673B6FEE }; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ahx.c b/Frameworks/vgmstream/vgmstream/src/meta/ahx.c index be434451c..6ea24780c 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ahx.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ahx.c @@ -1,48 +1,51 @@ #include "meta.h" #include "../coding/coding.h" #include "../util.h" +#if 0 +#include "adx_keys.h" +#endif -/* AHX - CRI format mainly for voices, contains MPEG-2 Layer 2 audio with lying frame headers */ -VGMSTREAM * init_vgmstream_ahx(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - off_t start_offset; - int channel_count = 1, loop_flag = 0, type; +/* AHX - CRI voice format */ +VGMSTREAM* init_vgmstream_ahx(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + uint32_t start_offset; + int channels = 1, loop_flag = 0, type; - /* check extension, case insensitive */ - if ( !check_extensions(streamFile, "ahx") ) goto fail; + /* checks */ + if (read_u16be(0x00,sf) != 0x8000) + goto fail; - /* check first 2 bytes */ - if ((uint16_t)read_16bitBE(0,streamFile)!=0x8000) goto fail; + if (!check_extensions(sf, "ahx") ) + goto fail; - /* get stream offset, check for CRI signature just before */ - start_offset = (uint16_t)read_16bitBE(0x02,streamFile) + 0x04; - - if ((uint16_t)read_16bitBE(start_offset-0x06,streamFile)!=0x2863 || /* "(c" */ - (uint32_t)read_32bitBE(start_offset-0x04,streamFile)!=0x29435249) /* ")CRI" */ + start_offset = read_u16be(0x02,sf) + 0x04; + if (read_u16be(start_offset - 0x06,sf) != 0x2863 || /* "(c" */ + read_u32be(start_offset - 0x04,sf) != 0x29435249) /* ")CRI" */ goto fail; - /* check for encoding type (0x10 is AHX for DC with bigger frames, 0x11 is AHX, 0x0N are ADX) */ - type = read_8bit(0x04,streamFile); + /* types: 0x10 = AHX for DC with bigger frames, 0x11 = AHX, 0x0N = ADX */ + type = read_u8(0x04,sf); if (type != 0x10 && type != 0x11) goto fail; - /* check for frame size (0 for AHX) */ - if (read_8bit(0x05,streamFile) != 0) goto fail; + /* frame size (0 for AHX) */ + if (read_u8(0x05,sf) != 0) goto fail; /* check for bits per sample? (0 for AHX) */ - if (read_8bit(0x06,streamFile) != 0) goto fail; + if (read_u8(0x06,sf) != 0) goto fail; /* check channel count (only mono AHXs can be created by the encoder) */ - if (read_8bit(0x07,streamFile) != 1) goto fail; + if (read_u8(0x07,sf) != 1) goto fail; /* check version signature */ - if (read_8bit(0x12,streamFile) != 0x06) goto fail; + if (read_u8(0x12,sf) != 0x06) goto fail; + /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); + vgmstream = allocate_vgmstream(channels, loop_flag); if (!vgmstream) goto fail; - vgmstream->sample_rate = read_32bitBE(0x08,streamFile); /* real sample rate */ - vgmstream->num_samples = read_32bitBE(0x0c,streamFile); /* doesn't include encoder_delay (handled in decoder) */ + vgmstream->sample_rate = read_s32be(0x08,sf); /* real sample rate */ + vgmstream->num_samples = read_s32be(0x0c,sf); /* doesn't include encoder_delay (handled in decoder) */ vgmstream->meta_type = meta_AHX; @@ -50,30 +53,51 @@ VGMSTREAM * init_vgmstream_ahx(STREAMFILE *streamFile) { #ifdef VGM_USE_MPEG mpeg_custom_config cfg = {0}; - cfg.encryption = read_8bit(0x13,streamFile); /* 0x08 = keyword encryption */ + cfg.encryption = read_u8(0x13,sf); /* 0x08 = keyword encryption */ cfg.cri_type = type; if (cfg.encryption) { - uint8_t keybuf[6]; - if (read_key_file(keybuf, 6, streamFile) == 6) { - cfg.cri_key1 = get_16bitBE(keybuf+0); - cfg.cri_key2 = get_16bitBE(keybuf+2); - cfg.cri_key3 = get_16bitBE(keybuf+4); + uint8_t keybuf[0x10+1] = {0}; /* approximate max for keystrings, +1 extra null for keystrings */ + size_t key_size; + + key_size = read_key_file(keybuf, sizeof(keybuf), sf); + if (key_size > 0) { +#if 0 + int i, is_ascii; + is_ascii = 1; + for (i = 0; i < key_size; i++) { + if (keybuf[i] < 0x20 || keybuf[i] > 0x7f) { + is_ascii = 0; + break; + } + } +#endif + if (key_size == 0x06 /*&& !is_ascii*/) { + cfg.cri_key1 = get_u16be(keybuf + 0x00); + cfg.cri_key2 = get_u16be(keybuf + 0x02); + cfg.cri_key3 = get_u16be(keybuf + 0x04); + } +#if 0 + else if (is_ascii) { + const char* keystring = (const char*)keybuf; + + derive_adx_key8(keystring, &cfg.cri_key1, &cfg.cri_key2, &cfg.cri_key3); + VGM_LOG("ok: %x, %x, %x\n", cfg.cri_key1, cfg.cri_key2, cfg.cri_key3 ); + } +#endif } } vgmstream->layout_type = layout_none; - vgmstream->codec_data = init_mpeg_custom(streamFile, start_offset, &vgmstream->coding_type, channel_count, MPEG_AHX, &cfg); + vgmstream->codec_data = init_mpeg_custom(sf, start_offset, &vgmstream->coding_type, channels, MPEG_AHX, &cfg); if (!vgmstream->codec_data) goto fail; #else goto fail; #endif } - - if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) ) + if (!vgmstream_open_stream(vgmstream, sf, start_offset)) goto fail; - return vgmstream; fail: diff --git a/Frameworks/vgmstream/vgmstream/src/meta/akb.c b/Frameworks/vgmstream/vgmstream/src/meta/akb.c index e320e91b9..97ab50d62 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/akb.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/akb.c @@ -1,5 +1,7 @@ #include "meta.h" #include "../coding/coding.h" +#include "sqex_streamfile.h" + #if defined(VGM_USE_MP4V2) && defined(VGM_USE_FDKAAC) /* AKB (AAC only) - found in SQEX iOS games */ @@ -11,8 +13,8 @@ VGMSTREAM * init_vgmstream_akb_mp4(STREAMFILE *sf) { if ((uint32_t)read_32bitBE(0, sf) != 0x414b4220) goto fail; - loop_start = read_32bitLE(0x14, sf); - loop_end = read_32bitLE(0x18, sf); + loop_start = read_s32le(0x14, sf); + loop_end = read_s32le(0x18, sf); filesize = get_streamfile_size( sf ); @@ -33,40 +35,59 @@ fail: #endif -/* AKB - found in SQEX iOS games */ -VGMSTREAM * init_vgmstream_akb(STREAMFILE *sf) { - VGMSTREAM * vgmstream = NULL; +/* AKB - found in SQEX 'sdlib' iOS/Android games */ +VGMSTREAM* init_vgmstream_akb(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; off_t start_offset, extradata_offset = 0; size_t stream_size, header_size, subheader_size = 0, extradata_size = 0; - int loop_flag = 0, channel_count, codec, sample_rate; + int loop_flag = 0, channels, codec, sample_rate, version, flags = 0; int num_samples, loop_start, loop_end; /* checks */ - if ( !check_extensions(sf, "akb") ) - goto fail; - if (read_32bitBE(0x00,sf) != 0x414B4220) /* "AKB " */ - goto fail; - if (read_32bitLE(0x08,sf) != get_streamfile_size(sf)) + if (!is_id32be(0x00,sf, "AKB ")) goto fail; - /* 0x04(1): version */ - header_size = read_16bitLE(0x06,sf); + if (!check_extensions(sf, "akb")) + goto fail; - codec = read_8bit(0x0c,sf); - channel_count = read_8bit(0x0d,sf); - sample_rate = (uint16_t)read_16bitLE(0x0e,sf); - num_samples = read_32bitLE(0x10,sf); - loop_start = read_32bitLE(0x14,sf); - loop_end = read_32bitLE(0x18,sf); + version = read_u8(0x04,sf); /* 00=TWEWY, 02=DQs, 03=FFAgito */ + /* 0x05(1); unused? */ + header_size = read_u16le(0x06,sf); + if (read_u32le(0x08,sf) != get_streamfile_size(sf)) + goto fail; + + /* material info, though can only hold 1 */ + codec = read_u8(0x0c,sf); + channels = read_u8(0x0d,sf); + sample_rate = read_u16le(0x0e,sf); + num_samples = read_s32le(0x10,sf); + loop_start = read_s32le(0x14,sf); + loop_end = read_s32le(0x18,sf); /* possibly more complex, see AKB2 */ if (header_size >= 0x44) { /* v2+ */ - extradata_size = read_16bitLE(0x1c,sf); + extradata_size = read_u16le(0x1c,sf); /* 0x20+: config? (pan, volume) */ - subheader_size = read_16bitLE(0x28,sf); + subheader_size = read_u16le(0x28,sf); /* 0x24: file_id? */ - /* 0x2b: encryption bitflag if version > 2? */ + /* 0x28: */ + /* 0x29: */ + /* 0x2a: */ + + /* flags: + * 1: (v2+) enable random volume + * 2: (v2+) enable random pitch + * 4: (v2+) enable random pan + * 8: (v3+) encryption (for MS-ADPCM / Ogg) [Final Fantasy Agito (Android)-ogg bgm only, other sounds don't use AKB] */ + flags = read_u8(0x2B,sf); + /* 0x2c: max random volume */ + /* 0x30: min random volume */ + /* 0x34: max random pitch */ + /* 0x38: min random pitch */ + /* 0x3c: max random pan */ + /* 0x40: min random pan */ + extradata_offset = header_size + subheader_size; start_offset = extradata_offset + extradata_size; } @@ -79,30 +100,34 @@ VGMSTREAM * init_vgmstream_akb(STREAMFILE *sf) { /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); + vgmstream = allocate_vgmstream(channels, loop_flag); if (!vgmstream) goto fail; - vgmstream->sample_rate = sample_rate; vgmstream->meta_type = meta_AKB; - + vgmstream->sample_rate = sample_rate; switch (codec) { case 0x02: { /* MSADPCM [Dragon Quest II (iOS) sfx] */ vgmstream->coding_type = coding_MSADPCM; vgmstream->layout_type = layout_none; - vgmstream->frame_size = read_16bitLE(extradata_offset + 0x02,sf); + vgmstream->frame_size = read_u16le(extradata_offset + 0x02,sf); + + /* encryption, untested but should be the same as Ogg */ + if (version >= 3 && (flags & 8)) + goto fail; /* adjusted samples; bigger or smaller than base samples, akb lib uses these fields instead * (base samples may have more than possible and read over file size otherwise, very strange) * loop_end seems to exist even with loop disabled */ - vgmstream->num_samples = read_32bitLE(extradata_offset + 0x04, sf); - vgmstream->loop_start_sample = read_32bitLE(extradata_offset + 0x08, sf); - vgmstream->loop_end_sample = read_32bitLE(extradata_offset + 0x0c, sf); + vgmstream->num_samples = read_s32le(extradata_offset + 0x04, sf); + vgmstream->loop_start_sample = read_s32le(extradata_offset + 0x08, sf); + vgmstream->loop_end_sample = read_s32le(extradata_offset + 0x0c, sf); break; } #ifdef VGM_USE_VORBIS case 0x05: { /* Ogg Vorbis [Final Fantasy VI (iOS), Dragon Quest II-VI (iOS)] */ + STREAMFILE* temp_sf; VGMSTREAM *ogg_vgmstream = NULL; ogg_vorbis_meta_info_t ovmi = {0}; @@ -111,7 +136,20 @@ VGMSTREAM * init_vgmstream_akb(STREAMFILE *sf) { /* extradata + 0x04: Ogg loop start offset */ /* oggs have loop info in the comments */ - ogg_vgmstream = init_vgmstream_ogg_vorbis_config(sf, start_offset, &ovmi); + /* enable encryption */ + if (version >= 3 && (flags & 8)) { + VGM_LOG("temp1\n"); + temp_sf = setup_sqex_streamfile(sf, start_offset, stream_size, 1, 0x00, 0x00, "ogg"); + if (!temp_sf) goto fail; + VGM_LOG("temp2\n"); + + ogg_vgmstream = init_vgmstream_ogg_vorbis_config(temp_sf, 0x00, &ovmi); + close_streamfile(temp_sf); + } + else { + ogg_vgmstream = init_vgmstream_ogg_vorbis_config(sf, start_offset, &ovmi); + } + if (ogg_vgmstream) { close_vgmstream(vgmstream); return ogg_vgmstream; @@ -161,13 +199,13 @@ VGMSTREAM * init_vgmstream_akb(STREAMFILE *sf) { } #endif - case 0x01: /* PCM16LE */ + case 0x01: /* PCM16LE (from debugging, not seen) */ default: goto fail; } /* open the file for reading */ - if ( !vgmstream_open_stream(vgmstream, sf, start_offset) ) + if (!vgmstream_open_stream(vgmstream, sf, start_offset)) goto fail; return vgmstream; @@ -178,67 +216,69 @@ fail: } -/* AKB2 - found in later SQEX iOS games */ -VGMSTREAM * init_vgmstream_akb2(STREAMFILE *sf) { - VGMSTREAM * vgmstream = NULL; +/* AKB2 - found in later SQEX 'sdlib' iOS/Android games */ +VGMSTREAM* init_vgmstream_akb2(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; off_t start_offset, material_offset, extradata_offset; size_t material_size, extradata_size, stream_size; - int loop_flag = 0, channel_count, encryption_flag, codec, sample_rate, num_samples, loop_start, loop_end; + int loop_flag = 0, channel_count, flags, codec, sample_rate, num_samples, loop_start, loop_end; int total_subsongs, target_subsong = sf->stream_index; - /* check extensions */ - if ( !check_extensions(sf, "akb") ) - goto fail; /* checks */ - if (read_32bitBE(0x00,sf) != 0x414B4232) /* "AKB2" */ + if (!is_id32be(0x00,sf, "AKB2")) goto fail; - if (read_32bitLE(0x08,sf) != get_streamfile_size(sf)) + + if (!check_extensions(sf, "akb")) goto fail; + /* 0x04: version */ + if (read_u32le(0x08,sf) != get_streamfile_size(sf)) + goto fail; /* parse tables */ { off_t table_offset; size_t table_size, entry_size; - off_t akb_header_size = read_16bitLE(0x06, sf); - int table_count = read_8bit(0x0c, sf); + off_t akb_header_size = read_u16le(0x06, sf); + int table_count = read_u8(0x0c, sf); /* probably each table has its type somewhere, but only seen last table = sound table */ if (table_count > 2) /* 2 only seen in some Mobius FF sound banks */ goto fail; entry_size = 0x10; /* technically every entry/table has its own size but to simplify... */ - table_offset = read_32bitLE(akb_header_size + (table_count-1)*entry_size + 0x04, sf); - table_size = read_16bitLE(table_offset + 0x02, sf); + table_offset = read_u32le(akb_header_size + (table_count-1)*entry_size + 0x04, sf); + table_size = read_u16le(table_offset + 0x02, sf); - total_subsongs = read_8bit(table_offset + 0x0f, sf); /* can contain 0 entries too */ + total_subsongs = read_u8(table_offset + 0x0f, sf); /* can contain 0 entries too */ if (target_subsong == 0) target_subsong = 1; if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail; - material_offset = table_offset + read_32bitLE(table_offset + table_size + (target_subsong-1)*entry_size + 0x04, sf); + material_offset = table_offset + read_u32le(table_offset + table_size + (target_subsong-1)*entry_size + 0x04, sf); } /** stream header (material) **/ /* 0x00: 0? */ - codec = read_8bit(material_offset+0x01,sf); - channel_count = read_8bit(material_offset+0x02,sf); - encryption_flag = read_8bit(material_offset+0x03,sf); - material_size = read_16bitLE(material_offset+0x04,sf); - sample_rate = (uint16_t)read_16bitLE(material_offset+0x06,sf); - stream_size = read_32bitLE(material_offset+0x08,sf); - num_samples = read_32bitLE(material_offset+0x0c,sf); + codec = read_u8(material_offset+0x01,sf); + channel_count = read_u8(material_offset+0x02,sf); + flags = read_u8(material_offset+0x03,sf); + material_size = read_u16le(material_offset+0x04,sf); + sample_rate = read_u16le(material_offset+0x06,sf); + stream_size = read_u32le(material_offset+0x08,sf); + num_samples = read_s32le(material_offset+0x0c,sf); - loop_start = read_32bitLE(material_offset+0x10,sf); - loop_end = read_32bitLE(material_offset+0x14,sf); - extradata_size = read_32bitLE(material_offset+0x18,sf); - /* rest: ? (empty or 0x3f80) */ + loop_start = read_s32le(material_offset+0x10,sf); + loop_end = read_s32le(material_offset+0x14,sf); + extradata_size = read_u32le(material_offset+0x18,sf); + /* rest: ? (empty, floats or 0x3f80) */ loop_flag = (loop_end > loop_start); extradata_offset = material_offset + material_size; start_offset = material_offset + material_size + extradata_size; - if (encryption_flag & 0x08) + /* encrypted, not seen (see AKB flags) */ + if (flags & 0x08) goto fail; @@ -267,14 +307,14 @@ VGMSTREAM * init_vgmstream_akb2(STREAMFILE *sf) { case 0x02: { /* MSADPCM [The Irregular at Magic High School Lost Zero (Android)] */ vgmstream->coding_type = coding_MSADPCM; vgmstream->layout_type = layout_none; - vgmstream->frame_size = read_16bitLE(extradata_offset + 0x02, sf); + vgmstream->frame_size = read_u16le(extradata_offset + 0x02, sf); /* adjusted samples; bigger or smaller than base samples, akb lib uses these fields instead * (base samples may have more than possible and read over file size otherwise, very strange) * loop_end seems to exist even with loop disabled */ - vgmstream->num_samples = read_32bitLE(extradata_offset + 0x04, sf); - vgmstream->loop_start_sample = read_32bitLE(extradata_offset + 0x08, sf); - vgmstream->loop_end_sample = read_32bitLE(extradata_offset + 0x0c, sf); + vgmstream->num_samples = read_s32le(extradata_offset + 0x04, sf); + vgmstream->loop_start_sample = read_s32le(extradata_offset + 0x08, sf); + vgmstream->loop_end_sample = read_s32le(extradata_offset + 0x0c, sf); break; } @@ -317,8 +357,8 @@ VGMSTREAM * init_vgmstream_akb2(STREAMFILE *sf) { /* When loop_flag num_samples may be much larger than real num_samples (it's fine when looping is off) * Actual num_samples would be loop_end_sample+1, but more testing is needed */ - vgmstream->num_samples = read_32bitLE(material_offset+0x0c,sf);//num_samples; - vgmstream->loop_start_sample = read_32bitLE(material_offset+0x10,sf);//loop_start; + vgmstream->num_samples = read_s32le(material_offset+0x0c,sf);//num_samples; + vgmstream->loop_start_sample = read_s32le(material_offset+0x10,sf);//loop_start; vgmstream->loop_end_sample = loop_end; break; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/apc.c b/Frameworks/vgmstream/vgmstream/src/meta/apc.c index 25239be82..281ac5ce7 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/apc.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/apc.c @@ -1,48 +1,48 @@ -#include "meta.h" -#include "../coding/coding.h" - -/* APC - from Cryo games [MegaRace 3 (PC)] */ -VGMSTREAM * init_vgmstream_apc(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - off_t start_offset; - size_t data_size; - int loop_flag, channel_count; - - - /* checks */ - if ( !check_extensions(streamFile,"apc") ) - goto fail; - if (read_32bitBE(0x00,streamFile) != 0x4352594F) /* "CRYO" */ - goto fail; - if (read_32bitBE(0x04,streamFile) != 0x5F415043) /* "_APC" */ - goto fail; - //if (read_32bitBE(0x08,streamFile) != 0x312E3230) /* "1.20" */ - // goto fail; - - /* 0x14/18: L/R hist sample? */ - - start_offset = 0x20; - data_size = get_streamfile_size(streamFile) - start_offset; - channel_count = read_32bitLE(0x1c,streamFile) == 0 ? 1 : 2; - loop_flag = 0; - - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); - if (!vgmstream) goto fail; - - vgmstream->meta_type = meta_APC; - vgmstream->sample_rate = read_32bitLE(0x10,streamFile); - vgmstream->num_samples = ima_bytes_to_samples(data_size,channel_count); - - vgmstream->coding_type = coding_IMA; - vgmstream->layout_type = layout_none; - - if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) ) - goto fail; - return vgmstream; - -fail: - close_vgmstream(vgmstream); - return NULL; -} +#include "meta.h" +#include "../coding/coding.h" + +/* APC - from Cryo games [MegaRace 3 (PC)] */ +VGMSTREAM* init_vgmstream_apc(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + uint32_t start_offset, data_size; + int loop_flag, channels, sample_rate; + + + /* checks */ + if (!is_id32be(0x00,sf, "CRYO")) + goto fail; + if (!is_id32be(0x04,sf, "_APC")) + goto fail; + //if (!is_id32be(0x04,sf, "1.20")) + // goto fail; + + if (!check_extensions(sf,"apc")) + goto fail; + + sample_rate = read_s32le(0x10,sf); + /* 0x14/18: L/R hist sample? */ + channels = read_s32le(0x1c,sf) == 0 ? 1 : 2; + loop_flag = 0; + start_offset = 0x20; + data_size = get_streamfile_size(sf) - start_offset; + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channels, loop_flag); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_APC; + vgmstream->sample_rate = sample_rate; + vgmstream->num_samples = ima_bytes_to_samples(data_size, channels); + + vgmstream->coding_type = coding_IMA; + vgmstream->layout_type = layout_none; + + if (!vgmstream_open_stream(vgmstream, sf, start_offset)) + goto fail; + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/asf.c b/Frameworks/vgmstream/vgmstream/src/meta/asf.c index 57ecfae8d..a6105808f 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/asf.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/asf.c @@ -1,58 +1,63 @@ -#include "meta.h" -#include "../coding/coding.h" - -/* ASF - Argonaut PC games [Croc 2 (PC), Aladdin: Nasira's Revenge (PC)] */ -VGMSTREAM * init_vgmstream_asf(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - off_t start_offset; - int loop_flag, channel_count, version; - - - /* checks */ - /* .asf: original - * .lasf: fake for plugins */ - if (!check_extensions(streamFile, "asf,lasf")) - goto fail; - - if (read_32bitBE(0x00,streamFile) != 0x41534600) /* "ASF\0" */ - goto fail; - if (read_32bitBE(0x04,streamFile) != 0x02000100) - goto fail; - if (read_32bitLE(0x08,streamFile) != 0x01 && - read_32bitLE(0x0c,streamFile) != 0x18 && - read_32bitLE(0x1c,streamFile) != 0x20) - goto fail; - - version = read_32bitLE(0x28,streamFile); /* assumed? */ - switch(version){ - case 0x0d: channel_count = 1; break; /* Aladdin: Nasira's Revenge (PC) */ - case 0x0f: channel_count = 2; break; /* Croc 2 (PC), The Emperor's New Groove (PC) */ - default: goto fail; - } - - loop_flag = 0; - start_offset = 0x2c; - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count, loop_flag); - if (!vgmstream) goto fail; - - vgmstream->sample_rate = (uint16_t)read_16bitLE(0x24, streamFile); - vgmstream->meta_type = meta_ASF; - vgmstream->coding_type = coding_ASF; - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = 0x11; - vgmstream->num_samples = (get_streamfile_size(streamFile)-start_offset)/(0x11*channel_count)*32; /* bytes_to_samples */ - //vgmstream->num_samples = read_32bitLE(0x18,streamFile) * (0x20<stream_name,0x10, 0x08+1,streamFile); - - - if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) - goto fail; - return vgmstream; - -fail: - close_vgmstream(vgmstream); - return NULL; -} +#include "meta.h" +#include "../coding/coding.h" + +/* ASF - Argonaut PC games [Croc 2 (PC), Aladdin: Nasira's Revenge (PC)] */ +VGMSTREAM* init_vgmstream_asf(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + uint32_t start_offset; + int loop_flag, channels, type, sample_rate; + + + /* checks */ + if (!is_id32be(0x00,sf, "ASF\0")) + goto fail; + + /* .asf: original + * .lasf: fake for plugins */ + if (!check_extensions(sf, "asf,lasf")) + goto fail; + + if (read_u32le(0x04,sf) != 0x00010002) /* v1.002? */ + goto fail; + if (read_u32le(0x08,sf) != 0x01 && + read_u32le(0x0c,sf) != 0x18) + goto fail; + /* 0x10~18: stream name (same as filename) */ + /* 0x18: non-full size? */ + if (read_u32le(0x1c,sf) != 0x20) /* samples per frame? */ + goto fail; + sample_rate = read_u16le(0x24, sf); + + type = read_u32le(0x28,sf); /* assumed? */ + switch(type){ + case 0x0d: channels = 1; break; /* Aladdin: Nasira's Revenge (PC) */ + case 0x0f: channels = 2; break; /* Croc 2 (PC), The Emperor's New Groove (PC) */ + default: goto fail; + } + + loop_flag = 0; + start_offset = 0x2c; + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channels, loop_flag); + if (!vgmstream) goto fail; + + vgmstream->sample_rate = sample_rate; + vgmstream->meta_type = meta_ASF; + vgmstream->coding_type = coding_ASF; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x11; + vgmstream->num_samples = (get_streamfile_size(sf) - start_offset) / (0x11 * channels) * 32; /* bytes_to_samples */ + //vgmstream->num_samples = read_32bitLE(0x18,sf) * (32 << channels); /* something like this? */ + + read_string(vgmstream->stream_name,0x10, 0x08+1,sf); + + + if (!vgmstream_open_stream(vgmstream, sf, start_offset)) + goto fail; + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/bkhd.c b/Frameworks/vgmstream/vgmstream/src/meta/bkhd.c index 2b255bfd9..a76173ae5 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/bkhd.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/bkhd.c @@ -206,10 +206,13 @@ VGMSTREAM* init_vgmstream_bkhd_fx(STREAMFILE* sf) { /* Not an actual stream but typically convolution reverb models and other FX plugin helpers. * Useless but to avoid "subsong not playing" complaints. */ - if (read_u32(0x00, sf) == 0x0400 && + /* Wwise Convolution Reverb */ + if ((read_u32(0x00, sf) == 0x00000400 || /* common */ + read_u32(0x00, sf) == 0x00020400 ) && /* Elden Ring */ read_u32(0x04, sf) == 0x0800) { sample_rate = read_u32(0x08, sf); channels = read_u32(0x0c, sf) & 0xFF; /* 0x31 at 0x0d in PC, field is 32b vs X360 */ + /* 0x10: some id or small size? (related to entries?) */ /* 0x14/18: some float? */ entries = read_u32(0x1c, sf); diff --git a/Frameworks/vgmstream/vgmstream/src/meta/fsb5.c b/Frameworks/vgmstream/vgmstream/src/meta/fsb5.c index e91950135..11478bf4b 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/fsb5.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/fsb5.c @@ -477,6 +477,7 @@ VGMSTREAM* init_vgmstream_fsb5(STREAMFILE* sf) { cfg.channels = fsb5.channels; cfg.sample_rate = fsb5.sample_rate; cfg.setup_id = read_u32le(fsb5.extradata_offset,sf); + cfg.stream_end = fsb5.stream_offset + fsb5.stream_size; vgmstream->codec_data = init_vorbis_custom(sb, fsb5.stream_offset, VORBIS_FSB, &cfg); if (!vgmstream->codec_data) goto fail; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/hca_keys.h b/Frameworks/vgmstream/vgmstream/src/meta/hca_keys.h index 9a594d6b2..1b3258b43 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/hca_keys.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/hca_keys.h @@ -988,6 +988,9 @@ static const hcakey_info hcakey_list[] = { // Sonic Origins {1991062320220623}, // 000712DC525289CF + // Pachislot Gyakuten Saiban (iOS/Android) + {2220022040477322}, // 0007E319291DBE8A + }; #endif/*_HCA_KEYS_H_*/ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/kwb.c b/Frameworks/vgmstream/vgmstream/src/meta/kwb.c index 950de9a2a..04025e2a3 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/kwb.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/kwb.c @@ -39,7 +39,13 @@ VGMSTREAM* init_vgmstream_kwb(STREAMFILE* sf) { /* checks */ + if (!is_id32be(0x00, sf, "WBD_") && + !is_id32le(0x00, sf, "WBD_") && + !is_id32be(0x00, sf, "WHD1")) + goto fail; + /* .wbd+wbh: common [Bladestorm Nightmare (PC)] + * .wbd+whd: uncommon [Nights of Azure 2 (PS4)] * .wb2+wh2: newer [Nights of Azure 2 (PC)] * .sed: mixed header+data [Dissidia NT (PC)] */ if (!check_extensions(sf, "wbd,wb2,sed")) @@ -47,18 +53,20 @@ VGMSTREAM* init_vgmstream_kwb(STREAMFILE* sf) { /* open companion header */ - if (check_extensions(sf, "wbd")) { + if (is_id32be(0x00, sf, "WHD1")) { /* .sed */ + sf_h = sf; + sf_b = sf; + } + else if (check_extensions(sf, "wbd")) { sf_h = open_streamfile_by_ext(sf, "wbh"); + if (!sf_h) + sf_h = open_streamfile_by_ext(sf, "whd"); sf_b = sf; } else if (check_extensions(sf, "wb2")) { sf_h = open_streamfile_by_ext(sf, "wh2"); sf_b = sf; } - else if (check_extensions(sf, "sed")) { - sf_h = sf; - sf_b = sf; - } else { goto fail; } @@ -378,14 +386,14 @@ fail: return 0; } -static int parse_type_k4hd(kwb_header* kwb, off_t offset, off_t body_offset, STREAMFILE* sf_h) { +static int parse_type_k4hd_pvhd(kwb_header* kwb, off_t offset, off_t body_offset, STREAMFILE* sf_h) { off_t ppva_offset, header_offset; int entries, current_subsongs, relative_subsong; size_t entry_size; /* a format mimicking PSVita's hd4+bd4 format */ - /* 00: K4HD id */ + /* 00: K4HD/PVHD id */ /* 04: chunk size */ /* 08: ? */ /* 0c: ? */ @@ -565,7 +573,7 @@ static int parse_kwb(kwb_header* kwb, STREAMFILE* sf_h, STREAMFILE* sf_b) { uint32_t (*read_u32)(off_t,STREAMFILE*) = NULL; - if (read_u32be(0x00, sf_h) == 0x57484431) { /* "WHD1" */ + if (is_id32be(0x00, sf_h, "WHD1")) { /* container of fused .wbh+wbd */ /* 0x04: fixed value? */ kwb->big_endian = read_u8(0x08, sf_h) == 0xFF; @@ -617,7 +625,8 @@ static int parse_kwb(kwb_header* kwb, STREAMFILE* sf_h, STREAMFILE* sf_b) { break; case 0x4B344844: /* "K4HD" [Dissidia NT (PS4), (Vita) */ - if (!parse_type_k4hd(kwb, head_offset, body_offset, sf_h)) + case 0x50564844: /* "PVHD" [Nights of Azure 2 (PS4)] */ + if (!parse_type_k4hd_pvhd(kwb, head_offset, body_offset, sf_h)) goto fail; break; @@ -632,6 +641,7 @@ static int parse_kwb(kwb_header* kwb, STREAMFILE* sf_h, STREAMFILE* sf_b) { break; default: + vgm_logi("KWB: unknown type\n"); goto fail; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/s14_sss.c b/Frameworks/vgmstream/vgmstream/src/meta/s14_sss.c index 71b87cf0d..921e4e0d3 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/s14_sss.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/s14_sss.c @@ -3,48 +3,45 @@ #include "../coding/coding.h" #include -/* .s14 and .sss - headerless siren14 stream (The Idolm@ster DS, Korogashi Puzzle Katamari Damacy DS) */ -VGMSTREAM * init_vgmstream_s14_sss(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; +static int test_interleave(STREAMFILE* sf, int channels, int interleave); + +/* .s14/.sss - headerless siren14 stream [The Idolm@ster (DS), Korogashi Puzzle Katamari Damacy (DS), Taiko no Tatsujin DS 1/2 (DS)] */ +VGMSTREAM* init_vgmstream_s14_sss(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; off_t start_offset = 0; - int channel_count, loop_flag = 0, interleave; + int channels, loop_flag = 0, interleave; /* check extension */ - if (check_extensions(streamFile,"sss")) { - channel_count = 2; - } else if (check_extensions(streamFile,"s14")) { - channel_count = 1; //todo missing dual _0ch.s14 _1ch.s14, but dual_ext thing doesn't work properly with siren14 decoder + if (check_extensions(sf,"sss")) { + channels = 2; + } else if (check_extensions(sf,"s14")) { + channels = 1; /* may have dual _0ch.s14 + _1ch.s14, needs .txtp */ } else { goto fail; } - /* raw siren comes in 3 frame sizes, try to guess the correct one - * (should try to decode and check the error flag but it isn't currently reported) */ + /* raw siren comes in 3 frame sizes, try to guess the correct one */ { - char filename[PATH_LIMIT]; - streamFile->get_name(streamFile,filename,sizeof(filename)); - - /* horrid but I ain't losing sleep over it (besides the header is often incrusted in-code as some tracks loop) */ - if (strstr(filename,"S037")==filename || strstr(filename,"b06")==filename || /* Korogashi Puzzle Katamari Damacy */ - strstr(filename,"_48kbps")!=NULL) /* Taiko no Tatsujin DS 1/2 */ + /* horrid but ain't losing sleep over it (besides the header is often incrusted in-code as some tracks loop) + * Katamari, Taiko = 0x78/0x50, idolmaster=0x3c (usually but can be any) */ + if (test_interleave(sf, channels, 0x78)) interleave = 0x78; - else if (strstr(filename,"32700")==filename || /* Hottarake no Shima - Kanata to Nijiiro no Kagami */ - strstr(filename,"b0")==filename || strstr(filename,"puzzle")==filename || strstr(filename,"M09")==filename || /* Korogashi Puzzle Katamari Damacy */ - strstr(filename,"_32kbps")!=NULL) /* Taiko no Tatsujin DS 1/2 */ - interleave = 0x50; + else if (test_interleave(sf, channels, 0x50)) + interleave = 0x50; + else if (test_interleave(sf, channels, 0x3c)) + interleave = 0x3c; else - interleave = 0x3c; /* The Idolm@ster - Dearly Stars */ + goto fail; } - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); + vgmstream = allocate_vgmstream(channels,loop_flag); if (!vgmstream) goto fail; - vgmstream->num_samples = get_streamfile_size(streamFile) / (interleave * channel_count) * (32000/50); - vgmstream->sample_rate = 32768; /* maybe 32700? */ + vgmstream->num_samples = get_streamfile_size(sf) / (interleave * channels) * (32000/50); + vgmstream->sample_rate = 32768; - vgmstream->meta_type = channel_count==1 ? meta_S14 : meta_SSS; + vgmstream->meta_type = channels==1 ? meta_S14 : meta_SSS; vgmstream->layout_type = layout_interleave; vgmstream->interleave_block_size = interleave; @@ -58,7 +55,7 @@ VGMSTREAM * init_vgmstream_s14_sss(STREAMFILE *streamFile) { #endif } - if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + if (!vgmstream_open_stream(vgmstream, sf, start_offset)) goto fail; return vgmstream; @@ -66,3 +63,27 @@ fail: close_vgmstream(vgmstream); return NULL; } + +/* pretty gross (should use TXTH), but codec info seems to be in hard-to-locate places/exe + * and varies per file, so for now autodetect possible types. could also check if data_size matches interleave */ +static int test_interleave(STREAMFILE* sf, int channels, int interleave) { +#ifdef VGM_USE_G7221 + int res; + g7221_codec_data* data = init_g7221(channels, interleave); + if (!data) goto fail; + + set_key_g7221(data, NULL); /* force test key */ + + /* though this is mainly for key testing, with no key can be used to test frames too */ + res = test_key_g7221(data, 0x00, sf); + if (res <= 0) goto fail; + + free_g7221(data); + return 1; +fail: + free_g7221(data); + return 0; +#else + return 0; +#endif +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/sgxd.c b/Frameworks/vgmstream/vgmstream/src/meta/sgxd.c index 0a3122c44..c7324302f 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/sgxd.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/sgxd.c @@ -32,7 +32,7 @@ VGMSTREAM* init_vgmstream_sgxd(STREAMFILE* sf) { /* checks */ /* .sgx: header+data (Genji) * .sgd: header+data (common) - * .sgh+sgd: header+data */ + * .sgh+sgd: header+data (streams) */ if (!check_extensions(sf,"sgx,sgd,sgb")) goto fail; @@ -73,7 +73,32 @@ VGMSTREAM* init_vgmstream_sgxd(STREAMFILE* sf) { } - /* typical chunks: WAVE, RGND, NAME (strings for WAVE or RGND), SEQD (related to SFX), WSUR, WMKR, BUSS */ + /* Format per chunk: + * - 0x00: id + * - 0x04: SGX: unknown; SGD/SGH: chunk length + * - 0x08: null + * - 0x0c: entries */ + + /* typical chunks (with some entry info): + * - WAVE: wave data (see below) + * - RGND: programs info, notably: + * - 0x18: min note range + * - 0x19: max note range + * - 0x1C: root note + * - 0x34: WAVE id + * > sample_rate = wave_sample_rate * (2 ^ (1/12)) ^ (target_note - root_note) + * - NAME: strings for other chunks + * - 0x00: sub-id? + * - 0x02: type? (possibly: 0000=bank, 0x2xxx=SEQD/WAVE, 0x3xxx=WSUR, 0x4xxx=BUSS, 0x6xxx=CONF) + * - 0x04: absolute offset + * - SEQD: related to SFX (sequences?), entries seem to be offsets to name offset + sequence offset + * > sequence format seems to be 1 byte type (0=sfx, 1=music) + midi without header + * (default tick resolution of 960 pulses per quarter note) + * - WSUR: ? + * - WMKR: ? + * - CONF: ? (name offset + config offset) + * - BUSS: bus config? + /* WAVE chunk (size 0x10 + files * 0x38 + optional padding) */ if (is_sgx) { /* position after chunk+size */ if (!is_id32be(0x10,sf_head, "WAVE")) @@ -83,7 +108,6 @@ VGMSTREAM* init_vgmstream_sgxd(STREAMFILE* sf) { if (!find_chunk_le(sf_head, get_id32be("WAVE"),0x10,0, &chunk_offset, NULL)) goto fail; } - /* 0x04 SGX: unknown; SGD/SGH: chunk length, 0x08 null */ /* check multi-streams (usually only SE containers; Puppeteer) */ total_subsongs = read_s32le(chunk_offset+0x04,sf_head); @@ -95,18 +119,18 @@ VGMSTREAM* init_vgmstream_sgxd(STREAMFILE* sf) { uint32_t stream_offset; chunk_offset += 0x08 + 0x38 * (target_subsong-1); /* position in target header*/ - /* 0x00 ? (00/01/02) */ + /* 0x00: ? (00/01/02) */ if (!is_sgx) /* meaning unknown in .sgx; offset 0 = not a stream (a RGND sample) */ name_offset = read_u32le(chunk_offset+0x04,sf_head); codec = read_u8(chunk_offset+0x08,sf_head); channels = read_u8(chunk_offset+0x09,sf_head); - /* 0x0a null */ + /* 0x0a: null */ sample_rate = read_s32le(chunk_offset+0x0c,sf_head); /* 0x10: info_type, meaning of the next value * (00=null, 30/40=data size without padding (ADPCM, ATRAC3plus), 80/A0=block size (AC3) */ /* 0x14: info_value (see above) */ - /* 0x18: unknown (ex. 0x0008/0010/3307/CC02/etc)x2 */ + /* 0x18: unknown (ex. 0x0008/0010/3307/CC02/etc, RGND related?) x2 */ /* 0x1c: null */ num_samples = read_s32le(chunk_offset+0x20,sf_head); diff --git a/Frameworks/vgmstream/vgmstream/src/meta/sndz.c b/Frameworks/vgmstream/vgmstream/src/meta/sndz.c index 67432b8e1..f23c9eccc 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/sndz.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/sndz.c @@ -6,7 +6,7 @@ VGMSTREAM* init_vgmstream_sndz(STREAMFILE* sf) { VGMSTREAM* vgmstream = NULL; STREAMFILE* sf_b = NULL; - uint32_t stream_offset, stream_size, name_offset, head_size, data_size; + uint32_t stream_offset, stream_size, name_offset, data_size; int channels, loop_flag, sample_rate, codec, streamed; int32_t num_samples, loop_start, loop_end; uint32_t at9_config; @@ -16,7 +16,7 @@ VGMSTREAM* init_vgmstream_sndz(STREAMFILE* sf) { if (!is_id32be(0x00, sf, "SNDZ")) goto fail; - head_size = read_u32le(0x04, sf); + //head_size = read_u32le(0x04, sf); data_size = read_u32le(0x08, sf); /* 0x0c: version? (0x00010001) */ /* 0x10: size size? */ @@ -26,8 +26,8 @@ VGMSTREAM* init_vgmstream_sndz(STREAMFILE* sf) { /* .szd1: header + .szd2 = data - * .szd3: szd1 + szd2 */ - if (!check_extensions(sf, "szd1,szd3")) + * .szd/szd3: szd1 + szd2 */ + if (!check_extensions(sf, "szd1,szd,szd3")) goto fail; /* parse chunk table and WAVS with offset to offset to WAVD */ @@ -95,7 +95,6 @@ VGMSTREAM* init_vgmstream_sndz(STREAMFILE* sf) { loop_flag = loop_end > 0; } -VGM_LOG("%i, %x, %x, %x\n", streamed, head_size, data_size, get_streamfile_size(sf)); /* szd3 is streamed but has header+data together, with padding between (data_size is the same as file size)*/ if (streamed && get_streamfile_size(sf) < data_size) { sf_b = open_streamfile_by_ext(sf, "szd2"); diff --git a/Frameworks/vgmstream/vgmstream/src/meta/sqex_sead.c b/Frameworks/vgmstream/vgmstream/src/meta/sqex_sead.c index f1e30be72..7aa4547a2 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/sqex_sead.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/sqex_sead.c @@ -1,6 +1,6 @@ #include "meta.h" #include "../coding/coding.h" -#include "sqex_sead_streamfile.h" +#include "sqex_streamfile.h" typedef struct { @@ -278,7 +278,7 @@ VGMSTREAM* init_vgmstream_sqex_sead(STREAMFILE* sf) { /* 0x0e: reserved x2 */ /* 0x10+ HCA header */ - temp_sf = setup_sqex_sead_streamfile(sf, subfile_offset, subfile_size, encryption, header_size, key_start); + temp_sf = setup_sqex_streamfile(sf, subfile_offset, subfile_size, encryption, header_size, key_start, "hca"); if (!temp_sf) goto fail; temp_vgmstream = init_vgmstream_hca(temp_sf); diff --git a/Frameworks/vgmstream/vgmstream/src/meta/sqex_sead_streamfile.h b/Frameworks/vgmstream/vgmstream/src/meta/sqex_streamfile.h similarity index 83% rename from Frameworks/vgmstream/vgmstream/src/meta/sqex_sead_streamfile.h rename to Frameworks/vgmstream/vgmstream/src/meta/sqex_streamfile.h index 25f567c81..c82298242 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/sqex_sead_streamfile.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/sqex_streamfile.h @@ -6,11 +6,11 @@ typedef struct { size_t start; size_t key_start; -} sqex_sead_io_data; +} sqex_io_data; -static size_t sqex_sead_io_read(STREAMFILE *sf, uint8_t *dest, off_t offset, size_t length, sqex_sead_io_data* data) { - /* Found in FFXII_TZA.exe (same key in SCD Ogg V3) */ +static size_t sqex_io_read(STREAMFILE *sf, uint8_t *dest, off_t offset, size_t length, sqex_io_data* data) { + /* Found in FFXII_TZA.exe (same key in SCD Ogg V3), also in AKB's sdlib as "EncKey" */ static const uint8_t key[0x100] = { 0x3A,0x32,0x32,0x32,0x03,0x7E,0x12,0xF7,0xB2,0xE2,0xA2,0x67,0x32,0x32,0x22,0x32, // 00-0F 0x32,0x52,0x16,0x1B,0x3C,0xA1,0x54,0x7B,0x1B,0x97,0xA6,0x93,0x1A,0x4B,0xAA,0xA6, // 10-1F @@ -45,22 +45,22 @@ static size_t sqex_sead_io_read(STREAMFILE *sf, uint8_t *dest, off_t offset, siz } /* decrypts subfile if needed */ -static STREAMFILE* setup_sqex_sead_streamfile(STREAMFILE* sf, off_t subfile_offset, size_t subfile_size, int encryption, size_t header_size, size_t key_start) { +static STREAMFILE* setup_sqex_streamfile(STREAMFILE* sf, off_t subfile_offset, size_t subfile_size, int encryption, size_t header_size, size_t key_start, const char* ext) { STREAMFILE* new_sf = NULL; /* setup sf */ new_sf = open_wrap_streamfile(sf); new_sf = open_clamp_streamfile_f(new_sf, subfile_offset, subfile_size); if (encryption) { - sqex_sead_io_data io_data = {0}; + sqex_io_data io_data = {0}; io_data.start = header_size; io_data.key_start = key_start; - new_sf = open_io_streamfile_f(new_sf, &io_data, sizeof(sqex_sead_io_data), sqex_sead_io_read, NULL); + new_sf = open_io_streamfile_f(new_sf, &io_data, sizeof(sqex_io_data), sqex_io_read, NULL); } - new_sf = open_fakename_streamfile_f(new_sf, NULL, "hca"); + new_sf = open_fakename_streamfile_f(new_sf, NULL, ext); return new_sf; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/swav.c b/Frameworks/vgmstream/vgmstream/src/meta/swav.c index ba1c5eaf0..f487e4cb1 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/swav.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/swav.c @@ -5,35 +5,52 @@ /* SWAV - wave files generated by the DS SDK */ VGMSTREAM* init_vgmstream_swav(STREAMFILE* sf) { VGMSTREAM* vgmstream = NULL; - int channel_count, loop_flag; - off_t start_offset; - int codec_number, bits_per_sample; + uint32_t start_offset, data_size, file_size; + int channels, loop_flag, codec, bits_per_sample, sample_rate; + int32_t loop_start, loop_end; coding_t coding_type; + /* checks */ + if (!is_id32be(0x00,sf, "SWAV")) + goto fail; + /* .swav: standard * .adpcm: Merlin - A Servant of Two Masters (DS) */ if (!check_extensions(sf, "swav,adpcm")) goto fail; - if (read_u32be(0x00,sf) != 0x53574156) /* "SWAV" */ - goto fail; - if (read_u32be(0x10,sf) != 0x44415441) /* "DATA" */ - goto fail; + /* 0x04: BOM mark */ + /* 0x06: version? (1.00) */ + file_size = read_u32le(0x08,sf); + /* 0x0c: always 16? */ + /* 0x0e: always 1? */ - /* check type details */ - codec_number = read_8bit(0x18,sf); - loop_flag = read_8bit(0x19,sf); + if (!is_id32be(0x10,sf, "DATA")) + goto fail; + data_size = read_u32le(0x14,sf); + codec = read_u8(0x18,sf); + loop_flag = read_u8(0x19,sf); + sample_rate = read_u16le(0x1A,sf); + /* 0x1c: related to size? */ + loop_start = read_u16le(0x1E,sf); + loop_end = read_s32le(0x20,sf); - channel_count = 1; - if (get_streamfile_size(sf) != read_s32le(0x08,sf)) { - if (get_streamfile_size(sf) != (read_s32le(0x08,sf) - 0x24) * 2 + 0x24) + start_offset = 0x24; + + /* strange values found in Face Training (DSi) samples, may be pitch/etc reference info? (samples sounds ok like this) */ + if (sample_rate < 0x2000) + sample_rate = 44100; + + channels = 1; + if (get_streamfile_size(sf) != file_size) { + if (get_streamfile_size(sf) != (file_size - 0x24) * 2 + 0x24) goto fail; - channel_count = 2; + channels = 2; } - switch (codec_number) { + switch (codec) { case 0: coding_type = coding_PCM8; bits_per_sample = 8; @@ -49,18 +66,17 @@ VGMSTREAM* init_vgmstream_swav(STREAMFILE* sf) { default: goto fail; } - start_offset = 0x24; /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count, loop_flag); + vgmstream = allocate_vgmstream(channels, loop_flag); if (!vgmstream) goto fail; - vgmstream->num_samples = (read_s32le(0x14,sf) - 0x14) * 8 / bits_per_sample; - vgmstream->sample_rate = read_u16le(0x1A,sf); + vgmstream->num_samples = (data_size - 0x14) * 8 / bits_per_sample; + vgmstream->sample_rate = sample_rate; if (loop_flag) { - vgmstream->loop_start_sample = read_u16le(0x1E,sf) * 32 / bits_per_sample; - vgmstream->loop_end_sample = read_s32le(0x20,sf) * 32 / bits_per_sample + vgmstream->loop_start_sample; + vgmstream->loop_start_sample = loop_start * 32 / bits_per_sample; + vgmstream->loop_end_sample = loop_end * 32 / bits_per_sample + vgmstream->loop_start_sample; } if (coding_type == coding_IMA_int) { @@ -71,18 +87,18 @@ VGMSTREAM* init_vgmstream_swav(STREAMFILE* sf) { { int i; - for (i = 0; i < channel_count; i++) { + for (i = 0; i < channels; i++) { vgmstream->ch[i].adpcm_history1_32 = read_s16le(start_offset + 0 + 4*i, sf); vgmstream->ch[i].adpcm_step_index = read_s16le(start_offset + 2 + 4*i, sf); } } - start_offset += 4 * channel_count; + start_offset += 4 * channels; } vgmstream->coding_type = coding_type; vgmstream->meta_type = meta_SWAV; - if (channel_count == 2) { + if (channels == 2) { vgmstream->layout_type = layout_interleave; vgmstream->interleave_block_size = 1; } else { diff --git a/Frameworks/vgmstream/vgmstream/src/meta/vag.c b/Frameworks/vgmstream/vgmstream/src/meta/vag.c index ff4e1c039..623b397cf 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/vag.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/vag.c @@ -52,8 +52,8 @@ VGMSTREAM* init_vgmstream_vag(STREAMFILE* sf) { /* check variation */ switch(vag_id) { - case 0x56414731: /* "VAG1" [Metal Gear Solid 3 (PS2), Cabela's African Safari (PSP)] */ - meta_type = meta_PS2_VAG1; //TODO not always Konami + case 0x56414731: /* "VAG1" [Metal Gear Solid 3 (PS2), Cabela's African Safari (PSP), Shamu's Deep Sea Adventures (PS2)] */ + meta_type = meta_PS2_VAG1; //TODO not always Konami (Sand Grain Studios) start_offset = 0x40; /* 0x30 is extra data in VAG1 */ interleave = 0x10; loop_flag = 0; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/wave.c b/Frameworks/vgmstream/vgmstream/src/meta/wave.c index 0e7feead2..554a97438 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/wave.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/wave.c @@ -1,71 +1,71 @@ #include "meta.h" +#include "../util/endianness.h" #include "../coding/coding.h" /* .WAVE - WayForward "EngineBlack" games [Mighty Switch Force! (3DS), Adventure Time: Hey Ice King! Why'd You Steal Our Garbage?! (3DS)] */ -VGMSTREAM * init_vgmstream_wave(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - off_t start_offset, extradata_offset; - int loop_flag = 0, channel_count, sample_rate, codec; - int32_t num_samples, loop_start = 0, loop_end = 0; - size_t interleave; +VGMSTREAM* init_vgmstream_wave(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + uint32_t start_offset, extradata_offset, interleave; + int channels, loop_flag, sample_rate, codec; + int32_t num_samples, loop_start, loop_end; int big_endian; - int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL; - float (*read_f32)(off_t,STREAMFILE*) = NULL; + read_u32_t read_u32; + read_s32_t read_s32; + read_f32_t read_f32; + /* checks */ - if (!check_extensions(streamFile, "wave")) + if (!is_id32be(0x00,sf, "VAW3") && /* Happy Feet Two (3DS) */ + read_u32le(0x00,sf) != 0xE5B7ECFE && /* common (LE) */ + read_u32be(0x00,sf) != 0xE5B7ECFE) /* used? */ goto fail; + /* 0x04: version? common=0, VAW3=2 */ - if (read_32bitLE(0x00,streamFile) != 0xE5B7ECFE && /* header id */ - read_32bitBE(0x00,streamFile) != 0xE5B7ECFE) - goto fail; - if (read_32bitBE(0x04,streamFile) != 0x00) /* version? */ + if (!check_extensions(sf, "wave")) goto fail; /* assumed */ - big_endian = read_32bitBE(0x00,streamFile) == 0xE5B7ECFE; + big_endian = read_u32be(0x00,sf) == 0xE5B7ECFE; if (big_endian) { - read_32bit = read_32bitBE; + read_u32 = read_u32be; + read_s32 = read_s32be; read_f32 = read_f32be; } else { - read_32bit = read_32bitLE; + read_u32 = read_u32le; + read_s32 = read_s32le; read_f32 = read_f32le; } - channel_count = read_8bit(0x05,streamFile); - - if (read_32bit(0x08,streamFile) != get_streamfile_size(streamFile)) - goto fail; - if (read_8bit(0x0c,streamFile) != 0x00) /* ? */ + if (read_u32(0x08,sf) != get_streamfile_size(sf)) goto fail; - sample_rate = (int)read_f32(0x0c, streamFile); /* sample rate in 32b float (WHY?) */ - num_samples = read_32bit(0x10, streamFile); - loop_start = read_32bit(0x14, streamFile); - loop_end = read_32bit(0x18, streamFile); + sample_rate = (int)read_f32(0x0c, sf); /* WHY */ + num_samples = read_s32(0x10, sf); + loop_start = read_s32(0x14, sf); + loop_end = read_s32(0x18, sf); - codec = read_8bit(0x1c, streamFile); - channel_count = read_8bit(0x1d, streamFile); - if (read_8bit(0x1e, streamFile) != 0x00) goto fail; /* unknown */ - if (read_8bit(0x1f, streamFile) != 0x00) goto fail; /* unknown */ + codec = read_u8(0x1c, sf); + channels = read_u8(0x1d, sf); + if (read_u8(0x1e, sf) != 0x00) goto fail; /* unknown */ + if (read_u8(0x1f, sf) != 0x00) goto fail; /* unknown */ - start_offset = read_32bit(0x20, streamFile); - interleave = read_32bit(0x24, streamFile); /* typically half data_size */ - extradata_offset = read_32bit(0x28, streamFile); /* OR: extradata size (0x2c) */ + start_offset = read_u32(0x20, sf); + interleave = read_u32(0x24, sf); /* typically half data_size */ + extradata_offset = read_u32(0x28, sf); /* OR: extradata size (always 0x2c) */ loop_flag = (loop_start > 0); /* some songs (ex. Adventure Time's m_candykingdom_overworld.wave) do full loops, but there is no way * to tell them apart from sfx/voices, so we try to detect if it's long enough. */ if(!loop_flag && loop_start == 0 && loop_end == num_samples /* full loop */ - && channel_count > 1 + && channels > 1 && num_samples > 20*sample_rate) { /* in seconds */ loop_flag = 1; } /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count, loop_flag); + vgmstream = allocate_vgmstream(channels, loop_flag); if (!vgmstream) goto fail; vgmstream->sample_rate = sample_rate; @@ -74,6 +74,7 @@ VGMSTREAM * init_vgmstream_wave(STREAMFILE *streamFile) { vgmstream->loop_end_sample = loop_end; vgmstream->meta_type = meta_WAVE; + /* not sure if there are other codecs but anyway */ switch(codec) { case 0x02: @@ -82,14 +83,14 @@ VGMSTREAM * init_vgmstream_wave(STREAMFILE *streamFile) { vgmstream->interleave_block_size = interleave; /* ADPCM setup: 0x20 coefs + 0x06 initial ps/hist1/hist2 + 0x06 loop ps/hist1/hist2, per channel */ - dsp_read_coefs(vgmstream, streamFile, extradata_offset+0x00, 0x2c, big_endian); - dsp_read_hist(vgmstream, streamFile, extradata_offset+0x22, 0x2c, big_endian); + dsp_read_coefs(vgmstream, sf, extradata_offset+0x00, 0x2c, big_endian); + dsp_read_hist(vgmstream, sf, extradata_offset+0x22, 0x2c, big_endian); break; default: goto fail; } - if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + if (!vgmstream_open_stream(vgmstream,sf,start_offset)) goto fail; return vgmstream; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/wwise.c b/Frameworks/vgmstream/vgmstream/src/meta/wwise.c index 093ff448f..a1e16a272 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/wwise.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/wwise.c @@ -172,6 +172,7 @@ VGMSTREAM* init_vgmstream_wwise_bnk(STREAMFILE* sf, int* p_prefetch) { cfg.channels = ww.channels; cfg.sample_rate = ww.sample_rate; cfg.big_endian = ww.big_endian; + cfg.stream_end = ww.data_offset + ww.data_size; if (ww.block_size != 0 || ww.bits_per_sample != 0) goto fail; /* always 0 for Worbis */ @@ -237,7 +238,7 @@ VGMSTREAM* init_vgmstream_wwise_bnk(STREAMFILE* sf, int* p_prefetch) { uint32_t setup_id = read_u32be(start_offset + setup_offset + 0x06, sf); /* if the setup after header starts with "(data)BCV" it's an inline codebook) */ - if ((setup_id & 0x00FFFFFF) == 0x00424356) { /* 0"BCV" */ + if ((setup_id & 0x00FFFFFF) == get_id32be("\0BCV")) { cfg.setup_type = WWV_FULL_SETUP; } /* if the setup is suspiciously big it's probably trimmed inline codebooks */ @@ -582,7 +583,7 @@ VGMSTREAM* init_vgmstream_wwise_bnk(STREAMFILE* sf, int* p_prefetch) { case mapping_2POINT1_xiph: /* 2ch+1ch, 2 streams */ case mapping_STEREO: cfg.coupled_count = 1; break; /* 2ch, 1 stream */ default: cfg.coupled_count = 0; break; /* 1ch, 1 stream */ - //TODO: AK OPUS doesn't seem to handles others mappings, though AK's .h imply they exist (uses 0 coupleds?) + //TODO: AK OPUS doesn't seem to handle others mappings, though AK's .h imply they exist (uses 0 coupleds?) } /* total number internal OPUS streams (should be >0) */ @@ -650,6 +651,11 @@ VGMSTREAM* init_vgmstream_wwise_bnk(STREAMFILE* sf, int* p_prefetch) { vgmstream->layout_type = layout_none; vgmstream->num_samples = read_s32(ww.fmt_offset + 0x1c, sf); + + if (ww.prefetch) { + vgmstream->num_samples = atrac9_bytes_to_samples_cfg(ww.file_size - start_offset, cfg.config_data); + } + break; } #endif @@ -932,8 +938,8 @@ static int parse_wwise(STREAMFILE* sf, wwise_header* ww) { } if (ww->codec == PCM || ww->codec == IMA || ww->codec == VORBIS || ww->codec == DSP || ww->codec == XMA2 || - ww->codec == OPUSNX || ww->codec == OPUS || ww->codec == OPUSWW || ww->codec == PTADPCM || ww->codec == XWMA) { - ww->prefetch = 1; /* only seen those, probably all exist (XWMA, AAC, HEVAG, ATRAC9?) */ + ww->codec == OPUSNX || ww->codec == OPUS || ww->codec == OPUSWW || ww->codec == PTADPCM || ww->codec == XWMA || ww->codec == ATRAC9) { + ww->prefetch = 1; /* only seen those, probably all exist (missing XWMA, AAC, HEVAG) */ } else { vgm_logi("WWISE: wrong expected size, maybe prefetch (report)\n"); goto fail; @@ -945,7 +951,7 @@ static int parse_wwise(STREAMFILE* sf, wwise_header* ww) { * From init bank and CAkSound's sources, those may be piped through their plugins. They come in * .opuspak (no names), have wrong riff/data sizes and only seem used for sfx (other audio is Vorbis). */ if (ww->format == 0xFFFE && ww->prefetch) { - if (read_u32be(ww->data_offset + 0x00, sf) == 0x4F676753) { + if (is_id32be(ww->data_offset + 0x00, sf, "OggS")) { ww->codec = OPUSCPR; } } diff --git a/Frameworks/vgmstream/vgmstream/src/plugins.c b/Frameworks/vgmstream/vgmstream/src/plugins.c index 998fee803..bee6839d4 100644 --- a/Frameworks/vgmstream/vgmstream/src/plugins.c +++ b/Frameworks/vgmstream/vgmstream/src/plugins.c @@ -21,8 +21,13 @@ int vgmstream_ctx_is_valid(const char* filename, vgmstream_ctx_valid_cfg *cfg) { extension = filename_extension(filename); } - /* some metas accept extensionless files */ + /* some metas accept extensionless files, but make sure it's not a path */ if (strlen(extension) <= 0) { + int len = strlen(filename); + if (len <= 0) + return 0; + if (filename[len - 1] == '/' || filename[len - 1] == '\\') + return 0; return !cfg->reject_extensionless; } diff --git a/Info.plist.template b/Info.plist.template index 18c992304..6114c4018 100644 --- a/Info.plist.template +++ b/Info.plist.template @@ -112,6 +112,7 @@ 2dx9 2pfs 3do + 3ds 4 8 800 @@ -585,6 +586,7 @@ sxd sxd2 sxd3 + szd szd1 szd3 tad