diff --git a/Frameworks/vgmstream/libvgmstream.xcodeproj/project.pbxproj b/Frameworks/vgmstream/libvgmstream.xcodeproj/project.pbxproj index 23eaa4a82..c4d05f8e6 100644 --- a/Frameworks/vgmstream/libvgmstream.xcodeproj/project.pbxproj +++ b/Frameworks/vgmstream/libvgmstream.xcodeproj/project.pbxproj @@ -504,6 +504,10 @@ 838BDB7B1D3B1FC20022CA6F /* CoreMedia.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 838BDB7A1D3B1FC20022CA6F /* CoreMedia.framework */; }; 838BDB7D1D3B1FCC0022CA6F /* CoreVideo.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 838BDB7C1D3B1FCC0022CA6F /* CoreVideo.framework */; }; 838BDB7F1D3B1FD10022CA6F /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 838BDB7E1D3B1FD10022CA6F /* Cocoa.framework */; }; + 8399335B2591E896001855AF /* vorbis_custom_utils_awc.c in Sources */ = {isa = PBXBuildFile; fileRef = 839933572591E896001855AF /* vorbis_custom_utils_awc.c */; }; + 8399335F2591E8C1001855AF /* ifs.c in Sources */ = {isa = PBXBuildFile; fileRef = 8399335C2591E8C0001855AF /* ifs.c */; }; + 839933602591E8C1001855AF /* ubi_sb_garbage_streamfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 8399335D2591E8C0001855AF /* ubi_sb_garbage_streamfile.h */; }; + 839933612591E8C1001855AF /* sbk.c in Sources */ = {isa = PBXBuildFile; fileRef = 8399335E2591E8C0001855AF /* sbk.c */; }; 83997F5B22D9569E00633184 /* rad.c in Sources */ = {isa = PBXBuildFile; fileRef = 83997F5722D9569E00633184 /* rad.c */; }; 839B54521EEE1D9600048A2D /* ngc_ulw.c in Sources */ = {isa = PBXBuildFile; fileRef = 831BD11F1EEE1CF200198540 /* ngc_ulw.c */; }; 839E21E01F2EDAF100EE54D7 /* vorbis_custom_data_fsb.h in Headers */ = {isa = PBXBuildFile; fileRef = 839E21D61F2EDAF000EE54D7 /* vorbis_custom_data_fsb.h */; }; @@ -1243,6 +1247,10 @@ 838BDB7A1D3B1FC20022CA6F /* CoreMedia.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreMedia.framework; path = System/Library/Frameworks/CoreMedia.framework; sourceTree = SDKROOT; }; 838BDB7C1D3B1FCC0022CA6F /* CoreVideo.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreVideo.framework; path = System/Library/Frameworks/CoreVideo.framework; sourceTree = SDKROOT; }; 838BDB7E1D3B1FD10022CA6F /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; }; + 839933572591E896001855AF /* vorbis_custom_utils_awc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = vorbis_custom_utils_awc.c; sourceTree = ""; }; + 8399335C2591E8C0001855AF /* ifs.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ifs.c; sourceTree = ""; }; + 8399335D2591E8C0001855AF /* ubi_sb_garbage_streamfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ubi_sb_garbage_streamfile.h; sourceTree = ""; }; + 8399335E2591E8C0001855AF /* sbk.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = sbk.c; sourceTree = ""; }; 83997F5722D9569E00633184 /* rad.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = rad.c; sourceTree = ""; }; 839E21D61F2EDAF000EE54D7 /* vorbis_custom_data_fsb.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = vorbis_custom_data_fsb.h; sourceTree = ""; }; 839E21D71F2EDAF000EE54D7 /* vorbis_custom_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = vorbis_custom_decoder.c; sourceTree = ""; }; @@ -1598,6 +1606,7 @@ 839E21DC1F2EDAF000EE54D7 /* vorbis_custom_data_wwise.h */, 839E21D71F2EDAF000EE54D7 /* vorbis_custom_decoder.c */, 839E21DB1F2EDAF000EE54D7 /* vorbis_custom_decoder.h */, + 839933572591E896001855AF /* vorbis_custom_utils_awc.c */, 839E21DA1F2EDAF000EE54D7 /* vorbis_custom_utils_fsb.c */, 83E56BA01F2EE3500026BC60 /* vorbis_custom_utils_ogl.c */, 839E21DF1F2EDAF000EE54D7 /* vorbis_custom_utils_sk.c */, @@ -1787,6 +1796,7 @@ 834FE0DF215C79EB000A5D3D /* hd3_bd3.c */, 836F6E5318BDC2180095E648 /* his.c */, 834FE0E0215C79EB000A5D3D /* idsp_ie.c */, + 8399335C2591E8C0001855AF /* ifs.c */, 83C7280922BC893C00678B4A /* ikm.c */, 837CEAE623487F2B00E62A4A /* ima.c */, 832BF81121E05149006F50F1 /* imc.c */, @@ -1980,6 +1990,7 @@ 836F6EEB18BDC2190095E648 /* sat_baka.c */, 836F6EEC18BDC2190095E648 /* sat_dvi.c */, 836F6EED18BDC2190095E648 /* sat_sap.c */, + 8399335E2591E8C0001855AF /* sbk.c */, 8349A8F51FE6257D00E26435 /* scd_pcm.c */, 836F6EEE18BDC2190095E648 /* sd9.c */, 834FE0E6215C79EC000A5D3D /* sdf.c */, @@ -2031,6 +2042,7 @@ 83031ED4243C510400C3F3E0 /* ubi_lyn_streamfile.h */, 8306B0CA2098458E000302D4 /* ubi_lyn.c */, 831BA6131EAC61A500CF89B0 /* ubi_raki.c */, + 8399335D2591E8C0001855AF /* ubi_sb_garbage_streamfile.h */, 8375737521F950EC00F01AF5 /* ubi_sb_streamfile.h */, 8349A8F41FE6257D00E26435 /* ubi_sb.c */, 834FE0C5215C79E6000A5D3D /* ue4opus.c */, @@ -2156,6 +2168,7 @@ 83FC176F23AC58D100E1025F /* cri_utf.h in Headers */, 83C7282822BC8C1500678B4A /* mixing.h in Headers */, 832BF82C21E0514B006F50F1 /* hca_keys_awb.h in Headers */, + 839933602591E8C1001855AF /* ubi_sb_garbage_streamfile.h in Headers */, 8306B0E020984590000302D4 /* ppst_streamfile.h in Headers */, 83AA7F722519BFEA004C5298 /* vorbis_bitreader.h in Headers */, 834FE0BA215C798C000A5D3D /* ea_mt_decoder_utk.h in Headers */, @@ -2390,6 +2403,7 @@ 837CEA7923487E2500E62A4A /* ubi_adpcm_decoder.c in Sources */, 834FE0EB215C79ED000A5D3D /* str_wav.c in Sources */, 83269DD32399F5DE00F49FE3 /* ivag.c in Sources */, + 8399335B2591E896001855AF /* vorbis_custom_utils_awc.c in Sources */, 8349A8DF1FE6251F00E26435 /* vorbis_custom_utils_vid1.c in Sources */, 83A21F8D201D8982000F04B9 /* sqex_sead.c in Sources */, 83EED5D3203A8BC7008BEB45 /* ea_swvr.c in Sources */, @@ -2846,6 +2860,7 @@ 836F6FBF18BDC2190095E648 /* otm.c in Sources */, 8306B0B420984552000302D4 /* blocked_tra.c in Sources */, 834FE0B8215C798C000A5D3D /* acm_decoder_decode.c in Sources */, + 8399335F2591E8C1001855AF /* ifs.c in Sources */, 8373342A23F60CDC00DE14DC /* lrmd.c in Sources */, 836F6FBD18BDC2190095E648 /* nwa.c in Sources */, 837CEAFC23487F2C00E62A4A /* raw_wavm.c in Sources */, @@ -2859,6 +2874,7 @@ 83AA7F7F2519C042004C5298 /* bsf.c in Sources */, 836F6F4118BDC2190095E648 /* blocked.c in Sources */, 836F6F3B18BDC2190095E648 /* ws_decoder.c in Sources */, + 839933612591E8C1001855AF /* sbk.c in Sources */, 838BDB6E1D3B043C0022CA6F /* ffmpeg.c in Sources */, 832BF82B21E0514B006F50F1 /* xopus.c in Sources */, 836F6F3418BDC2190095E648 /* nwa_decoder.c in Sources */, diff --git a/Frameworks/vgmstream/vgmstream/src/coding/coding.h b/Frameworks/vgmstream/vgmstream/src/coding/coding.h index 6ddaf7845..2807bd9cb 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/coding.h +++ b/Frameworks/vgmstream/vgmstream/src/coding/coding.h @@ -37,7 +37,8 @@ void decode_apple_ima4(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelsp void decode_fsb_ima(VGMSTREAM* vgmstream, VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); void decode_wwise_ima(VGMSTREAM* vgmstream, VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); void decode_awc_ima(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); -void decode_ubi_ima(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, int codec_config); +void decode_ubi_ima(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); +void decode_ubi_sce_ima(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); void decode_h4m_ima(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, uint16_t frame_format); void decode_cd_ima(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); size_t ima_bytes_to_samples(size_t bytes, int channels); @@ -343,6 +344,7 @@ typedef enum { VORBIS_OGL, /* Shin'en OGL: custom packet headers */ VORBIS_SK, /* Silicon Knights AUD: "OggS" replaced by "SK" */ VORBIS_VID1, /* Neversoft VID1: custom packet blocks/headers */ + VORBIS_AWC, /* Rockstar AWC: custom packet blocks/headers */ } vorbis_custom_t; /* config for Wwise Vorbis (3 types for flexibility though not all combinations exist) */ @@ -365,6 +367,9 @@ typedef struct { wwise_header_t header_type; wwise_packet_t packet_type; + /* AWC config */ + off_t header_offset; + /* output (kinda ugly here but to simplify) */ off_t data_start_offset; @@ -543,6 +548,8 @@ typedef struct { /* frame table */ off_t table_offset; int table_count; + /* fixed frames */ + uint16_t frame_size; } opus_config; ffmpeg_codec_data* init_ffmpeg_switch_opus_config(STREAMFILE* sf, off_t start_offset, size_t data_size, opus_config* cfg); @@ -552,6 +559,7 @@ ffmpeg_codec_data* init_ffmpeg_ea_opus(STREAMFILE* sf, off_t start_offset, size_ ffmpeg_codec_data* init_ffmpeg_x_opus(STREAMFILE* sf, off_t table_offset, int table_count, off_t data_offset, size_t data_size, int channels, int skip); ffmpeg_codec_data* init_ffmpeg_fsb_opus(STREAMFILE* sf, off_t start_offset, size_t data_size, int channels, int skip, int sample_rate); ffmpeg_codec_data* init_ffmpeg_wwise_opus(STREAMFILE* sf, off_t data_offset, size_t data_size, opus_config* cfg); +ffmpeg_codec_data* init_ffmpeg_fixed_opus(STREAMFILE* sf, off_t data_offset, size_t data_size, opus_config* cfg); size_t switch_opus_get_samples(off_t offset, size_t stream_size, STREAMFILE* sf); diff --git a/Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder_custom_opus.c b/Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder_custom_opus.c index b143998d8..8aa649c6d 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder_custom_opus.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder_custom_opus.c @@ -17,7 +17,7 @@ * https://github.com/hcs64/ww2ogg */ -typedef enum { OPUS_SWITCH, OPUS_UE4_v1, OPUS_UE4_v2, OPUS_EA, OPUS_X, OPUS_FSB, OPUS_WWISE } opus_type_t; +typedef enum { OPUS_SWITCH, OPUS_UE4_v1, OPUS_UE4_v2, OPUS_EA, OPUS_X, OPUS_FSB, OPUS_WWISE, OPUS_FIXED } opus_type_t; static size_t make_oggs_first(uint8_t *buf, int buf_size, opus_config *cfg); static size_t make_oggs_page(uint8_t *buf, int buf_size, size_t data_size, int page_sequence, int granule); @@ -36,6 +36,9 @@ typedef struct { int table_count; uint16_t* frame_table; + /* fixed frame size for variations that use this */ + uint16_t frame_size; + /* state */ off_t logical_offset; /* offset that corresponds to physical_offset */ off_t physical_offset; /* actual file offset */ @@ -130,6 +133,10 @@ static size_t opus_io_read(STREAMFILE* sf, uint8_t *dest, off_t offset, size_t l data_size = get_table_frame_size(data, data->sequence - 2); skip_size = 0; break; + case OPUS_FIXED: + data_size = data->frame_size; + skip_size = 0; + break; default: return 0; } @@ -231,6 +238,10 @@ static size_t opus_io_size(STREAMFILE* sf, opus_io_data* data) { data_size = get_table_frame_size(data, packet); skip_size = 0x00; break; + case OPUS_FIXED: + data_size = data->frame_size; + skip_size = 0; + break; default: return 0; } @@ -295,7 +306,7 @@ static void opus_io_close(STREAMFILE* sf, opus_io_data* data) { /* Prepares custom IO for custom Opus, that is converted to Ogg Opus on the fly */ -static STREAMFILE* setup_opus_streamfile(STREAMFILE* sf, opus_config *cfg, off_t stream_offset, size_t stream_size, opus_type_t type) { +static STREAMFILE* setup_opus_streamfile(STREAMFILE* sf, opus_config* cfg, off_t stream_offset, size_t stream_size, opus_type_t type) { STREAMFILE* new_sf = NULL; opus_io_data io_data = {0}; @@ -308,6 +319,7 @@ static STREAMFILE* setup_opus_streamfile(STREAMFILE* sf, opus_config *cfg, off_t io_data.physical_offset = stream_offset; io_data.table_offset = cfg->table_offset; io_data.table_count = cfg->table_count; + io_data.frame_size = cfg->frame_size; io_data.head_size = make_oggs_first(io_data.head_buffer, sizeof(io_data.head_buffer), cfg); if (!io_data.head_size) goto fail; @@ -612,12 +624,16 @@ static size_t custom_opus_get_samples(off_t offset, size_t stream_size, STREAMFI skip_size = 0x02; break; -#if 0 // needs data* for frame table, but num_samples should exist on header +#if 0 //needs data*, num_samples should exist on header case OPUS_X: case OPUS_WWISE: data_size = get_table_frame_size(data, packet); skip_size = 0x00; break; + case OPUS_FIXED: + data_size = data->frame_size; + skip_size = 0; + break; #endif default: return 0; @@ -661,6 +677,11 @@ static size_t custom_opus_get_encoder_delay(off_t offset, STREAMFILE* sf, opus_t case OPUS_WWISE: skip_size = 0x00; break; +#if 0 //should exist on header + case OPUS_FIXED: + skip_size = 0x00; + break; +#endif default: return 0; } @@ -756,6 +777,9 @@ ffmpeg_codec_data* init_ffmpeg_fsb_opus(STREAMFILE* sf, off_t start_offset, size ffmpeg_codec_data* init_ffmpeg_wwise_opus(STREAMFILE* sf, off_t data_offset, size_t data_size, opus_config* cfg) { return init_ffmpeg_custom_opus_config(sf, data_offset, data_size, cfg, OPUS_WWISE); } +ffmpeg_codec_data* init_ffmpeg_fixed_opus(STREAMFILE* sf, off_t data_offset, size_t data_size, opus_config* cfg) { + return init_ffmpeg_custom_opus_config(sf, data_offset, data_size, cfg, OPUS_FIXED); +} static opus_type_t get_ue4opus_version(STREAMFILE* sf, off_t offset) { int read_samples, calc_samples; diff --git a/Frameworks/vgmstream/vgmstream/src/coding/ima_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/ima_decoder.c index 4721bbdb3..a0b4599f2 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/ima_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/ima_decoder.c @@ -1065,9 +1065,8 @@ void decode_awc_ima(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspa /* DVI stereo/mono with some mini header and sample output */ -void decode_ubi_ima(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, int codec_config) { +void decode_ubi_ima(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { int i, sample_count = 0; - int has_header = (codec_config & 0x80) == 0; int32_t hist1 = stream->adpcm_history1_32; int step_index = stream->adpcm_step_index; @@ -1075,7 +1074,7 @@ void decode_ubi_ima(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspa //internal interleave //header in the beginning of the stream - if (has_header && stream->channel_start_offset == stream->offset) { + if (stream->channel_start_offset == stream->offset) { int version, big_endian, header_samples, max_samples_to_do; int16_t (*read_16bit)(off_t,STREAMFILE*) = NULL; off_t offset = stream->offset; @@ -1108,16 +1107,10 @@ void decode_ubi_ima(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspa } } - if (has_header) { - first_sample -= 10; //todo fix hack (needed to adjust nibble offset below) + first_sample -= 10; //todo fix hack (needed to adjust nibble offset below) - if (step_index < 0) step_index = 0; - if (step_index > 88) step_index = 88; - } else { - if (step_index < 0) step_index = 0; - if (step_index > 89) step_index = 89; - } - + if (step_index < 0) step_index = 0; + if (step_index > 88) step_index = 88; for (i = first_sample; i < first_sample + samples_to_do; i++, sample_count += channelspacing) { off_t byte_offset = channelspacing == 1 ? @@ -1131,12 +1124,39 @@ void decode_ubi_ima(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspa outbuf[sample_count] = (short)(hist1); /* all samples are written */ } - //external interleave + stream->adpcm_history1_32 = hist1; + stream->adpcm_step_index = step_index; +} + +/* standard IMA but with a tweak for Ubi's encoder bug with step index (see blocked_ubi_sce.c) */ +void decode_ubi_sce_ima(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { + int i, sample_count = 0; + + int32_t hist1 = stream->adpcm_history1_32; + int step_index = stream->adpcm_step_index; + + //internal interleave + + if (step_index < 0) step_index = 0; + if (step_index > 89) step_index = 89; + + for (i = first_sample; i < first_sample + samples_to_do; i++, sample_count += channelspacing) { + off_t byte_offset = channelspacing == 1 ? + stream->offset + i/2 : /* mono mode */ + stream->offset + i; /* stereo mode */ + int nibble_shift = channelspacing == 1 ? + (!(i%2) ? 4:0) : /* mono mode (high first) */ + (channel==0 ? 4:0); /* stereo mode (high=L,low=R) */ + + std_ima_expand_nibble(stream, byte_offset,nibble_shift, &hist1, &step_index); + outbuf[sample_count] = (short)(hist1); /* all samples are written */ + } stream->adpcm_history1_32 = hist1; stream->adpcm_step_index = step_index; } + /* IMA with variable frame formats controlled by the block layout. The original code uses * tables mapping all standard IMA combinations (to optimize calculations), but decodes the same. * Based on HCS's and Nisto's reverse engineering in h4m_audio_decode. */ diff --git a/Frameworks/vgmstream/vgmstream/src/coding/mpeg_custom_utils_ealayer3.c b/Frameworks/vgmstream/vgmstream/src/coding/mpeg_custom_utils_ealayer3.c index 4f0f19b47..21f22e726 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/mpeg_custom_utils_ealayer3.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/mpeg_custom_utils_ealayer3.c @@ -97,23 +97,23 @@ static int ealayer3_is_empty_frame_v2p(STREAMFILE* sf, off_t offset); /* **************************************************************************** */ /* init codec from an EALayer3 frame */ -int mpeg_custom_setup_init_ealayer3(STREAMFILE* streamfile, off_t start_offset, mpeg_codec_data* data, coding_t *coding_type) { +int mpeg_custom_setup_init_ealayer3(STREAMFILE* sf, off_t start_offset, mpeg_codec_data* data, coding_t *coding_type) { int ok; ealayer3_buffer_t ib = {0}; ealayer3_frame_t eaf; - //;VGM_LOG("init at %lx\n", start_offset); + //;VGM_LOG("init at %lx, %x\n", start_offset, read_u32be(start_offset, sf)); /* get first frame for info */ { - ib.sf = streamfile; + ib.sf = sf; ib.offset = start_offset; ib.is.buf = ib.buf; ok = ealayer3_parse_frame(data, -1, &ib, &eaf); if (!ok) goto fail; } - ;VGM_ASSERT(!eaf.mpeg1, "EAL3: mpeg2 found at 0x%lx\n", start_offset); /* rare [FIFA 08 (PS3) abk] */ + VGM_ASSERT(!eaf.mpeg1, "EAL3: mpeg2 found at 0x%lx\n", start_offset); /* rare [FIFA 08 (PS3) abk] */ *coding_type = coding_MPEG_ealayer3; data->channels_per_frame = eaf.channels; @@ -385,11 +385,16 @@ static int ealayer3_parse_frame_v2(ealayer3_buffer_t* ib, ealayer3_frame_t* eaf) ok = ealayer3_parse_frame_common(ib, eaf); if (!ok) goto fail; } + else { + /* rarely frames contain PCM data only [FIFA 2014 World Cup Brazil (PS3)] */ + eaf->channels = eaf->v2_stereo_flag + 1; + } + VGM_ASSERT(eaf->v2_extended_flag && eaf->v2_common_size == 0, "EA EAL3: v2 empty frame\n"); /* seen in V2S */ VGM_ASSERT(eaf->v2_extended_flag && eaf->v2_offset_samples > 0, "EA EAL3: v2_offset_mode=%x with value=0x%x\n", eaf->v2_offset_mode, eaf->v2_offset_samples); //VGM_ASSERT(eaf->v2_pcm_samples > 0, "EA EAL3: v2_pcm_samples 0x%x\n", eaf->v2_pcm_samples); - eaf->pcm_size = (2*eaf->v2_pcm_samples * eaf->channels); + eaf->pcm_size = (eaf->v2_pcm_samples * sizeof(int16_t) * eaf->channels); eaf->eaframe_size = eaf->pre_size + eaf->common_size + eaf->pcm_size; @@ -408,14 +413,14 @@ fail: /* parses an EALayer3 frame (common part) */ static int ealayer3_parse_frame_common(ealayer3_buffer_t* ib, ealayer3_frame_t* eaf) { /* index tables */ - static const int versions[4] = { /* MPEG 2.5 */ 3, /* reserved */ -1, /* MPEG 2 */ 2, /* MPEG 1 */ 1 }; - static const int sample_rates[4][4] = { /* [version_index][sample rate index] */ + static const int version_table[4] = { /* MPEG 2.5 */ 3, /* reserved */ -1, /* MPEG 2 */ 2, /* MPEG 1 */ 1 }; + static const int sample_rate_table[4][4] = { /* [version_index][sample rate index] */ { 11025, 12000, 8000, -1}, /* MPEG2.5 */ { -1, -1, -1, -1}, /* reserved */ { 22050, 24000, 16000, -1}, /* MPEG2 */ { 44100, 48000, 32000, -1}, /* MPEG1 */ }; - static const int channels[4] = { 2,2,2, 1 }; /* [channel_mode] */ + static const int channel_table[4] = { 2,2,2, 1 }; /* [channel_mode] */ bitstream_t* is = &ib->is; off_t start_b_off = is->b_off; @@ -441,9 +446,9 @@ static int ealayer3_parse_frame_common(ealayer3_buffer_t* ib, ealayer3_frame_t* /* derived */ - eaf->version = versions[eaf->version_index]; - eaf->channels = channels[eaf->channel_mode]; - eaf->sample_rate = sample_rates[eaf->version_index][eaf->sample_rate_index]; + eaf->version = version_table[eaf->version_index]; + eaf->channels = channel_table[eaf->channel_mode]; + eaf->sample_rate = sample_rate_table[eaf->version_index][eaf->sample_rate_index]; eaf->mpeg1 = (eaf->version == 1); if (eaf->version == -1 || eaf->sample_rate == -1) { diff --git a/Frameworks/vgmstream/vgmstream/src/coding/oki_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/oki_decoder.c index acea729b9..e9efbd940 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/oki_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/oki_decoder.c @@ -1,214 +1,214 @@ -#include "coding.h" - - -static const int step_sizes[49] = { /* OKI table (subsection of IMA's table) */ - 16, 17, 19, 21, 23, 25, 28, 31, - 34, 37, 41, 45, 50, 55, 60, 66, - 73, 80, 88, 97, 107, 118, 130, 143, - 157, 173, 190, 209, 230, 253, 279, 307, - 337, 371, 408, 449, 494, 544, 598, 658, - 724, 796, 876, 963, 1060, 1166, 1282, 1411, - 1552 -}; - -static const int stex_indexes[16] = { /* OKI table (also from IMA) */ - -1, -1, -1, -1, 2, 4, 6, 8, - -1, -1, -1, -1, 2, 4, 6, 8 -}; - - -static void pcfx_expand_nibble(VGMSTREAMCHANNEL* stream, off_t byte_offset, int nibble_shift, int32_t* hist1, int32_t* step_index, int16_t* out_sample, int mode) { - int code, step, delta; - - code = (read_8bit(byte_offset,stream->streamfile) >> nibble_shift) & 0xf; - step = step_sizes[*step_index]; - - delta = (code & 0x7); - if (mode & 1) { - if (step == 1552) /* bad last step_sizes value from OKI table */ - step = 1522; - delta = step * (delta + 1) * 2; - } - else { - delta = step * (delta + 1); - } - if (code & 0x8) - delta = -delta; - - *step_index += stex_indexes[code]; - if (*step_index < 0) *step_index = 0; - if (*step_index > 48) *step_index = 48; - - *hist1 += delta; - if (*hist1 > 16383) *hist1 = 16383; - if (*hist1 < -16384) *hist1 = -16384; - - if (mode & 1) { - *out_sample = *hist1; - } else { - *out_sample = *hist1 << 1; - } - - /* seems real HW does filtering here too */ - - /* double volume since it clips at half */ - if (mode & 2) { - *out_sample = *hist1 << 1; - } -} - -static void oki16_expand_nibble(VGMSTREAMCHANNEL* stream, off_t byte_offset, int nibble_shift, int32_t* hist1, int32_t* step_index, int16_t* out_sample) { - int code, step, delta; - - code = (read_8bit(byte_offset,stream->streamfile) >> nibble_shift) & 0xf; - step = step_sizes[*step_index]; - - /* IMA 'mul' style (standard OKI uses 'shift-add') */ - delta = (code & 0x7); - delta = (((delta * 2) + 1) * step) >> 3; - if (code & 0x8) - delta = -delta; - *hist1 += delta; - - /* standard OKI clamps hist to 2047,-2048 here */ - - *step_index += stex_indexes[code]; - if (*step_index < 0) *step_index = 0; - if (*step_index > 48) *step_index = 48; - - *out_sample = *hist1; -} - -/* Possible variation for adp_konami (Viper hardware): - * delta = ((n&7) + 0.5) * stepsize / 4; clamps 2047,-2048; nigh nibble first - * - * Results are very similar, but can't verify actual decoding, and oki4s is used in - * Jubeat (also Konami) so it makes sense they would have reused it. - * Viper sound chip may be a YMZ280B though. - */ - -static void oki4s_expand_nibble(VGMSTREAMCHANNEL* stream, off_t byte_offset, int nibble_shift, int32_t* hist1, int32_t* step_index, int16_t* out_sample) { - int code, step, delta; - - code = (read_8bit(byte_offset,stream->streamfile) >> nibble_shift) & 0xf; - step = step_sizes[*step_index]; - - step = step << 4; /* original table has precomputed step_sizes so that this isn't done */ - - /* IMA 'shift-add' style (like standard OKI) */ - delta = step >> 3; - if (code & 1) delta += step >> 2; - if (code & 2) delta += step >> 1; - if (code & 4) delta += step; - if (code & 8) delta = -delta; - *hist1 += delta; - - *hist1 = clamp16(*hist1); /* standard OKI clamps hist to 2047,-2048 here */ - - *step_index += stex_indexes[code]; - if (*step_index < 0) *step_index = 0; - if (*step_index > 48) *step_index = 48; - - *out_sample = *hist1; -} - -/* PC-FX ADPCM decoding, variation of OKI/Dialogic/VOX ADPCM. Based on mednafen/pcfx-music-dump. - * Apparently most ADPCM was made with a buggy encoder, resulting in incorrect sound in real hardware - * and sound clipped at half. Decoding can be controlled with modes: - * - 0: hardware decoding (waveforms in many games will look wrong, ex. Der Langrisser track 032) - * - 1: 'buggy encoder' decoding (waveforms will look fine) - * - 2: hardware decoding with double volume (may clip?) - * - 3: 'buggy encoder' decoding with double volume - * - * PC-FX ISOs don't have a standard filesystem nor file formats (raw data must be custom-ripped), - * so it's needs GENH/TXTH. Sample rate can only be base_value divided by 1/2/3/4, where - * base_value is approximately ~31468.5 (follows hardware clocks), mono or interleaved for stereo. - */ -void decode_pcfx(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int mode) { - int i, sample_count = 0; - int32_t hist1 = stream->adpcm_history1_32; - int step_index = stream->adpcm_step_index; - int16_t out_sample; - - for (i = first_sample; i < first_sample + samples_to_do; i++) { - off_t byte_offset = stream->offset + i/2; - int nibble_shift = (i&1?4:0); /* low nibble first */ - - pcfx_expand_nibble(stream, byte_offset,nibble_shift, &hist1, &step_index, &out_sample, mode); - outbuf[sample_count] = out_sample; - sample_count += channelspacing; - } - - stream->adpcm_history1_32 = hist1; - stream->adpcm_step_index = step_index; -} - -/* OKI variation with 16-bit output (vs standard's 12-bit), found in FrontWing's PS2 games (Sweet Legacy, Hooligan). - * Reverse engineered from the ELF with help from the folks at hcs. */ -void decode_oki16(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { - int i, sample_count = 0; - int32_t hist1 = stream->adpcm_history1_32; - int step_index = stream->adpcm_step_index; - int16_t out_sample; - int is_stereo = channelspacing > 1; - - - /* external interleave */ - - /* no header (external setup), pre-clamp for wrong values */ - if (step_index < 0) step_index=0; - if (step_index > 48) step_index=48; - - /* decode nibbles (layout: varies) */ - for (i = first_sample; i < first_sample + samples_to_do; i++, sample_count += channelspacing) { - off_t byte_offset = is_stereo ? - stream->offset + i : /* stereo: one nibble per channel */ - stream->offset + i/2; /* mono: consecutive nibbles (assumed) */ - int nibble_shift = - is_stereo ? (!(channel&1) ? 0:4) : (!(i&1) ? 0:4); /* even = low, odd = high */ - - oki16_expand_nibble(stream, byte_offset,nibble_shift, &hist1, &step_index, &out_sample); - outbuf[sample_count] = (out_sample); - } - - stream->adpcm_history1_32 = hist1; - stream->adpcm_step_index = step_index; -} - -/* OKI variation with 16-bit output (vs standard's 12-bit) and pre-adjusted tables (shifted by 4), found in Jubeat Clan (AC). - * Reverse engineered from the DLLs. */ -void decode_oki4s(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { - int i, sample_count = 0; - int32_t hist1 = stream->adpcm_history1_32; - int step_index = stream->adpcm_step_index; - int16_t out_sample; - int is_stereo = channelspacing > 1; - - - /* external interleave */ - - /* no header (external setup), pre-clamp for wrong values */ - if (step_index < 0) step_index=0; - if (step_index > 48) step_index=48; - - /* decode nibbles (layout: varies) */ - for (i = first_sample; i < first_sample + samples_to_do; i++, sample_count += channelspacing) { - off_t byte_offset = is_stereo ? - stream->offset + i : /* stereo: one nibble per channel */ - stream->offset + i/2; /* mono: consecutive nibbles (assumed) */ - int nibble_shift = - is_stereo ? ((channel&1) ? 0:4) : ((i&1) ? 0:4); /* even = high, odd = low */ - - oki4s_expand_nibble(stream, byte_offset,nibble_shift, &hist1, &step_index, &out_sample); - outbuf[sample_count] = (out_sample); - } - - stream->adpcm_history1_32 = hist1; - stream->adpcm_step_index = step_index; -} - -size_t oki_bytes_to_samples(size_t bytes, int channels) { - if (channels <= 0) return 0; - /* 2 samples per byte (2 nibbles) in stereo or mono config */ - return bytes * 2 / channels; -} +#include "coding.h" + + +static const int step_sizes[49] = { /* OKI table (subsection of IMA's table) */ + 16, 17, 19, 21, 23, 25, 28, 31, + 34, 37, 41, 45, 50, 55, 60, 66, + 73, 80, 88, 97, 107, 118, 130, 143, + 157, 173, 190, 209, 230, 253, 279, 307, + 337, 371, 408, 449, 494, 544, 598, 658, + 724, 796, 876, 963, 1060, 1166, 1282, 1411, + 1552 +}; + +static const int stex_indexes[16] = { /* OKI table (also from IMA) */ + -1, -1, -1, -1, 2, 4, 6, 8, + -1, -1, -1, -1, 2, 4, 6, 8 +}; + + +static void pcfx_expand_nibble(VGMSTREAMCHANNEL* stream, off_t byte_offset, int nibble_shift, int32_t* hist1, int32_t* step_index, int16_t* out_sample, int mode) { + int code, step, delta; + + code = (read_8bit(byte_offset,stream->streamfile) >> nibble_shift) & 0xf; + step = step_sizes[*step_index]; + + delta = (code & 0x7); + if (mode & 1) { + if (step == 1552) /* bad last step_sizes value from OKI table */ + step = 1522; + delta = step * (delta + 1) * 2; + } + else { + delta = step * (delta + 1); + } + if (code & 0x8) + delta = -delta; + + *step_index += stex_indexes[code]; + if (*step_index < 0) *step_index = 0; + if (*step_index > 48) *step_index = 48; + + *hist1 += delta; + if (*hist1 > 16383) *hist1 = 16383; + if (*hist1 < -16384) *hist1 = -16384; + + if (mode & 1) { + *out_sample = *hist1; + } else { + *out_sample = *hist1 << 1; + } + + /* seems real HW does filtering here too */ + + /* double volume since it clips at half */ + if (mode & 2) { + *out_sample = *hist1 << 1; + } +} + +static void oki16_expand_nibble(VGMSTREAMCHANNEL* stream, off_t byte_offset, int nibble_shift, int32_t* hist1, int32_t* step_index, int16_t* out_sample) { + int code, step, delta; + + code = (read_8bit(byte_offset,stream->streamfile) >> nibble_shift) & 0xf; + step = step_sizes[*step_index]; + + /* IMA 'mul' style (standard OKI uses 'shift-add') */ + delta = (code & 0x7); + delta = (((delta * 2) + 1) * step) >> 3; + if (code & 0x8) + delta = -delta; + *hist1 += delta; + + /* standard OKI clamps hist to 2047,-2048 here */ + + *step_index += stex_indexes[code]; + if (*step_index < 0) *step_index = 0; + if (*step_index > 48) *step_index = 48; + + *out_sample = *hist1; +} + +/* Possible variation for adp_konami (Viper hardware): + * delta = ((n&7) + 0.5) * stepsize / 4; clamps 2047,-2048 + * + * Results are very similar, but can't verify actual decoding, and oki4s is used in + * Jubeat (also Konami) so it makes sense they would have reused it. + * Viper sound chip may be a YMZ280B though. + */ + +static void oki4s_expand_nibble(VGMSTREAMCHANNEL* stream, off_t byte_offset, int nibble_shift, int32_t* hist1, int32_t* step_index, int16_t* out_sample) { + int code, step, delta; + + code = (read_8bit(byte_offset,stream->streamfile) >> nibble_shift) & 0xf; + step = step_sizes[*step_index]; + + step = step << 4; /* original table has precomputed step_sizes so that this isn't done */ + + /* IMA 'shift-add' style (like standard OKI) */ + delta = step >> 3; + if (code & 1) delta += step >> 2; + if (code & 2) delta += step >> 1; + if (code & 4) delta += step; + if (code & 8) delta = -delta; + *hist1 += delta; + + *hist1 = clamp16(*hist1); /* standard OKI clamps hist to 2047,-2048 here */ + + *step_index += stex_indexes[code]; + if (*step_index < 0) *step_index = 0; + if (*step_index > 48) *step_index = 48; + + *out_sample = *hist1; +} + +/* PC-FX ADPCM decoding, variation of OKI/Dialogic/VOX ADPCM. Based on mednafen/pcfx-music-dump. + * Apparently most ADPCM was made with a buggy encoder, resulting in incorrect sound in real hardware + * and sound clipped at half. Decoding can be controlled with modes: + * - 0: hardware decoding (waveforms in many games will look wrong, ex. Der Langrisser track 032) + * - 1: 'buggy encoder' decoding (waveforms will look fine) + * - 2: hardware decoding with double volume (may clip?) + * - 3: 'buggy encoder' decoding with double volume + * + * PC-FX ISOs don't have a standard filesystem nor file formats (raw data must be custom-ripped), + * so it's needs GENH/TXTH. Sample rate can only be base_value divided by 1/2/3/4, where + * base_value is approximately ~31468.5 (follows hardware clocks), mono or interleaved for stereo. + */ +void decode_pcfx(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int mode) { + int i, sample_count = 0; + int32_t hist1 = stream->adpcm_history1_32; + int step_index = stream->adpcm_step_index; + int16_t out_sample; + + for (i = first_sample; i < first_sample + samples_to_do; i++) { + off_t byte_offset = stream->offset + i/2; + int nibble_shift = (i&1?4:0); /* low nibble first */ + + pcfx_expand_nibble(stream, byte_offset,nibble_shift, &hist1, &step_index, &out_sample, mode); + outbuf[sample_count] = out_sample; + sample_count += channelspacing; + } + + stream->adpcm_history1_32 = hist1; + stream->adpcm_step_index = step_index; +} + +/* OKI variation with 16-bit output (vs standard's 12-bit), found in FrontWing's PS2 games (Sweet Legacy, Hooligan). + * Reverse engineered from the ELF with help from the folks at hcs. Codec has no name so OKI16 is just a description. */ +void decode_oki16(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { + int i, sample_count = 0; + int32_t hist1 = stream->adpcm_history1_32; + int step_index = stream->adpcm_step_index; + int16_t out_sample; + int is_stereo = channelspacing > 1; + + + /* external interleave */ + + /* no header (external setup), pre-clamp for wrong values */ + if (step_index < 0) step_index=0; + if (step_index > 48) step_index=48; + + /* decode nibbles (layout: varies) */ + for (i = first_sample; i < first_sample + samples_to_do; i++, sample_count += channelspacing) { + off_t byte_offset = is_stereo ? + stream->offset + i : /* stereo: one nibble per channel */ + stream->offset + i/2; /* mono: consecutive nibbles (assumed) */ + int nibble_shift = + is_stereo ? (!(channel&1) ? 0:4) : (!(i&1) ? 0:4); /* even = low, odd = high */ + + oki16_expand_nibble(stream, byte_offset,nibble_shift, &hist1, &step_index, &out_sample); + outbuf[sample_count] = (out_sample); + } + + stream->adpcm_history1_32 = hist1; + stream->adpcm_step_index = step_index; +} + +/* OKI variation with 16-bit output (vs standard's 12-bit) and pre-adjusted tables (shifted by 4), found in Jubeat Clan (AC). + * Reverse engineered from the DLLs (libbmsd-engine.dll). Internally code calls it "adpcm", so OKI4S is just a description. */ +void decode_oki4s(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { + int i, sample_count = 0; + int32_t hist1 = stream->adpcm_history1_32; + int step_index = stream->adpcm_step_index; + int16_t out_sample; + int is_stereo = channelspacing > 1; + + + /* external interleave */ + + /* no header (external setup), pre-clamp for wrong values */ + if (step_index < 0) step_index=0; + if (step_index > 48) step_index=48; + + /* decode nibbles (layout: varies) */ + for (i = first_sample; i < first_sample + samples_to_do; i++, sample_count += channelspacing) { + off_t byte_offset = is_stereo ? + stream->offset + i : /* stereo: one nibble per channel */ + stream->offset + i/2; /* mono: consecutive nibbles */ + int nibble_shift = + is_stereo ? ((channel&1) ? 0:4) : ((i&1) ? 0:4); /* even = high, odd = low */ + + oki4s_expand_nibble(stream, byte_offset,nibble_shift, &hist1, &step_index, &out_sample); + outbuf[sample_count] = (out_sample); + } + + stream->adpcm_history1_32 = hist1; + stream->adpcm_step_index = step_index; +} + +size_t oki_bytes_to_samples(size_t bytes, int channels) { + if (channels <= 0) return 0; + /* 2 samples per byte (2 nibbles) in stereo or mono config */ + return bytes * 2 / channels; +} diff --git a/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_decoder.c index 4b887a461..4dbcd8259 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_decoder.c @@ -51,6 +51,7 @@ vorbis_custom_codec_data* init_vorbis_custom(STREAMFILE* sf, off_t start_offset, case VORBIS_OGL: ok = vorbis_custom_setup_init_ogl(sf, start_offset, data); break; case VORBIS_SK: ok = vorbis_custom_setup_init_sk(sf, start_offset, data); break; case VORBIS_VID1: ok = vorbis_custom_setup_init_vid1(sf, start_offset, data); break; + case VORBIS_AWC: ok = vorbis_custom_setup_init_awc(sf, start_offset, data); break; default: goto fail; } if(!ok) goto fail; @@ -134,6 +135,7 @@ void decode_vorbis_custom(VGMSTREAM* vgmstream, sample_t* outbuf, int32_t sample case VORBIS_OGL: ok = vorbis_custom_parse_packet_ogl(stream, data); break; case VORBIS_SK: ok = vorbis_custom_parse_packet_sk(stream, data); break; case VORBIS_VID1: ok = vorbis_custom_parse_packet_vid1(stream, data); break; + case VORBIS_AWC: ok = vorbis_custom_parse_packet_awc(stream, data); break; default: goto decode_fail; } if(!ok) { diff --git a/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_decoder.h b/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_decoder.h index 8080800d2..b3fa05aef 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_decoder.h +++ b/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_decoder.h @@ -43,12 +43,14 @@ int vorbis_custom_setup_init_wwise(STREAMFILE* sf, off_t start_offset, vorbis_cu int vorbis_custom_setup_init_ogl(STREAMFILE* sf, off_t start_offset, vorbis_custom_codec_data* data); int vorbis_custom_setup_init_sk(STREAMFILE* sf, off_t start_offset, vorbis_custom_codec_data* data); int vorbis_custom_setup_init_vid1(STREAMFILE* sf, off_t start_offset, vorbis_custom_codec_data* data); +int vorbis_custom_setup_init_awc(STREAMFILE* sf, off_t start_offset, vorbis_custom_codec_data* data); -int vorbis_custom_parse_packet_fsb(VGMSTREAMCHANNEL *stream, vorbis_custom_codec_data* data); -int vorbis_custom_parse_packet_wwise(VGMSTREAMCHANNEL *stream, vorbis_custom_codec_data* data); -int vorbis_custom_parse_packet_ogl(VGMSTREAMCHANNEL *stream, vorbis_custom_codec_data* data); -int vorbis_custom_parse_packet_sk(VGMSTREAMCHANNEL *stream, vorbis_custom_codec_data* data); -int vorbis_custom_parse_packet_vid1(VGMSTREAMCHANNEL *stream, vorbis_custom_codec_data* data); +int vorbis_custom_parse_packet_fsb(VGMSTREAMCHANNEL* stream, vorbis_custom_codec_data* data); +int vorbis_custom_parse_packet_wwise(VGMSTREAMCHANNEL* stream, vorbis_custom_codec_data* data); +int vorbis_custom_parse_packet_ogl(VGMSTREAMCHANNEL* stream, vorbis_custom_codec_data* data); +int vorbis_custom_parse_packet_sk(VGMSTREAMCHANNEL* stream, vorbis_custom_codec_data* data); +int vorbis_custom_parse_packet_vid1(VGMSTREAMCHANNEL* stream, vorbis_custom_codec_data* data); +int vorbis_custom_parse_packet_awc(VGMSTREAMCHANNEL* stream, vorbis_custom_codec_data* data); #endif/* VGM_USE_VORBIS */ #endif/*_VORBIS_CUSTOM_DECODER_H_ */ diff --git a/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_utils_awc.c b/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_utils_awc.c new file mode 100644 index 000000000..b87793aaa --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_utils_awc.c @@ -0,0 +1,86 @@ +#include "vorbis_custom_decoder.h" + +#ifdef VGM_USE_VORBIS +#include + + +/* **************************************************************************** */ +/* EXTERNAL API */ +/* **************************************************************************** */ + +/** + * AWC removes the Ogg layer and uses 32b frame sizes for headers and 16b frame sizes for data + */ +int vorbis_custom_setup_init_awc(STREAMFILE* sf, off_t start_offset, vorbis_custom_codec_data* data) { + off_t offset = data->config.header_offset; + size_t packet_size; + + /* read 3 packets with triad (id/comment/setup), each with an AWC header */ + + /* normal identificacion packet */ + packet_size = read_u32le(offset, sf); + if (packet_size > data->buffer_size) goto fail; + data->op.bytes = read_streamfile(data->buffer, offset + 0x04, packet_size, sf); + if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) != 0) + goto fail; + offset += 0x04 + packet_size; + + /* normal comment packet */ + packet_size = read_u32le(offset, sf); + if (packet_size > data->buffer_size) goto fail; + data->op.bytes = read_streamfile(data->buffer, offset + 0x04, packet_size, sf); + if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) !=0) + goto fail; + offset += 0x04 + packet_size; + + /* normal setup packet */ + packet_size = read_u32le(offset, sf); + if (packet_size > data->buffer_size) goto fail; + data->op.bytes = read_streamfile(data->buffer, offset + 0x04, packet_size, sf); + if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) != 0) + goto fail; + offset += 0x04 + packet_size; + + /* data starts separate */ + data->config.data_start_offset = start_offset; + + return 1; + +fail: + return 0; +} + +/* as AWC was coded by wackos, frames are forced to fit 0x800 chunks and rest is padded, in both sfx and music/blocked modes + * (ex. read frame until 0x7A0 + next frame is size 0x140 > pads 0x60 and last goes to next chunk) */ +static inline off_t find_padding_awc(off_t offset, vorbis_custom_codec_data* data) { + offset = offset - data->config.data_start_offset; + + return 0x800 - (offset % 0x800); +} + +int vorbis_custom_parse_packet_awc(VGMSTREAMCHANNEL* stream, vorbis_custom_codec_data* data) { + size_t bytes; + + /* get next packet size from the AWC 16b header, except between chunks */ + data->op.bytes = read_u16le(stream->offset, stream->streamfile); + if (data->op.bytes == 0) { // || (stream->offset - start & 0x800) < 0x01 //todo could pad near block end? + stream->offset += find_padding_awc(stream->offset, data); + data->op.bytes = read_u16le(stream->offset, stream->streamfile); + } + + stream->offset += 0x02; + if (data->op.bytes == 0 || data->op.bytes == 0xFFFF || data->op.bytes > data->buffer_size) + goto fail; /* EOF or end padding */ + + /* read raw block */ + bytes = read_streamfile(data->buffer, stream->offset, data->op.bytes, stream->streamfile); + stream->offset += data->op.bytes; + if (bytes != data->op.bytes) goto fail; /* wrong packet? */ + + return 1; + +fail: + return 0; +} + +#endif diff --git a/Frameworks/vgmstream/vgmstream/src/decode.c b/Frameworks/vgmstream/vgmstream/src/decode.c index b44407a40..14a2fc7a7 100644 --- a/Frameworks/vgmstream/vgmstream/src/decode.c +++ b/Frameworks/vgmstream/vgmstream/src/decode.c @@ -355,6 +355,7 @@ int get_vgmstream_samples_per_frame(VGMSTREAM* vgmstream) { case coding_SNDS_IMA: case coding_OTNS_IMA: case coding_UBI_IMA: + case coding_UBI_SCE_IMA: case coding_OKI16: case coding_OKI4S: case coding_MTF_IMA: @@ -580,6 +581,8 @@ int get_vgmstream_frame_size(VGMSTREAM* vgmstream) { return 0; //todo: 0x01? case coding_UBI_IMA: /* variable (PCM then IMA) */ return 0; + case coding_UBI_SCE_IMA: + return 0; case coding_XBOX_IMA: //todo should be 0x48 when stereo, but blocked/interleave layout don't understand stereo codecs return 0x24; //vgmstream->channels==1 ? 0x24 : 0x48; @@ -1145,8 +1148,13 @@ void decode_vgmstream(VGMSTREAM* vgmstream, int samples_written, int samples_to_ case coding_UBI_IMA: for (ch = 0; ch < vgmstream->channels; ch++) { decode_ubi_ima(&vgmstream->ch[ch], buffer+ch, - vgmstream->channels, vgmstream->samples_into_block, samples_to_do, ch, - vgmstream->codec_config); + vgmstream->channels, vgmstream->samples_into_block, samples_to_do, ch); + } + break; + case coding_UBI_SCE_IMA: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_ubi_sce_ima(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do, ch); } break; case coding_H4M_IMA: diff --git a/Frameworks/vgmstream/vgmstream/src/formats.c b/Frameworks/vgmstream/vgmstream/src/formats.c index 295bf2c92..3d27fd46c 100644 --- a/Frameworks/vgmstream/vgmstream/src/formats.c +++ b/Frameworks/vgmstream/vgmstream/src/formats.c @@ -103,7 +103,7 @@ static const char* extension_list[] = { "bgw", "bh2pcm", "bik", - "bika", + "bika", //fake extension for .bik (to be removed) "bik2", //"bin", //common "bk2", @@ -221,6 +221,7 @@ static const char* extension_list[] = { "idwav", "idx", "idxma", + "ifs", "ikm", "ild", "ilv", //txth/reserved [Star Wars Episode III (PS2)] @@ -341,7 +342,8 @@ static const char* extension_list[] = { "n64", "naac", - "ndp", + "nds", + "ndp", //fake extension/header id for .nds "ngca", "nlsd", "nop", @@ -425,6 +427,7 @@ static const char* extension_list[] = { "sb5", "sb6", "sb7", + "sbk", "sbr", "sbv", "sm0", @@ -749,6 +752,7 @@ static const coding_info coding_info_list[] = { {coding_REF_IMA, "Reflections 4-bit IMA ADPCM"}, {coding_AWC_IMA, "Rockstar AWC 4-bit IMA ADPCM"}, {coding_UBI_IMA, "Ubisoft 4-bit IMA ADPCM"}, + {coding_UBI_SCE_IMA, "Ubisoft 4-bit SCE IMA ADPCM"}, {coding_H4M_IMA, "Hudson HVQM4 4-bit IMA ADPCM"}, {coding_CD_IMA, "Crystal Dynamics 4-bit IMA ADPCM"}, @@ -1318,6 +1322,9 @@ static const meta_info meta_info_list[] = { {meta_WADY, "Marble WADY header"}, {meta_DSP_SQEX, "Square Enix DSP header"}, {meta_DSP_WIIVOICE, "Koei Tecmo WiiVoice header"}, + {meta_SBK, "Team17 SBK header"}, + {meta_DSP_WIIADPCM, "Exient WIIADPCM header"}, + {meta_DSP_CWAC, "CRI CWAC header"}, }; void get_vgmstream_coding_description(VGMSTREAM* vgmstream, char* out, size_t out_size) { diff --git a/Frameworks/vgmstream/vgmstream/src/layout/blocked_ubi_sce.c b/Frameworks/vgmstream/vgmstream/src/layout/blocked_ubi_sce.c index 640aca39d..318438ab1 100644 --- a/Frameworks/vgmstream/vgmstream/src/layout/blocked_ubi_sce.c +++ b/Frameworks/vgmstream/vgmstream/src/layout/blocked_ubi_sce.c @@ -1,4 +1,5 @@ #include "layout.h" +#include "../coding/coding.h" #include "../vgmstream.h" /* weird mix of Ubi ADPCM format with Ubi IMA, found in Splinter Cell Essentials (PSP) */ @@ -18,8 +19,6 @@ void block_update_ubi_sce(off_t block_offset, VGMSTREAM* vgmstream) { * (higher bit has a special meaning) */ - vgmstream->codec_config |= 0x80; /* flag for decoder, ugly I know */ - if ((vgmstream->codec_config & 1) == 0) { header_size = 0x34; /* read header in first subframe */ } @@ -34,8 +33,8 @@ void block_update_ubi_sce(off_t block_offset, VGMSTREAM* vgmstream) { padding_size = 0x01; vgmstream->current_block_offset = block_offset; - vgmstream->current_block_size = subframe_size; vgmstream->next_block_offset = block_offset + header_size * vgmstream->channels + subframe_size + padding_size; + vgmstream->current_block_samples = ima_bytes_to_samples(subframe_size, channels); for (i = 0; i < vgmstream->channels; i++) { vgmstream->ch[i].offset = block_offset + header_size * channels; @@ -44,11 +43,11 @@ void block_update_ubi_sce(off_t block_offset, VGMSTREAM* vgmstream) { vgmstream->ch[i].adpcm_step_index = read_32bitLE(block_offset + header_size * i + 0x04, sf); vgmstream->ch[i].adpcm_history1_32 = read_32bitLE(block_offset + header_size * i + 0x08, sf); - /* First step is always 0x500, not sure if it's a bug or a feature but the game just takes it as is and - * ends up reading 0 from out-of-bounds memory area which causes a pop at the start. Yikes. - * It gets clampled later so the rest of the sound plays ok. - * We put 89 here as our special index which contains 0 to simulate this. - */ + /* First step is always 0x500, not sure if it's a bug or a feature but the game just takes it as is and + * ends up reading 0 from out-of-bounds memory area which causes a pop at the start. Yikes. + * It gets clampled later so the rest of the sound plays ok. + * We put 89 here as our special index which contains 0 to simulate this. + */ if (vgmstream->ch[i].adpcm_step_index == 0x500) { vgmstream->ch[i].adpcm_step_index = 89; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/aifc.c b/Frameworks/vgmstream/vgmstream/src/meta/aifc.c index 08a3b6e90..492203f92 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/aifc.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/aifc.c @@ -326,12 +326,31 @@ VGMSTREAM* init_vgmstream_aifc(STREAMFILE* sf) { * will become apparent. * We shouldn't have a loop point that overflows an int32_t anyway. */ loop_flag = 1; - if (loop_start==loop_end) + if (loop_start == loop_end) loop_flag = 0; } } + } + if (!loop_flag && mark_offset) { + int mark_count = read_u16be(mark_offset + 0x00,sf); - /* Relic has "beg loop" "end loop" comments but no actual looping? */ + /* use "beg/end" loop comments [Battle Tryst (Arcade)] + * Relic codec has 3 "beg loop" "end loop" "start offset" comments, but always begin = 0 and end = -1 */ + if (mark_count == 2) { + /* per mark: + * 00(2): id + * 02(4): sample point + * 06(1): string size + * --(-): string (non-null terminated) + * --(1): null terminator */ + /* simplified... */ + if (read_u32be(mark_offset + 0x09,sf) == 0x62656720 && /* "beg " */ + read_u32be(mark_offset + 0x19,sf) == 0x656E6420) { /* "end " */ + loop_start = read_s32be(mark_offset + 0x04, sf); + loop_end = read_s32be(mark_offset + 0x14, sf); + loop_flag = 1; + } + } } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/awb.c b/Frameworks/vgmstream/vgmstream/src/meta/awb.c index 929d6f014..29a091aa3 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/awb.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/awb.c @@ -1,7 +1,7 @@ #include "meta.h" #include "../coding/coding.h" -typedef enum { ADX, HCA, VAG, RIFF, CWAV, DSP } awb_type; +typedef enum { ADX, HCA, VAG, RIFF, CWAV, DSP, CWAC } awb_type; static void load_awb_name(STREAMFILE* sf, STREAMFILE* sf_acb, VGMSTREAM* vgmstream, int waveid); @@ -116,6 +116,10 @@ VGMSTREAM* init_vgmstream_awb_memory(STREAMFILE* sf, STREAMFILE* sf_acb) { type = DSP; extension = "dsp"; } + else if (is_id32be(subfile_offset,sf, "CWAC")) { /* type 13 again */ + type = CWAC; + extension = "dsp"; + } else { VGM_LOG("AWB: unknown codec\n"); goto fail; @@ -150,6 +154,10 @@ VGMSTREAM* init_vgmstream_awb_memory(STREAMFILE* sf, STREAMFILE* sf_acb) { vgmstream = init_vgmstream_ngc_dsp_std(temp_sf); if (!vgmstream) goto fail; break; + case CWAC: /* Mario & Sonic at the Rio 2016 Olympic Games (WiiU) */ + vgmstream = init_vgmstream_dsp_cwac(temp_sf); + if (!vgmstream) goto fail; + break; default: goto fail; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/awc.c b/Frameworks/vgmstream/vgmstream/src/meta/awc.c index 583553521..d1f82c40e 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/awc.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/awc.c @@ -10,7 +10,7 @@ typedef struct { int total_subsongs; - int channel_count; + int channels; int sample_rate; int codec; int num_samples; @@ -19,27 +19,30 @@ typedef struct { off_t stream_offset; size_t stream_size; + off_t vorbis_offset[VGMSTREAM_MAX_CHANNELS]; } awc_header; -static int parse_awc_header(STREAMFILE* streamFile, awc_header* awc); +static int parse_awc_header(STREAMFILE* sf, awc_header* awc); + +static layered_layout_data* build_layered_awc(STREAMFILE* sf, awc_header* awc); /* AWC - from RAGE (Rockstar Advanced Game Engine) audio [Red Dead Redemption, Max Payne 3, GTA5 (multi)] */ -VGMSTREAM * init_vgmstream_awc(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; +VGMSTREAM* init_vgmstream_awc(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; awc_header awc = {0}; /* checks */ - if (!check_extensions(streamFile,"awc")) + if (!check_extensions(sf,"awc")) goto fail; - if (!parse_awc_header(streamFile, &awc)) + if (!parse_awc_header(sf, &awc)) goto fail; /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(awc.channel_count, 0); + vgmstream = allocate_vgmstream(awc.channels, 0); if (!vgmstream) goto fail; vgmstream->sample_rate = awc.sample_rate; @@ -77,15 +80,15 @@ VGMSTREAM * init_vgmstream_awc(STREAMFILE *streamFile) { layered_layout_data * data = NULL; /* init layout */ - data = init_layout_layered(awc.channel_count); + data = init_layout_layered(awc.channels); if (!data) goto fail; vgmstream->layout_data = data; vgmstream->layout_type = layout_layered; vgmstream->coding_type = coding_FFmpeg; /* open each layer subfile */ - for (i = 0; i < awc.channel_count; i++) { - STREAMFILE* temp_streamFile; + for (i = 0; i < awc.channels; i++) { + STREAMFILE* temp_sf = NULL; int layer_channels = 1; /* build the layer VGMSTREAM */ @@ -99,20 +102,20 @@ VGMSTREAM * init_vgmstream_awc(STREAMFILE *streamFile) { data->layers[i]->num_samples = awc.num_samples; /* setup custom IO streamfile, pass to FFmpeg and hope it's fooled */ - temp_streamFile = setup_awc_xma_streamfile(streamFile, awc.stream_offset, awc.stream_size, awc.block_chunk, awc.channel_count, i); - if (!temp_streamFile) goto fail; + temp_sf = setup_awc_xma_streamfile(sf, awc.stream_offset, awc.stream_size, awc.block_chunk, awc.channels, i); + if (!temp_sf) goto fail; - substream_offset = 0; /* where FFmpeg thinks data starts, which our custom streamFile will clamp */ - substream_size = get_streamfile_size(temp_streamFile); /* data of one XMA substream without blocks */ + substream_offset = 0; /* where FFmpeg thinks data starts, which our custom sf will clamp */ + substream_size = get_streamfile_size(temp_sf); /* data of one XMA substream without blocks */ block_size = 0x8000; /* no idea */ block_count = substream_size / block_size; /* not accurate but not needed */ bytes = ffmpeg_make_riff_xma2(buf, 0x100, awc.num_samples, substream_size, layer_channels, awc.sample_rate, block_count, block_size); - data->layers[i]->codec_data = init_ffmpeg_header_offset(temp_streamFile, buf,bytes, substream_offset,substream_size); + data->layers[i]->codec_data = init_ffmpeg_header_offset(temp_sf, buf,bytes, substream_offset,substream_size); - xma_fix_raw_samples(data->layers[i], temp_streamFile, substream_offset,substream_size, 0, 0,0); /* samples are ok? */ + xma_fix_raw_samples(data->layers[i], temp_sf, substream_offset,substream_size, 0, 0,0); /* samples are ok? */ - close_streamfile(temp_streamFile); + close_streamfile(temp_sf); if (!data->layers[i]->codec_data) goto fail; } @@ -125,13 +128,13 @@ VGMSTREAM * init_vgmstream_awc(STREAMFILE *streamFile) { block_size = 0x8000; /* no idea */ block_count = awc.stream_size / block_size; /* not accurate but not needed */ - bytes = ffmpeg_make_riff_xma2(buf, 0x100, awc.num_samples, awc.stream_size, awc.channel_count, awc.sample_rate, block_count, block_size); - vgmstream->codec_data = init_ffmpeg_header_offset(streamFile, buf,bytes, awc.stream_offset,awc.stream_size); + bytes = ffmpeg_make_riff_xma2(buf, 0x100, awc.num_samples, awc.stream_size, awc.channels, awc.sample_rate, block_count, block_size); + vgmstream->codec_data = init_ffmpeg_header_offset(sf, buf,bytes, awc.stream_offset,awc.stream_size); if (!vgmstream->codec_data) goto fail; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; - xma_fix_raw_samples(vgmstream, streamFile, awc.stream_offset,awc.stream_size, 0, 0,0); /* samples are ok? */ + xma_fix_raw_samples(vgmstream, sf, awc.stream_offset,awc.stream_size, 0, 0,0); /* samples are ok? */ } break; @@ -146,21 +149,43 @@ VGMSTREAM * init_vgmstream_awc(STREAMFILE *streamFile) { cfg.chunk_size = awc.block_chunk; cfg.big_endian = awc.big_endian; - vgmstream->codec_data = init_mpeg_custom(streamFile, awc.stream_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_AWC, &cfg); + vgmstream->codec_data = init_mpeg_custom(sf, awc.stream_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_AWC, &cfg); if (!vgmstream->codec_data) goto fail; vgmstream->layout_type = layout_none; break; } #endif +#ifdef VGM_USE_VORBIS + case 0x08: { /* Vorbis (PC) [Red Dead Redemption 2 (PC)] */ + if (awc.is_music) { + vgmstream->layout_data = build_layered_awc(sf, &awc); + if (!vgmstream->layout_data) goto fail; + vgmstream->layout_type = layout_layered; + vgmstream->coding_type = coding_FFmpeg; + } + else { + vorbis_custom_config cfg = {0}; + cfg.channels = awc.channels; + cfg.sample_rate = awc.sample_rate; + cfg.header_offset = awc.vorbis_offset[0]; + + vgmstream->codec_data = init_vorbis_custom(sf, awc.stream_offset, VORBIS_AWC, &cfg); + if (!vgmstream->codec_data) goto fail; + vgmstream->layout_type = layout_none; + vgmstream->coding_type = coding_VORBIS_custom; + } + break; + } +#endif default: VGM_LOG("AWC: unknown codec 0x%02x\n", awc.codec); goto fail; } - if (!vgmstream_open_stream(vgmstream,streamFile,awc.stream_offset)) + if (!vgmstream_open_stream(vgmstream, sf, awc.stream_offset)) goto fail; return vgmstream; @@ -172,38 +197,38 @@ fail: /* Parse Rockstar's AWC header (much info from LibertyV: https://github.com/koolkdev/libertyv). * Made of entries for N streams, each with a number of tags pointing to chunks (header, data, events, etc). */ -static int parse_awc_header(STREAMFILE* streamFile, awc_header* awc) { - int64_t (*read_64bit)(off_t,STREAMFILE*) = NULL; - int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL; - int16_t (*read_16bit)(off_t,STREAMFILE*) = NULL; +static int parse_awc_header(STREAMFILE* sf, awc_header* awc) { + uint64_t (*read_u64)(off_t,STREAMFILE*) = NULL; + uint32_t (*read_u32)(off_t,STREAMFILE*) = NULL; + uint16_t (*read_u16)(off_t,STREAMFILE*) = NULL; int i, ch, entries; uint32_t flags, info_header, tag_count = 0, tags_skip = 0; - off_t off; - int target_subsong = streamFile->stream_index; + off_t offset; + int target_subsong = sf->stream_index; /* check header */ - if (read_32bitBE(0x00,streamFile) != 0x41444154 && /* "ADAT" (LE) */ - read_32bitBE(0x00,streamFile) != 0x54414441) /* "TADA" (BE) */ + if (read_u32be(0x00,sf) != 0x41444154 && /* "ADAT" (LE) */ + read_u32be(0x00,sf) != 0x54414441) /* "TADA" (BE) */ goto fail; - awc->big_endian = read_32bitBE(0x00,streamFile) == 0x54414441; + awc->big_endian = read_u32be(0x00,sf) == 0x54414441; if (awc->big_endian) { - read_64bit = read_64bitBE; - read_32bit = read_32bitBE; - read_16bit = read_16bitBE; + read_u64 = read_u64be; + read_u32 = read_u32be; + read_u16 = read_u16be; } else { - read_64bit = read_64bitLE; - read_32bit = read_32bitLE; - read_16bit = read_16bitLE; + read_u64 = read_u64le; + read_u32 = read_u32le; + read_u16 = read_u16le; } - flags = read_32bit(0x04,streamFile); - entries = read_32bit(0x08,streamFile); - //header_size = read_32bit(0x0c,streamFile); /* after to stream id/tags, not including chunks */ + flags = read_u32(0x04,sf); + entries = read_u32(0x08,sf); + //header_size = read_u32(0x0c,sf); /* after to stream id/tags, not including chunks */ - off = 0x10; + offset = 0x10; if ((flags & 0xFF00FFFF) != 0xFF000001 || (flags & 0x00F00000)) { VGM_LOG("AWC: unknown flags 0x%08x\n", flags); @@ -211,7 +236,7 @@ static int parse_awc_header(STREAMFILE* streamFile, awc_header* awc) { } if (flags & 0x00010000) /* some kind of mini offset table */ - off += 0x2 * entries; + offset += 0x2 * entries; //if (flags % 0x00020000) /* seems to indicate chunks are not ordered (ie. header may go after data) */ // ... //if (flags % 0x00040000) /* music/multichannel flag? (GTA5, not seen in RDR) */ @@ -227,7 +252,7 @@ static int parse_awc_header(STREAMFILE* streamFile, awc_header* awc) { /* Music when the first id is 0 (base/fake entry with info for all channels), sfx pack otherwise. * sfx = N single streams, music = N-1 interleaved mono channels (even for MP3/XMA). * Music seems layered (N-1/2 stereo pairs), maybe set with events? */ - awc->is_music = (read_32bit(off + 0x00,streamFile) & 0x1FFFFFFF) == 0x00000000; + awc->is_music = (read_u32(offset + 0x00,sf) & 0x1FFFFFFF) == 0x00000000; if (awc->is_music) { /* all streams except id 0 is a channel */ awc->total_subsongs = 1; target_subsong = 1; /* we only need id 0, though channels may have its own tags/chunks */ @@ -241,65 +266,69 @@ static int parse_awc_header(STREAMFILE* streamFile, awc_header* awc) { /* get stream base info */ for (i = 0; i < entries; i++) { - info_header = read_32bit(off + 0x04*i, streamFile); + info_header = read_u32(offset + 0x04*i, sf); tag_count = (info_header >> 29) & 0x7; /* 3b */ //id = (info_header >> 0) & 0x1FFFFFFF; /* 29b */ if (target_subsong-1 == i) break; tags_skip += tag_count; /* tags to skip to reach target's tags, in the next header */ } - off += 0x04*entries; - off += 0x08*tags_skip; + offset += 0x04*entries; + offset += 0x08*tags_skip; /* get stream tags */ for (i = 0; i < tag_count; i++) { uint64_t tag_header; - uint8_t tag; - size_t size; - off_t offset; + uint8_t tag_type; + size_t tag_size; + off_t tag_offset; - tag_header = (uint64_t)read_64bit(off + 0x08*i,streamFile); - tag = (uint8_t)((tag_header >> 56) & 0xFF); /* 8b */ - size = (size_t)((tag_header >> 28) & 0x0FFFFFFF); /* 28b */ - offset = (off_t)((tag_header >> 0) & 0x0FFFFFFF); /* 28b */ + tag_header = read_u64(offset + 0x08*i,sf); + tag_type = (uint8_t)((tag_header >> 56) & 0xFF); /* 8b */ + tag_size = (size_t)((tag_header >> 28) & 0x0FFFFFFF); /* 28b */ + tag_offset = (off_t)((tag_header >> 0) & 0x0FFFFFFF); /* 28b */ + ;VGM_LOG("AWC: tag%i/%i at %lx: t=%x, o=%lx, s=%x\n", i, tag_count, offset + 0x08*i, tag_type, tag_offset, tag_size); /* Tags are apparently part of a hash derived from a word ("data", "format", etc). * If music + 1ch, the header and data chunks can repeat for no reason (sometimes not even pointed). */ - switch(tag) { + switch(tag_type) { case 0x55: /* data */ - awc->stream_offset = offset; - awc->stream_size = size; + awc->stream_offset = tag_offset; + awc->stream_size = tag_size; break; case 0x48: /* music header */ + if (!awc->is_music) { VGM_LOG("AWC: music header found in sfx\n"); goto fail; } /* 0x00(32): unknown (some count?) */ - awc->block_chunk = read_32bit(offset + 0x04,streamFile); - awc->channel_count = read_32bit(offset + 0x08,streamFile); + awc->block_chunk = read_u32(tag_offset + 0x04,sf); + awc->channels = read_u32(tag_offset + 0x08,sf); - if (awc->channel_count != entries - 1) { /* not counting id-0 */ + if (awc->channels != entries - 1) { /* not counting id-0 */ VGM_LOG("AWC: number of music channels doesn't match entries\n"); goto fail; } - for (ch = 0; ch < awc->channel_count; ch++) { + for (ch = 0; ch < awc->channels; ch++) { int num_samples, sample_rate, codec; - /* 0x00(32): stream id (not always in the header entries order) */ - /* 0x08(16): headroom?, 0x0d(8): round size?, 0x0e(16): unknown (zero?) */ - num_samples = read_32bit(offset + 0x0c + 0x10*ch + 0x04,streamFile); - sample_rate = (uint16_t)read_16bit(offset + 0x0c + 0x10*ch + 0x0a,streamFile); - codec = read_8bit(offset + 0x0c + 0x10*ch + 0x0c, streamFile); + /* 0x00): stream id (not always in the header entries order) */ + num_samples = read_u32(tag_offset + 0x0c + 0x10*ch + 0x04,sf); + /* 0x08: headroom */ + sample_rate = read_u16(tag_offset + 0x0c + 0x10*ch + 0x0a,sf); + codec = read_u8(tag_offset + 0x0c + 0x10*ch + 0x0c,sf); + /* 0x0d(8): round size? */ + /* 0x0e: unknown (zero/-1) */ /* validate channels differences */ if ((awc->num_samples && !(awc->num_samples >= num_samples - 10 && awc->num_samples <= num_samples + 10)) || (awc->sample_rate && awc->sample_rate != sample_rate)) { VGM_LOG("AWC: found header diffs in channel %i, ns=%i vs %i, sr=%i vs %i\n", ch, awc->num_samples, num_samples, awc->sample_rate, sample_rate); - /* sometimes (often cutscenes in Max Payne 3 and RDR DLC) channels have bif sample diffs, + /* sometimes (often cutscenes in Max Payne 3 and RDR DLC) channels have sample diffs, * probably one stream is simply silent after its samples end */ } @@ -313,6 +342,7 @@ static int parse_awc_header(STREAMFILE* streamFile, awc_header* awc) { awc->sample_rate = sample_rate; awc->codec = codec; } + break; case 0xFA: /* sfx header */ @@ -320,16 +350,39 @@ static int parse_awc_header(STREAMFILE* streamFile, awc_header* awc) { VGM_LOG("AWC: sfx header found in music\n"); goto fail; } - /* 0x04(32): -1?, 0x0a(16x4): unknown x4, 0x12: null? */ - awc->num_samples = read_32bit(offset + 0x00,streamFile); - awc->sample_rate = (uint16_t)read_16bit(offset + 0x08,streamFile); - awc->codec = read_8bit(offset + 0x13, streamFile); - awc->channel_count = 1; + + awc->num_samples = read_u32(tag_offset + 0x00,sf); + /* 0x04: -1? */ + awc->sample_rate = read_u16(tag_offset + 0x08,sf); + /* 0x0a: unknown x4 */ + /* 0x12: null? */ + awc->codec = read_u8(tag_offset + 0x13, sf); + awc->channels = 1; + break; + + case 0x76: /* sfx header for vorbis */ + if (awc->is_music) { + VGM_LOG("AWC: sfx header found in music\n"); + goto fail; + } + + awc->num_samples = read_u32(tag_offset + 0x00,sf); + /* 0x04: -1? */ + awc->sample_rate = read_u16(tag_offset + 0x08,sf); + /* 0x0a: granule start? (negative) */ + /* 0x0c: granule max? */ + /* 0x10: unknown */ + awc->codec = read_u8(tag_offset + 0x1c, sf); /* 16b? */ + /* 0x1e: vorbis header size */ + awc->channels = 1; + + awc->vorbis_offset[0] = tag_offset + 0x20; break; case 0xA3: /* block-to-sample table (32b x number of blocks w/ num_samples at the start of each block) */ case 0xBD: /* events (32bx4): type_hash, params_hash, timestamp_ms, flags */ - default: /* 0x5C=animation/RSC?, 0x68=midi?, 0x36/0x2B/0x5A/0xD9=? */ + case 0x5C: /* animation/RSC config? */ + default: /* 0x68=midi?, 0x36=hash thing?, 0x2B=sizes, 0x5A/0xD9=? */ //VGM_LOG("AWC: ignoring unknown tag 0x%02x\n", tag); break; } @@ -340,11 +393,27 @@ static int parse_awc_header(STREAMFILE* streamFile, awc_header* awc) { goto fail; } - /* If music, data is divided into blocks of block_chunk size with padding. + /* vorbis offset table, somehow offsets are unordered and can go before tags */ + if (awc->is_music && awc->codec == 0x08) { + offset += 0x08 * tag_count; + + for (ch = 0; ch < awc->channels; ch++) { + awc->vorbis_offset[ch] = read_u16(offset + 0x08*ch + 0x00, sf); + /* 0x02: always 0xB000? */ + /* 0x04: always 0x00CD? */ + /* 0x06: always 0x7F00? */ + } + } + + + /* In music mode, data is divided into blocks of block_chunk size with padding. * Each block has a header/seek table and interleaved data for all channels */ - if (awc->is_music && read_32bit(awc->stream_offset, streamFile) != 0) { - VGM_LOG("AWC: music found, but block doesn't start with seek table at %x\n", (uint32_t)awc->stream_offset); - goto fail; + { + int32_t seek_start = read_u32(awc->stream_offset, sf); /* -1 in later (RDR2) versions */ + if (awc->is_music && !(seek_start == 0 || seek_start == -1)) { + VGM_LOG("AWC: music found, but block doesn't start with seek table at %x\n", (uint32_t)awc->stream_offset); + goto fail; + } } @@ -352,3 +421,164 @@ static int parse_awc_header(STREAMFILE* streamFile, awc_header* awc) { fail: return 0; } + +/* ************************************************************************* */ + +//TODO: this method won't work properly, needs internal handling of blocks. +// +// This setups a decoder per block, but seems Vorbis' uses first frame as setup so it +// returns samples (576 vs 1024), making num_samples count in each block being off + causing +// gaps. So they must be using a single encoder + setting decode_to_discard per block +// to ge the thing working. +// +// However since blocks are probably also used for seeking, maybe they aren't resetting +// the decoder when seeking? or they force first frame to be 1024? +// +// In case of Vorvis, when setting skip samples seems repeated data from last block is +// exactly last 0x800 bytes of that channel. + +static VGMSTREAM* build_block_vgmstream(STREAMFILE* sf, awc_header* awc, int channel, int32_t num_samples, int32_t skip_samples, off_t block_start, size_t block_size) { + STREAMFILE* temp_sf = NULL; + VGMSTREAM* vgmstream = NULL; + int block_channels = 1; + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(block_channels, 0); + if (!vgmstream) goto fail; + + vgmstream->sample_rate = awc->sample_rate; + vgmstream->num_samples = num_samples - skip_samples; + vgmstream->stream_size = block_size; + vgmstream->meta_type = meta_AWC; + + switch(awc->codec) { +#ifdef VGM_USE_VORBIS + case 0x08: { /* Vorbis (PC) [Red Dead Redemption 2 (PC)] */ + vorbis_custom_config cfg = {0}; + + cfg.channels = 1; + cfg.sample_rate = awc->sample_rate; + cfg.header_offset = awc->vorbis_offset[channel]; + //cfg.skip_samples = skip_samples; //todo + + vgmstream->codec_data = init_vorbis_custom(sf, block_start, VORBIS_AWC, &cfg); + if (!vgmstream->codec_data) goto fail; + vgmstream->layout_type = layout_none; + vgmstream->coding_type = coding_VORBIS_custom; + } + break; +#endif + default: + goto fail; + } + + if (!vgmstream_open_stream(vgmstream, sf, block_start)) + goto fail; + close_streamfile(temp_sf); + return vgmstream; +fail: + close_streamfile(temp_sf); + close_vgmstream(vgmstream); + return NULL; +} + +static VGMSTREAM* build_blocks_vgmstream(STREAMFILE* sf, awc_header* awc, int channel) { + VGMSTREAM* vgmstream = NULL; + segmented_layout_data* data = NULL; + int i, ch; + int blocks = awc->stream_size / awc->block_chunk + (awc->stream_size % awc->block_chunk ? 1 : 0) ; + + + /* init layout */ + data = init_layout_segmented(blocks); + if (!data) goto fail; + + /* one segment per block of this channel */ + for (i = 0; i < blocks; i++) { + off_t block_offset = awc->stream_offset + i * awc->block_chunk; + int32_t num_samples = 0, skip_samples = 0; + uint32_t header_skip = 0, block_skip = 0, block_start = 0, block_data = 0; + + /* read stupid block crap to get proper offsets and whatnot, format: + * - per channel: number of channel entries + skip samples + num samples + * - per channel: seek table with N entries */ + for (ch = 0; ch < awc->channels; ch++) { + /* 0x00: -1 */ + int entries = read_u32le(block_offset + 0x18 * ch + 0x04, sf); + int32_t entry_skip = read_u32le(block_offset + 0x18 * ch + 0x08, sf); + int32_t entry_samples = read_u32le(block_offset + 0x18 * ch + 0x0c, sf); + + if (ch == channel) { + num_samples = entry_samples; + skip_samples = entry_skip; + + block_start = block_offset + block_skip; + block_data = entries * 0x800; + } + + header_skip += 0x18 + entries * 0x04; + block_skip += entries * 0x800; + } + + if (!block_start) + goto fail; + + header_skip = align_size_to_block(header_skip, 0x800); + block_start += header_skip; + //;VGM_LOG("AWC: build ch%i, block=%i at %lx, o=%x, s=%x, ns=%i, ss=%i\n", channel, i, block_offset, block_start, block_data, num_samples, skip_samples); + + data->segments[i] = build_block_vgmstream(sf, awc, channel, num_samples, skip_samples, block_start, block_data); + if (!data->segments[i]) goto fail; + } + + /* setup VGMSTREAMs */ + if (!setup_layout_segmented(data)) + goto fail; + + /* build the layout VGMSTREAM */ + vgmstream = allocate_segmented_vgmstream(data, 0, 0, 0); + if (!vgmstream) goto fail; + + return vgmstream; +fail: + close_vgmstream(vgmstream); + if (!vgmstream) + free_layout_segmented(data); + return NULL; +} + +/* ************************************************************************* */ + +/* Make layers per channel for AWC's abhorrent blocks. + * + * File has N channels = N streams, that use their own mono decoder. + * Each block then has header + seek table for all channels. But in each block there is + * a "skip samples" value per channel, and blocks repeat some data from last block + * for this, so PCM must be discarded. Also, channels in a block don't need to have + * the same number of samples. + */ +static layered_layout_data* build_layered_awc(STREAMFILE* sf, awc_header* awc) { + int i; + layered_layout_data* data = NULL; + + + /* init layout */ + data = init_layout_layered(awc->channels); + if (!data) goto fail; + + /* open each layer subfile */ + for (i = 0; i < awc->channels; i++) { + data->layers[i] = build_blocks_vgmstream(sf, awc, i); + if (!data->layers[i]) goto fail; + } + + /* setup layered VGMSTREAMs */ + if (!setup_layout_layered(data)) + goto fail; + return data; +fail: + free_layout_layered(data); + return NULL; +} + diff --git a/Frameworks/vgmstream/vgmstream/src/meta/bmp_konami.c b/Frameworks/vgmstream/vgmstream/src/meta/bmp_konami.c index 32c6e49b7..ef0925094 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/bmp_konami.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/bmp_konami.c @@ -1,45 +1,44 @@ -#include "meta.h" - - -/* BMP - from Jubeat series (AC) */ -VGMSTREAM * init_vgmstream_bmp_konami(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - off_t start_offset; - int loop_flag, channel_count; - - - /* checks */ - /* .bin: actual extension - * .lbin: for plugins */ - if (!check_extensions(streamFile, "bin,lbin")) - goto fail; - - if (read_32bitBE(0x00,streamFile) != 0x424D5000) /* "BMP\0" "*/ - goto fail; - - channel_count = read_8bit(0x10,streamFile); /* assumed */ - if (channel_count != 2) goto fail; - loop_flag = 0; - start_offset = 0x20; - - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count, loop_flag); - if (!vgmstream) goto fail; - - vgmstream->meta_type = meta_BMP_KONAMI; - - vgmstream->num_samples = read_32bitBE(0x04,streamFile); - vgmstream->sample_rate = read_32bitBE(0x14, streamFile); - - vgmstream->coding_type = coding_OKI4S; - 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" + + +/* BMP - from Konami arcade games [drummania (AC), GITADORA (AC), Jubeat (AC)] */ +VGMSTREAM* init_vgmstream_bmp_konami(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + off_t start_offset; + int loop_flag, channels; + + + /* checks */ + /* .bin: actual extension + * .lbin: for plugins */ + if (!check_extensions(sf, "bin,lbin")) + goto fail; + + if (!is_id32be(0x00,sf, "BMP\0")) + goto fail; + + channels = read_u8(0x10,sf); /* assumed */ + if (channels != 2) goto fail; + loop_flag = 0; + start_offset = 0x20; + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channels, loop_flag); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_BMP_KONAMI; + + vgmstream->num_samples = read_u32be(0x04,sf); + vgmstream->sample_rate = read_u32be(0x14, sf); + + vgmstream->coding_type = coding_OKI4S; + 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/ea_eaac.c b/Frameworks/vgmstream/vgmstream/src/meta/ea_eaac.c index 99c834b3a..53b720be2 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ea_eaac.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ea_eaac.c @@ -806,29 +806,31 @@ fail: /* EA Harmony Sample Bank - used in 8th gen EA Sports games */ VGMSTREAM * init_vgmstream_ea_sbr_harmony(STREAMFILE *sf) { - uint32_t num_dsets, set_sounds, chunk_id, data_offset, table_offset, dset_offset, base_offset, sound_table_offset, sound_offset; - uint32_t i, j; + uint64_t set_sounds, base_offset, sound_offset; + uint32_t chunk_id, data_offset, table_offset, dset_offset, sound_table_offset; + uint16_t num_dsets; uint8_t set_type, flag, offset_size; + uint32_t i, j; char sound_name[STREAM_NAME_SIZE]; - STREAMFILE *sbsFile = NULL, *sf_data = NULL; + STREAMFILE *sf_sbs = NULL, *sf_data = NULL; VGMSTREAM *vgmstream = NULL; int target_stream = sf->stream_index, total_sounds, local_target, is_streamed = 0; + uint64_t(*read_u64)(off_t, STREAMFILE *); uint32_t(*read_u32)(off_t, STREAMFILE*); uint16_t(*read_u16)(off_t, STREAMFILE*); if (!check_extensions(sf, "sbr")) goto fail; - /* Logically, big endian version starts with SBbe. However, this format is - * only used on 8th gen systems so far so big endian version probably doesn't exist. */ - if (read_32bitBE(0x00, sf) == 0x53426C65) { /* "SBle" */ + /* check header */ + if (read_u32be(0x00, sf) == 0x53426C65) { /* "SBle" */ + read_u64 = read_u64le; read_u32 = read_u32le; read_u16 = read_u16le; -#if 0 - } else if (read_32bitBE(0x00, sf) == 0x53426265) { /* "SBbe" */ - read_32bit = read_u32be; - read_16bit = read_u16be; -#endif + } else if (read_u32be(0x00, sf) == 0x53426265) { /* "SBbe" */ + read_u64 = read_u64be; + read_u32 = read_u32be; + read_u16 = read_u16be; } else { goto fail; } @@ -880,13 +882,13 @@ VGMSTREAM * init_vgmstream_ea_sbr_harmony(STREAMFILE *sf) { if (local_target < 0 || local_target > 0) continue; - sound_offset = read_u32(dset_offset + 0x08, sf); + sound_offset = read_u64(dset_offset + 0x08, sf); } else if (set_type == 0x01) { total_sounds += 2; if (local_target < 0 || local_target > 1) continue; - base_offset = read_u32(dset_offset + 0x08, sf); + base_offset = read_u64(dset_offset + 0x08, sf); if (local_target == 0) { sound_offset = base_offset; @@ -894,9 +896,9 @@ VGMSTREAM * init_vgmstream_ea_sbr_harmony(STREAMFILE *sf) { sound_offset = base_offset + read_u16(dset_offset + 0x06, sf); } } else if (set_type == 0x02) { - flag = read_u8(dset_offset + 0x06, sf); - offset_size = read_u8(dset_offset + 0x07, sf); - base_offset = read_u32(dset_offset + 0x08, sf); + flag = (read_u16(dset_offset + 0x06, sf) >> 0) & 0xFF; + offset_size = (read_u16(dset_offset + 0x06, sf) >> 8) & 0xFF; + base_offset = read_u64(dset_offset + 0x08, sf); sound_table_offset = read_u32(dset_offset + 0x10, sf); total_sounds += set_sounds; @@ -911,12 +913,14 @@ VGMSTREAM * init_vgmstream_ea_sbr_harmony(STREAMFILE *sf) { for (j = 0; j < flag; j++) sound_offset *= 2; } else if (offset_size == 0x04) { sound_offset = read_u32(sound_table_offset + 0x04 * local_target, sf); + } else { + goto fail; } sound_offset += base_offset; } else if (set_type == 0x03) { - offset_size = read_u8(dset_offset + 0x07, sf); - set_sounds = read_u32(dset_offset + 0x08, sf); + offset_size = (read_u16(dset_offset + 0x06, sf) >> 8) & 0xFF; + set_sounds = read_u64(dset_offset + 0x08, sf); sound_table_offset = read_u32(dset_offset + 0x10, sf); total_sounds += set_sounds; @@ -929,6 +933,8 @@ VGMSTREAM * init_vgmstream_ea_sbr_harmony(STREAMFILE *sf) { sound_offset = read_u16(sound_table_offset + 0x02 * local_target, sf); } else if (offset_size == 0x04) { sound_offset = read_u32(sound_table_offset + 0x04 * local_target, sf); + } else { + goto fail; } } else if (set_type == 0x04) { total_sounds += set_sounds; @@ -955,23 +961,25 @@ VGMSTREAM * init_vgmstream_ea_sbr_harmony(STREAMFILE *sf) { if (!is_streamed) { /* RAM asset */ - if (read_32bitBE(data_offset, sf) != 0x64617461) /* "data" */ + if (read_u32be(data_offset, sf) != 0x64617461 && /* "data" */ + read_u32be(data_offset, sf) != 0x44415441) /* "DATA" */ goto fail; sf_data = sf; sound_offset += data_offset; } else { /* streamed asset */ - sbsFile = open_streamfile_by_ext(sf, "sbs"); - if (!sbsFile) + sf_sbs = open_streamfile_by_ext(sf, "sbs"); + if (!sf_sbs) goto fail; - if (read_32bitBE(0x00, sbsFile) != 0x64617461) /* "data" */ + if (read_u32be(0x00, sf_sbs) != 0x64617461 && /* "data" */ + read_u32be(0x00, sf_sbs) != 0x44415441) /* "DATA" */ goto fail; - sf_data = sbsFile; + sf_data = sf_sbs; - if (read_32bitBE(sound_offset, sf_data) == 0x736C6F74) { + if (read_u32be(sound_offset, sf_data) == 0x736C6F74) { /* skip "slot" section */ sound_offset += 0x30; } @@ -983,11 +991,11 @@ VGMSTREAM * init_vgmstream_ea_sbr_harmony(STREAMFILE *sf) { vgmstream->num_streams = total_sounds; strncpy(vgmstream->stream_name, sound_name, STREAM_NAME_SIZE); - close_streamfile(sbsFile); + close_streamfile(sf_sbs); return vgmstream; fail: - close_streamfile(sbsFile); + close_streamfile(sf_sbs); return NULL; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ffmpeg.c b/Frameworks/vgmstream/vgmstream/src/meta/ffmpeg.c index 3d2d48a04..084cf5187 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ffmpeg.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ffmpeg.c @@ -6,7 +6,20 @@ static int read_pos_file(uint8_t* buf, size_t bufsize, STREAMFILE* sf); static int find_ogg_loops(ffmpeg_codec_data* data, int32_t* p_loop_start, int32_t* p_loop_end); -/* parses any file supported by FFmpeg and not handled elsewhere (mainly: MP4/AAC, MP3, MPC, FLAC) */ +/* parses any format supported by FFmpeg and not handled elsewhere: + * - MP3 (.mp3, .mus): Marc Ecko's Getting Up (PC) + * - MPC (.mpc): Moonshine Runners (PC), Asphalt 7 (PC) + * - FLAC (.flac): Warcraft 3 Reforged (PC), Call of Duty: Ghosts (PC) + * - DUCK (.wav): Sonic Jam (SAT), Virtua Fighter 2 (SAT) + * - ALAC/AAC (.caf): Chrono Trigger (iOS) + * - ATRAC3 (.oma, .aa3): demuxed PSP/PS3 videos + * - WMA/WMAPRO (.wma): Castlevania Symphony of the Night (Xbox) + * - AC3 (.ac3): some PS2 games + * + * May also catch files that are supported elsewhere but rejected due to bugs, + * but those should be fixed in their parser for proper loops/etc support + * (catch-all behavior may be disabled later). + */ VGMSTREAM* init_vgmstream_ffmpeg(STREAMFILE* sf) { VGMSTREAM* vgmstream = NULL; ffmpeg_codec_data* data = NULL; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/hca_keys.h b/Frameworks/vgmstream/vgmstream/src/meta/hca_keys.h index d4c0764d5..4c9daf378 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/hca_keys.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/hca_keys.h @@ -382,6 +382,9 @@ static const hcakey_info hcakey_list[] = { /* Readyyy! (Android) */ {1234567890987654321}, // 112210F4B16C1CB1 + /* HoneyWorks Premium Live (Android) */ + {20200401000000}, // 0000125F45B9D640 + /* Dragalia Lost (iOS/Android) */ {2967411924141, subkeys_dgl, sizeof(subkeys_dgl) / sizeof(subkeys_dgl[0]) }, // 000002B2E7889CAD diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ifs.c b/Frameworks/vgmstream/vgmstream/src/meta/ifs.c new file mode 100644 index 000000000..b81bf15a7 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/ifs.c @@ -0,0 +1,67 @@ +#include "meta.h" +#include "../coding/coding.h" + + +/* .ifs - Konami arcade games container [drummania (AC), GITADORA (AC)] */ +VGMSTREAM* init_vgmstream_ifs(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + STREAMFILE* temp_sf = NULL; + off_t subfile_offset, subfile_size; + int total_subsongs, target_subsong = sf->stream_index; + + + /* checks */ + if (!check_extensions(sf, "ifs")) + goto fail; + + if (read_u32be(0x00,sf) != 0x6CAD8F89) + goto fail; + if (read_u16be(0x04,sf) != 0x0003) + goto fail; + + /* .ifs format is a binary XML thing with types/fields/nodes/etc, that sometimes + * contains Konami's BMP as subsongs (may differ in num_samples). This is an + * abridged version of the whole thing, see: + * - https://github.com/mon/ifstools + * - https://github.com/mon/kbinxml + * - https://bitbucket.org/ahigerd/gitadora2wav/ + */ + + { + off_t offset, size, subfile_start; + + subfile_start = read_u32be(0x10,sf); + + /* skip root section and point to childs */ + offset = 0x28 + 0x04 + read_u32be(0x28,sf); + size = read_u32be(offset + 0x00,sf); + offset += 0x04; + + /* point to subfile offsets */ + size = size - 0x04 - read_u32be(offset + 0x00,sf) - 0x04; + offset = offset + 0x04 + read_u32be(offset + 0x00,sf) + 0x04; + + total_subsongs = size / 0x0c; + if (target_subsong == 0) target_subsong = 1; + if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail; + + subfile_offset = read_u32be(offset + (target_subsong-1)*0x0c + 0x00,sf) + subfile_start; + subfile_size = read_u32be(offset + (target_subsong-1)*0x0c + 0x04,sf); + } + + temp_sf = setup_subfile_streamfile(sf, subfile_offset, subfile_size, "bin"); + if (!temp_sf) goto fail; + + vgmstream = init_vgmstream_bmp_konami(temp_sf); + if (!vgmstream) goto fail; + + vgmstream->num_streams = total_subsongs; + /* subsongs have names but are packed in six-bit strings */ + + close_streamfile(temp_sf); + return vgmstream; +fail: + close_streamfile(temp_sf); + close_vgmstream(vgmstream); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/meta.h b/Frameworks/vgmstream/vgmstream/src/meta/meta.h index c19b9c136..95b372323 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/meta.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/meta.h @@ -42,7 +42,7 @@ VGMSTREAM* init_vgmstream_wii_was(STREAMFILE* sf); VGMSTREAM* init_vgmstream_dsp_str_ig(STREAMFILE* sf); VGMSTREAM* init_vgmstream_dsp_xiii(STREAMFILE* sf); VGMSTREAM* init_vgmstream_dsp_cabelas(STREAMFILE* sf); -VGMSTREAM* init_vgmstream_wii_ndp(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_dsp_ndp(STREAMFILE* sf); VGMSTREAM* init_vgmstream_ngc_dsp_aaap(STREAMFILE* sf); VGMSTREAM* init_vgmstream_dsp_dspw(STREAMFILE* sf); VGMSTREAM* init_vgmstream_ngc_dsp_iadp(STREAMFILE* sf); @@ -56,6 +56,8 @@ VGMSTREAM* init_vgmstream_dsp_ds2(STREAMFILE* sf); VGMSTREAM* init_vgmstream_dsp_itl(STREAMFILE* sf); VGMSTREAM* init_vgmstream_dsp_sqex(STREAMFILE* sf); VGMSTREAM* init_vgmstream_dsp_wiivoice(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_dsp_wiiadpcm(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_dsp_cwac(STREAMFILE* sf); VGMSTREAM * init_vgmstream_csmp(STREAMFILE *streamFile); @@ -928,4 +930,8 @@ VGMSTREAM* init_vgmstream_wady(STREAMFILE* sf); VGMSTREAM* init_vgmstream_cpk(STREAMFILE* sf); VGMSTREAM* init_vgmstream_cpk_memory(STREAMFILE* sf, STREAMFILE* sf_acb); +VGMSTREAM *init_vgmstream_sbk(STREAMFILE *sf); + +VGMSTREAM* init_vgmstream_ifs(STREAMFILE* sf); + #endif /*_META_H*/ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/naac.c b/Frameworks/vgmstream/vgmstream/src/meta/naac.c index 9f649cf45..f8b7fd536 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/naac.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/naac.c @@ -1,51 +1,56 @@ #include "meta.h" #include "../coding/coding.h" + /* .NAAC - from Namco 3DS games (Ace Combat - Assault Horizon Legacy, Taiko no Tatsujin Don to Katsu no Jikuu Daibouken) */ VGMSTREAM* init_vgmstream_naac(STREAMFILE* sf) { VGMSTREAM* vgmstream = NULL; off_t start_offset; - int loop_flag, channel_count; + int loop_flag, channels; size_t data_size; - /* check extension */ - if ( !check_extensions(sf,"naac") ) + + /* checks */ + if (!check_extensions(sf,"naac")) goto fail; - /* check header */ - if (read_32bitBE(0x00,sf) != 0x41414320) /* "AAC " */ + if (read_u32be(0x00,sf) != 0x41414320) /* "AAC " */ goto fail; - if (read_32bitLE(0x04,sf) != 0x01) /* version? */ + if (read_u32le(0x04,sf) != 0x01) /* version? */ goto fail; start_offset = 0x1000; - loop_flag = (read_32bitLE(0x18,sf) != 0); - channel_count = read_32bitLE(0x08,sf); + loop_flag = (read_s32le(0x18,sf) != 0); + channels = read_s32le(0x08,sf); /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); + vgmstream = allocate_vgmstream(channels, loop_flag); if (!vgmstream) goto fail; - vgmstream->sample_rate = read_32bitLE(0x0c,sf); - vgmstream->num_samples = read_32bitLE(0x10,sf); /* without skip_samples */ - vgmstream->loop_start_sample = read_32bitLE(0x14,sf); /* with skip_samples */ - vgmstream->loop_end_sample = read_32bitLE(0x18,sf); - /* 0x1c: loop start offset, 0x20: loop end offset (within data) */ - data_size = read_32bitLE(0x24,sf); - /* 0x28: unknown; 0x2c: table start offset?; 0x30: seek table (always 0xFD0, padded) */ + vgmstream->sample_rate = read_s32le(0x0c,sf); + vgmstream->num_samples = read_s32le(0x10,sf); /* without skip_samples */ + vgmstream->loop_start_sample = read_s32le(0x14,sf); /* with skip_samples */ + vgmstream->loop_end_sample = read_s32le(0x18,sf) + 1; /* without skip_samples */ + /* 0x1c: loop start offset */ + /* 0x20: loop end offset (within data) */ + data_size = read_u32le(0x24,sf); + /* 0x28: unknown */ + /* 0x2c: table start offset? */ + /* 0x30: seek table (always 0xFD0, padded) */ vgmstream->meta_type = meta_NAAC; #ifdef VGM_USE_FFMPEG { - vgmstream->codec_data = init_ffmpeg_offset(sf, start_offset,data_size); + vgmstream->codec_data = init_ffmpeg_offset(sf, start_offset, data_size); if (!vgmstream->codec_data) goto fail; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; /* observed default, some files start without silence though seems correct when loop_start=0 */ ffmpeg_set_skip_samples(vgmstream->codec_data, 1024); /* raw AAC doesn't set this */ - vgmstream->num_samples -= 1024; + vgmstream->num_samples -= 1024; /* may end with 1024 of silence? */ + vgmstream->loop_end_sample -= 1024; } #else goto fail; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ngc_dsp_std.c b/Frameworks/vgmstream/vgmstream/src/meta/ngc_dsp_std.c index 50a8cb081..e56bf6e93 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ngc_dsp_std.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ngc_dsp_std.c @@ -7,19 +7,19 @@ * then this is actually how they are laid out in the file, albeit big-endian */ struct dsp_header { uint32_t sample_count; /* 0x00 */ - uint32_t nibble_count; /* 0x04 */ - uint32_t sample_rate; /* 0x08 */ + uint32_t nibble_count; /* 0x04 (includes frame headers) */ + uint32_t sample_rate; /* 0x08 (generally 32/48kz but games like Wario World set 32028hz to adjust for GC's rate) */ uint16_t loop_flag; /* 0x0c */ - uint16_t format; /* 0x0e */ + uint16_t format; /* 0x0e (always 0 for ADPCM) */ uint32_t loop_start_offset; /* 0x10 */ uint32_t loop_end_offset; /* 0x14 */ - uint32_t ca; /* 0x18 */ - int16_t coef[16]; /* 0x1c (really 8x2) */ - uint16_t gain; /* 0x3c */ - uint16_t initial_ps; /* 0x3e */ + uint32_t ca; /* 0x18 (always 0) */ + int16_t coef[16]; /* 0x1c (eight pairs) */ + uint16_t gain; /* 0x3c (always 0 for ADPCM) */ + uint16_t initial_ps; /* 0x3e (predictor/scale in frame header) */ int16_t initial_hist1; /* 0x40 */ int16_t initial_hist2; /* 0x42 */ - uint16_t loop_ps; /* 0x44 */ + uint16_t loop_ps; /* 0x44 (predictor/scale in loop frame header) */ int16_t loop_hist1; /* 0x46 */ int16_t loop_hist2; /* 0x48 */ int16_t channel_count; /* 0x4a (DSPADPCM.exe ~v2.7 extension) */ @@ -29,34 +29,35 @@ struct dsp_header { /* read the above struct; returns nonzero on failure */ static int read_dsp_header_endian(struct dsp_header *header, off_t offset, STREAMFILE* sf, int big_endian) { - int32_t (*get_32bit)(const uint8_t *) = big_endian ? get_32bitBE : get_32bitLE; - int16_t (*get_16bit)(const uint8_t *) = big_endian ? get_16bitBE : get_16bitLE; + uint32_t (*get_u32)(const uint8_t*) = big_endian ? get_u32be : get_u32le; + uint16_t (*get_u16)(const uint8_t*) = big_endian ? get_u16be : get_u16le; + int16_t (*get_s16)(const uint8_t*) = big_endian ? get_s16be : get_s16le; int i; - uint8_t buf[0x4e]; + uint8_t buf[0x60]; if (offset > get_streamfile_size(sf)) return 1; - if (read_streamfile(buf, offset, 0x4e, sf) != 0x4e) + if (read_streamfile(buf, offset, 0x60, sf) != 0x60) return 1; - header->sample_count = get_32bit(buf+0x00); - header->nibble_count = get_32bit(buf+0x04); - header->sample_rate = get_32bit(buf+0x08); - header->loop_flag = get_16bit(buf+0x0c); - header->format = get_16bit(buf+0x0e); - header->loop_start_offset = get_32bit(buf+0x10); - header->loop_end_offset = get_32bit(buf+0x14); - header->ca = get_32bit(buf+0x18); + header->sample_count = get_u32(buf+0x00); + header->nibble_count = get_u32(buf+0x04); + header->sample_rate = get_u32(buf+0x08); + header->loop_flag = get_u16(buf+0x0c); + header->format = get_u16(buf+0x0e); + header->loop_start_offset = get_u32(buf+0x10); + header->loop_end_offset = get_u32(buf+0x14); + header->ca = get_u32(buf+0x18); for (i=0; i < 16; i++) - header->coef[i] = get_16bit(buf+0x1c+i*0x02); - header->gain = get_16bit(buf+0x3c); - header->initial_ps = get_16bit(buf+0x3e); - header->initial_hist1 = get_16bit(buf+0x40); - header->initial_hist2 = get_16bit(buf+0x42); - header->loop_ps = get_16bit(buf+0x44); - header->loop_hist1 = get_16bit(buf+0x46); - header->loop_hist2 = get_16bit(buf+0x48); - header->channel_count = get_16bit(buf+0x4a); - header->block_size = get_16bit(buf+0x4c); + header->coef[i] = get_s16(buf+0x1c+i*0x02); + header->gain = get_u16(buf+0x3c); + header->initial_ps = get_u16(buf+0x3e); + header->initial_hist1 = get_s16(buf+0x40); + header->initial_hist2 = get_s16(buf+0x42); + header->loop_ps = get_u16(buf+0x44); + header->loop_hist1 = get_s16(buf+0x46); + header->loop_hist2 = get_s16(buf+0x48); + header->channel_count = get_s16(buf+0x4a); + header->block_size = get_s16(buf+0x4c); return 0; } static int read_dsp_header(struct dsp_header *header, off_t offset, STREAMFILE* file) { @@ -91,6 +92,7 @@ typedef struct { int fix_loop_start; /* weird files with bad loop start */ int single_header; /* all channels share header, thus totals are off */ int ignore_header_agreement; /* sometimes there are minor differences between headers */ + int ignore_loop_ps; /* sometimes has bad loop start ps */ } dsp_meta; #define COMMON_DSP_MAX_CHANNELS 6 @@ -113,8 +115,10 @@ static VGMSTREAM* init_vgmstream_dsp_common(STREAMFILE* sf, dsp_meta *dspm) { /* load standard DSP header per channel */ { for (i = 0; i < dspm->channel_count; i++) { - if (read_dsp_header_endian(&ch_header[i], dspm->header_offset + i*dspm->header_spacing, sf, !dspm->little_endian)) + if (read_dsp_header_endian(&ch_header[i], dspm->header_offset + i*dspm->header_spacing, sf, !dspm->little_endian)) { + //;VGM_LOG("DSP: bad header\n"); goto fail; + } } } @@ -129,8 +133,10 @@ static VGMSTREAM* init_vgmstream_dsp_common(STREAMFILE* sf, dsp_meta *dspm) { /* check type==0 and gain==0 */ { for (i = 0; i < dspm->channel_count; i++) { - if (ch_header[i].format || ch_header[i].gain) + if (ch_header[i].format || ch_header[i].gain) { + //;VGM_LOG("DSP: bad type/gain\n"); goto fail; + } } } @@ -156,26 +162,31 @@ static VGMSTREAM* init_vgmstream_dsp_common(STREAMFILE* sf, dsp_meta *dspm) { for (i = 0; i < channels; i++) { off_t channel_offset = dspm->start_offset + i*dspm->interleave; - if (ch_header[i].initial_ps != (uint8_t)read_8bit(channel_offset, sf)) + if (ch_header[i].initial_ps != read_u8(channel_offset, sf)) { + //;VGM_LOG("DSP: bad initial ps\n"); goto fail; + } } } /* check expected loop predictor/scale */ - if (ch_header[0].loop_flag) { + if (ch_header[0].loop_flag && !dspm->ignore_loop_ps) { int channels = dspm->channel_count; if (dspm->single_header) channels = 1; for (i = 0; i < channels; i++) { off_t loop_offset = ch_header[i].loop_start_offset; + + loop_offset = loop_offset / 0x8 * 0x8; /* loop points to a nibble, but we need closest frame header */ if (dspm->interleave) { - loop_offset = loop_offset / 16 * 8; loop_offset = (loop_offset / dspm->interleave * dspm->interleave * channels) + (loop_offset % dspm->interleave); } - if (ch_header[i].loop_ps != (uint8_t)read_8bit(dspm->start_offset + i*dspm->interleave + loop_offset,sf)) + if (ch_header[i].loop_ps != read_u8(dspm->start_offset + i*dspm->interleave + loop_offset,sf)) { + //;VGM_LOG("DSP: bad loop ps: %x vs at %lx\n", ch_header[i].loop_ps, dspm->start_offset + i*dspm->interleave + loop_offset); goto fail; + } } } @@ -199,7 +210,7 @@ static VGMSTREAM* init_vgmstream_dsp_common(STREAMFILE* sf, dsp_meta *dspm) { vgmstream->sample_rate = ch_header[0].sample_rate; vgmstream->num_samples = ch_header[0].sample_count; vgmstream->loop_start_sample = dsp_nibbles_to_samples(ch_header[0].loop_start_offset); - vgmstream->loop_end_sample = dsp_nibbles_to_samples(ch_header[0].loop_end_offset)+1; + vgmstream->loop_end_sample = dsp_nibbles_to_samples(ch_header[0].loop_end_offset) + 1; vgmstream->meta_type = dspm->meta_type; vgmstream->coding_type = coding_NGC_DSP; @@ -224,7 +235,7 @@ static VGMSTREAM* init_vgmstream_dsp_common(STREAMFILE* sf, dsp_meta *dspm) { } } - /* don't know why, but it does happen*/ + /* don't know why, but it does happen */ if (dspm->fix_looping && vgmstream->loop_end_sample > vgmstream->num_samples) vgmstream->loop_end_sample = vgmstream->num_samples; @@ -235,7 +246,7 @@ static VGMSTREAM* init_vgmstream_dsp_common(STREAMFILE* sf, dsp_meta *dspm) { } - if (!vgmstream_open_stream(vgmstream,sf,dspm->start_offset)) + if (!vgmstream_open_stream(vgmstream, sf, dspm->start_offset)) goto fail; return vgmstream; @@ -867,25 +878,28 @@ fail: } /* NPD - Icon Games header + subinterleaved DSPs [Vertigo (Wii), Build n' Race (Wii)] */ -VGMSTREAM* init_vgmstream_wii_ndp(STREAMFILE* sf) { +VGMSTREAM* init_vgmstream_dsp_ndp(STREAMFILE* sf) { dsp_meta dspm = {0}; /* checks */ - if (!check_extensions(sf, "ndp")) + /* .nds: standard + * .ndp: header id */ + if (!check_extensions(sf, "nds,ndp")) goto fail; - if (read_32bitBE(0x00,sf) != 0x4E445000) /* "NDP\0" */ + if (!is_id32be(0x00,sf, "NDP\0")) goto fail; - if (read_32bitLE(0x08,sf) + 0x18 != get_streamfile_size(sf)) + if (read_u32le(0x08,sf) + 0x18 != get_streamfile_size(sf)) goto fail; /* 0x0c: sample rate */ - dspm.channel_count = read_32bitLE(0x10,sf); + dspm.channel_count = read_u32le(0x10,sf); dspm.max_channels = 2; dspm.header_offset = 0x18; dspm.header_spacing = 0x60; dspm.start_offset = dspm.header_offset + dspm.channel_count*dspm.header_spacing; dspm.interleave = 0x04; + dspm.ignore_loop_ps = 1; /* some files loops from 0 but loop ps is null */ dspm.meta_type = meta_WII_NDP; return init_vgmstream_dsp_common(sf, &dspm); @@ -1094,8 +1108,6 @@ VGMSTREAM* init_vgmstream_dsp_sps_n1(STREAMFILE* sf) { dspm.start_offset = dspm.header_offset + dspm.header_spacing*dspm.channel_count; dspm.interleave = 0; - dspm.fix_loop_start = 1; - dspm.meta_type = meta_DSP_VAG; return init_vgmstream_dsp_common(sf, &dspm); fail: @@ -1306,3 +1318,60 @@ VGMSTREAM* init_vgmstream_dsp_wiivoice(STREAMFILE* sf) { fail: return NULL; } + + +/* WIIADPCM - Exient wrapper [Need for Speed: Hot Pursuit (Wii)] */ +VGMSTREAM* init_vgmstream_dsp_wiiadpcm(STREAMFILE* sf) { + dsp_meta dspm = {0}; + + /* checks */ + if (!check_extensions(sf, "adpcm")) + goto fail; + if (!is_id32be(0x00,sf, "WIIA") && !is_id32be(0x00,sf, "DPCM")) + goto fail; + + dspm.interleave = read_u32be(0x08,sf); /* interleave offset */ + if (dspm.interleave) { + dspm.interleave -= 0x10; + } + /* 0x0c: 0 when RAM (2 DSP headers), interleave size when stream (2 WIIADPCM headers) */ + + dspm.channel_count = (dspm.interleave ? 2 : 1); + dspm.max_channels = 2; + + dspm.header_offset = 0x10; + dspm.header_spacing = dspm.interleave; + dspm.start_offset = dspm.header_offset + 0x60; + + dspm.meta_type = meta_DSP_WIIADPCM; + return init_vgmstream_dsp_common(sf, &dspm); +fail: + return NULL; +} + + +/* CWAC - CRI wrapper [Mario & Sonic at the Rio 2016 Olympic Games (WiiU)] */ +VGMSTREAM* init_vgmstream_dsp_cwac(STREAMFILE* sf) { + dsp_meta dspm = {0}; + + /* checks */ + /* .dsp: assumed */ + if (!check_extensions(sf, "dsp")) + goto fail; + if (!is_id32be(0x00,sf, "CWAC")) + goto fail; + + dspm.channel_count = read_u16be(0x04,sf); + dspm.header_offset = read_u32be(0x08,sf); + dspm.interleave = read_u32be(0x0c,sf) - dspm.header_offset; + + dspm.max_channels = 2; + dspm.header_spacing = dspm.interleave; + dspm.start_offset = dspm.header_offset + 0x60; + dspm.ignore_loop_ps = 1; /* loop offset seems relative to CWAC? also interleave affects it */ + + dspm.meta_type = meta_DSP_CWAC; + return init_vgmstream_dsp_common(sf, &dspm); +fail: + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/nxa.c b/Frameworks/vgmstream/vgmstream/src/meta/nxa.c index fc05d6818..d1f1ba201 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/nxa.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/nxa.c @@ -1,58 +1,79 @@ -#include "meta.h" -#include "../coding/coding.h" - - -/* Entergram NXA Opus [Higurashi no Naku Koro ni Hou (Switch)] */ -VGMSTREAM * init_vgmstream_opus_nxa(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - off_t start_offset; - int loop_flag = 0, channel_count; - size_t data_size, skip = 0; - - /* checks */ - if (!check_extensions(streamFile, "nxa")) - goto fail; - if (read_32bitBE(0x00, streamFile) != 0x4E584131) /* "NXA1" */ - goto fail; - - channel_count = read_16bitLE(0x10, streamFile); - skip = read_16bitLE(0x16, streamFile); - data_size = read_32bitLE(0x08, streamFile)-0x30; - start_offset = 0x30; - - /* TODO: Determine if loop points are stored externally. No visible loop points in header */ - loop_flag = 0; - - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count, loop_flag); - if (!vgmstream) goto fail; - - vgmstream->meta_type = meta_NXA; - vgmstream->num_samples = read_32bitLE(0x20, streamFile); - vgmstream->sample_rate = read_32bitLE(0x0C, streamFile); - -#ifdef VGM_USE_FFMPEG - { - vgmstream->codec_data = init_ffmpeg_switch_opus(streamFile, start_offset, data_size, vgmstream->channels, skip, vgmstream->sample_rate); - if (!vgmstream->codec_data) goto fail; - vgmstream->coding_type = coding_FFmpeg; - vgmstream->layout_type = layout_none; - - if (vgmstream->num_samples == 0) { - vgmstream->num_samples = switch_opus_get_samples(start_offset, data_size, streamFile) - skip; - } - } -#else - goto fail; -#endif - - /* open the file for reading */ - 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" + + +/* Entergram NXA Opus [Higurashi no Naku Koro ni Hou (Switch), Gensou Rougoku no Kaleidoscope (Switch)] */ +VGMSTREAM* init_vgmstream_opus_nxa(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + off_t start_offset; + int loop_flag, channels, type, sample_rate; + int32_t num_samples, loop_start, loop_end, skip; + size_t data_size, frame_size; + + /* checks */ + if (!check_extensions(sf, "nxa")) + goto fail; + if (!is_id32be(0x00, sf, "NXA1")) + goto fail; + + start_offset = 0x30; + type = read_u32le(0x04, sf); + data_size = read_u32le(0x08, sf) - start_offset; + sample_rate = read_u32le(0x0C, sf); + channels = read_s16le(0x10, sf); + frame_size = read_u16le(0x12, sf); + /* 0x14: frame samples */ + skip = read_s16le(0x16, sf); + num_samples = read_s32le(0x18, sf); + loop_start = read_s32le(0x1c, sf); + loop_end = read_s32le(0x20, sf); + /* rest: null */ + + loop_flag = (loop_start > 0); + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channels, loop_flag); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_NXA; + vgmstream->sample_rate = sample_rate; + vgmstream->num_samples = num_samples; + vgmstream->loop_start_sample = loop_start; + vgmstream->loop_end_sample = loop_end; + + switch(type) { +#ifdef VGM_USE_FFMPEG + case 1: { /* Higurashi no Naku Koro ni Hou (Switch) */ + vgmstream->codec_data = init_ffmpeg_switch_opus(sf, start_offset, data_size, vgmstream->channels, skip, vgmstream->sample_rate); + if (!vgmstream->codec_data) goto fail; + vgmstream->coding_type = coding_FFmpeg; + vgmstream->layout_type = layout_none; + break; + } + + case 2: { /* Gensou Rougoku no Kaleidoscope (Switch) */ + opus_config cfg = {0}; + + cfg.channels = channels; + cfg.skip = skip; + cfg.frame_size = frame_size; + + vgmstream->codec_data = init_ffmpeg_fixed_opus(sf, start_offset, data_size, &cfg); + if (!vgmstream->codec_data) goto fail; + vgmstream->coding_type = coding_FFmpeg; + vgmstream->layout_type = layout_none; + break; + } +#endif + default: + goto fail; + } + + 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/sbk.c b/Frameworks/vgmstream/vgmstream/src/meta/sbk.c new file mode 100644 index 000000000..995313888 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/sbk.c @@ -0,0 +1,128 @@ +#include "meta.h" +#include "../coding/coding.h" + +/* .SBK - from Addiction Pinball (PC) */ +VGMSTREAM *init_vgmstream_sbk(STREAMFILE *sf) { + VGMSTREAM *vgmstream = NULL; + uint32_t sound_offset, sound_size, padding_size, sample_rate; + uint16_t format, channels, block_size, bps; + off_t table_offset, data_offset, entry_offset, cfg_fmt_offset; + size_t table_size, data_size, cfg_entry_size; + int target_subsong = sf->stream_index, total_subsongs, loop_flag, is_streamed; + + /* checks */ + if (!check_extensions(sf, "sbk")) + goto fail; + + /* check header */ + if (read_u32be(0x00, sf) != 0x52494646) /* "RIFF" */ + goto fail; + if (read_u32be(0x08, sf) != 0x53424E4B) /* "SBNK" */ + goto fail; + + if (!find_chunk_le(sf, 0x57415649, 0x0c, 0, &table_offset, &table_size)) /* "WAVI" */ + goto fail; + + if (find_chunk_le(sf, 0x53574156, 0x0c, 0, &data_offset, &data_size)) { /* "SWAV" */ + cfg_entry_size = 0x38; + cfg_fmt_offset = 0x1c; + } else { + /* 1997 demo version with sound names and no streamed section */ + cfg_entry_size = 0x24; + cfg_fmt_offset = 0x0c; + } + + total_subsongs = table_size / cfg_entry_size; + + if (target_subsong == 0) target_subsong = 1; + if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) + goto fail; + + entry_offset = table_offset + cfg_entry_size * (target_subsong - 1); + sound_offset = read_u32le(entry_offset + 0x04, sf); + sound_size = read_u32le(entry_offset + 0x00, sf); + if (cfg_entry_size == 0x38) { + padding_size = read_u32le(entry_offset + 0x10, sf); + sound_offset += padding_size; + sound_size -= padding_size; + + is_streamed = read_u8(entry_offset + 0x36, sf); + } else { + is_streamed = 0; + } + + if (!is_streamed) { + if (!find_chunk_le(sf, 0x57415644, 0x0c, 0, &data_offset, &data_size)) /* "WAVD" */ + goto fail; + } else { + if (!find_chunk_le(sf, 0x53574156, 0x0c, 0, &data_offset, &data_size)) /* "SWAV" */ + goto fail; + } + + sound_offset += data_offset; + + /* read fmt chunk */ + format = read_u16le(entry_offset + cfg_fmt_offset + 0x00, sf); + channels = read_u16le(entry_offset + cfg_fmt_offset + 0x02, sf); + sample_rate = read_u32le(entry_offset + cfg_fmt_offset + 0x04, sf); + block_size = read_u16le(entry_offset + cfg_fmt_offset + 0x0c, sf); + bps = read_u16le(entry_offset + cfg_fmt_offset + 0x0e, sf); + + loop_flag = 0; + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channels, loop_flag); + if (!vgmstream) goto fail; + + /* fill in the vital statistics */ + vgmstream->meta_type = meta_SBK; + vgmstream->sample_rate = sample_rate; + vgmstream->stream_size = sound_size; + vgmstream->num_streams = total_subsongs; + + if (cfg_entry_size == 0x24) { + uint32_t num_entries, i; + + if (!find_chunk_le(sf, 0x544F4320, 0x0c, 0, &table_offset, &table_size)) /* "TOC " */ + goto fail; + + num_entries = table_size / 0x10; + for (i = 0; i < num_entries; i++) { + entry_offset = table_offset + 0x10 * i; + + if ((read_u8(entry_offset + 0x01, sf) & 0x80) && + read_u8(entry_offset + 0x00, sf) == (target_subsong - 1)) { + read_string(vgmstream->stream_name, 0x0c, entry_offset + 0x04, sf); + break; + } + } + } + + switch (format) { + case 0x01: /* PCM */ + if (bps != 8 && bps != 16) + goto fail; + + vgmstream->coding_type = (bps == 8) ? coding_PCM8_U : coding_PCM16LE; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = (bps == 8) ? 0x01 : 0x02; + vgmstream->num_samples = pcm_bytes_to_samples(sound_size, channels, bps); + break; + case 0x11: /* Microsoft IMA */ + vgmstream->coding_type = coding_MS_IMA; + vgmstream->layout_type = layout_none; + vgmstream->interleave_block_size = block_size; + vgmstream->num_samples = ms_ima_bytes_to_samples(sound_size, block_size, channels); + break; + default: + goto fail; + } + + if (!vgmstream_open_stream(vgmstream, sf, sound_offset)) + goto fail; + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/smp.c b/Frameworks/vgmstream/vgmstream/src/meta/smp.c index c7025e4ae..6dbf31c1f 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/smp.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/smp.c @@ -1,127 +1,137 @@ -#include "meta.h" -#include "../coding/coding.h" - - -/* .smp - Terminal Reality's Infernal Engine 'samples' [Ghostbusters: The Video Game (PS2/PS3/X360/PC/PSP), Chandragupta (PS2/PSP)] */ -VGMSTREAM * init_vgmstream_smp(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - off_t start_offset; - int loop_flag, channel_count, sample_rate, codec, version, num_samples, bps; - size_t data_size; - - - /* checks */ - if (!check_extensions(streamFile, "smp")) - goto fail; - - version = read_32bitLE(0x00,streamFile); - if (version != 0x05 && /* Ghostbusters (PS2), Mushroom Men (Wii) */ - version != 0x06 && /* Ghostbusters (PS3/X360/PC) */ - version != 0x07 && /* Ghostbusters (PSP) */ - version != 0x08) /* Chandragupta (PS2/PSP), Street Cricket Champions 1/2 (PSP) */ - goto fail; - - /* 0x04~14: guid? */ - if (read_32bitLE(0x14,streamFile) != 0) /* reserved? */ - goto fail; - num_samples = read_32bitLE(0x18,streamFile); - start_offset = read_32bitLE(0x1c,streamFile); - data_size = read_32bitLE(0x20,streamFile); - codec = read_32bitLE(0x24,streamFile); - channel_count = read_32bitLE(0x28,streamFile); - bps = read_32bitLE(0x2c,streamFile); - sample_rate = read_32bitLE(0x30,streamFile); - - loop_flag = 0; - if (start_offset + data_size != get_streamfile_size(streamFile)) - goto fail; - - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count, loop_flag); - if (!vgmstream) goto fail; - - vgmstream->meta_type = meta_SMP; - vgmstream->sample_rate = sample_rate; - vgmstream->num_samples = num_samples; - - switch(codec) { -#ifdef VGM_USE_FFMPEG - case 0x01: { - int block_align, encoder_delay; - if (bps != 16) goto fail; - - block_align = 0x98 * vgmstream->channels; - encoder_delay = 0; /* 1024 looks ok, but num_samples needs to be adjusted too */ - - vgmstream->codec_data = init_ffmpeg_atrac3_raw(streamFile, start_offset,data_size, vgmstream->num_samples,vgmstream->channels,vgmstream->sample_rate, block_align, encoder_delay); - if (!vgmstream->codec_data) goto fail; - vgmstream->coding_type = coding_FFmpeg; - vgmstream->layout_type = layout_none; - break; - } -#endif - - case 0x02: - if (bps != 4) goto fail; - if (channel_count > 1) goto fail; /* not known */ - /* 0x34: standard DSP header, but LE */ - - vgmstream->coding_type = coding_NGC_DSP; - vgmstream->layout_type = layout_none; - dsp_read_coefs_le(vgmstream,streamFile,0x50,0x00); - break; - - case 0x04: - if (bps != 4) goto fail; - if (!msadpcm_check_coefs(streamFile, 0x36)) - goto fail; - - vgmstream->coding_type = coding_MSADPCM; - vgmstream->layout_type = layout_none; - vgmstream->frame_size = 0x86*channel_count; - break; - - case 0x06: - if (bps != 4) goto fail; - if (channel_count > 1) goto fail; /* not known */ - - vgmstream->coding_type = coding_PSX; - vgmstream->layout_type = layout_none; - break; - -#ifdef VGM_USE_FFMPEG - case 0x07: { - uint8_t buf[0x100]; - int bytes, block_size, block_count; - - if (bps != 16) goto fail; - /* 0x34(0x28): XMA config/table? */ - - block_size = 0x8000; /* assumed, @0x3e(2)? */ - block_count = data_size / block_size + (data_size % block_size ? 1 : 0); /* @0x54(2)? */ - - bytes = ffmpeg_make_riff_xma2(buf,0x100, vgmstream->num_samples, data_size, vgmstream->channels, vgmstream->sample_rate, block_count, block_size); - vgmstream->codec_data = init_ffmpeg_header_offset(streamFile, buf,bytes, start_offset,data_size); - if (!vgmstream->codec_data) goto fail; - vgmstream->coding_type = coding_FFmpeg; - vgmstream->layout_type = layout_none; - - //xma_fix_raw_samples(vgmstream, streamFile, start_offset,data_size, 0, ); //todo - break; - } -#endif - - default: - goto fail; - } - - - 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" + + +/* .smp - Terminal Reality's Infernal Engine 'samples' [Ghostbusters: The Video Game (PS2/PS3/X360/PC/PSP), Chandragupta (PS2/PSP)] */ +VGMSTREAM* init_vgmstream_smp(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + off_t start_offset, extra_offset; + int loop_flag, channels, sample_rate, codec, version, num_samples, bps; + size_t data_size; + + + /* checks */ + if (!check_extensions(sf, "smp")) + goto fail; + + version = read_u32le(0x00,sf); + if (version != 0x05 && /* Ghostbusters (PS2), Mushroom Men (Wii) */ + version != 0x06 && /* Ghostbusters (PS3/X360/PC) */ + version != 0x07 && /* Ghostbusters (PSP) */ + version != 0x08) /* Chandragupta (PS2/PSP), Street Cricket Champions 1/2 (PSP), Guilty Party (Wii) */ + goto fail; + + /* 0x04~14: guid? */ + if (read_u32le(0x14,sf) != 0) /* reserved? */ + goto fail; + num_samples = read_s32le(0x18,sf); + start_offset = read_u32le(0x1c,sf); + data_size = read_u32le(0x20,sf); + codec = read_u32le(0x24,sf); + /* smaller header found in Guilty Party (Wii) */ + if (version == 0x08 && start_offset == 0x80) { + channels = read_u8(0x28,sf); + bps = read_u8(0x29,sf); + sample_rate = read_u16le(0x2a,sf); + extra_offset = 0x2c; /* coefs only */ + } + else { + channels = read_u32le(0x28,sf); + bps = read_u32le(0x2c,sf); + sample_rate = read_u32le(0x30,sf); + extra_offset = 0x34 + 0x1c; /* standard DSP header, but LE */ + } + + loop_flag = 0; + if (start_offset + data_size != get_streamfile_size(sf)) + goto fail; + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channels, loop_flag); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_SMP; + vgmstream->sample_rate = sample_rate; + vgmstream->num_samples = num_samples; + + switch(codec) { +#ifdef VGM_USE_FFMPEG + case 0x01: { + int block_align, encoder_delay; + if (bps != 16) goto fail; + + block_align = 0x98 * vgmstream->channels; + encoder_delay = 0; /* 1024 looks ok, but num_samples needs to be adjusted too */ + + vgmstream->codec_data = init_ffmpeg_atrac3_raw(sf, start_offset,data_size, vgmstream->num_samples,vgmstream->channels,vgmstream->sample_rate, block_align, encoder_delay); + if (!vgmstream->codec_data) goto fail; + vgmstream->coding_type = coding_FFmpeg; + vgmstream->layout_type = layout_none; + break; + } +#endif + + case 0x02: + if (bps != 4) goto fail; + if (channels > 1) goto fail; /* not known */ + + vgmstream->coding_type = coding_NGC_DSP; + vgmstream->layout_type = layout_none; + dsp_read_coefs_le(vgmstream, sf, extra_offset, 0x00); + //todo adpcm hist + break; + + case 0x04: + if (bps != 4) goto fail; + if (!msadpcm_check_coefs(sf, 0x36)) + goto fail; + + vgmstream->coding_type = coding_MSADPCM; + vgmstream->layout_type = layout_none; + vgmstream->frame_size = 0x86*channels; + break; + + case 0x06: + if (bps != 4) goto fail; + if (channels > 1) goto fail; /* not known */ + + vgmstream->coding_type = coding_PSX; + vgmstream->layout_type = layout_none; + break; + +#ifdef VGM_USE_FFMPEG + case 0x07: { + uint8_t buf[0x100]; + int bytes, block_size, block_count; + + if (bps != 16) goto fail; + /* 0x34(0x28): XMA config/table? */ + + block_size = 0x8000; /* assumed, @0x3e(2)? */ + block_count = data_size / block_size + (data_size % block_size ? 1 : 0); /* @0x54(2)? */ + + bytes = ffmpeg_make_riff_xma2(buf,0x100, vgmstream->num_samples, data_size, vgmstream->channels, vgmstream->sample_rate, block_count, block_size); + vgmstream->codec_data = init_ffmpeg_header_offset(sf, buf,bytes, start_offset,data_size); + if (!vgmstream->codec_data) goto fail; + vgmstream->coding_type = coding_FFmpeg; + vgmstream->layout_type = layout_none; + + //xma_fix_raw_samples(vgmstream, sf, start_offset,data_size, 0, ); //todo + break; + } +#endif + + default: + goto fail; + } + + + 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/txth.c b/Frameworks/vgmstream/vgmstream/src/meta/txth.c index 230b1ad1c..13b38930c 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/txth.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/txth.c @@ -610,10 +610,22 @@ static VGMSTREAM* init_subfile(txth_header* txth) { sf_sub = setup_subfile_streamfile(txth->sf_body, txth->subfile_offset, txth->subfile_size, extension); if (!sf_sub) goto fail; - sf_sub->stream_index = txth->sf->stream_index; /* in case of subfiles with subsongs */ + sf_sub->stream_index = txth->sf->stream_index; vgmstream = init_vgmstream_from_STREAMFILE(sf_sub); - if (!vgmstream) goto fail; + if (!vgmstream) { + /* In case of subfiles with subsongs pass subsong N by default (ex. subfile is a .fsb with N subsongs). + * But if the subfile is a single-subsong subfile (ex. subfile is a .fsb with 1 subsong) try again + * without passing index (as it would fail first trying to open subsong N). */ + if (sf_sub->stream_index > 1) { + sf_sub->stream_index = 0; + vgmstream = init_vgmstream_from_STREAMFILE(sf_sub); + if (!vgmstream) goto fail; + } + else { + goto fail; + } + } /* apply some fields */ if (txth->sample_rate) diff --git a/Frameworks/vgmstream/vgmstream/src/meta/txtp.c b/Frameworks/vgmstream/vgmstream/src/meta/txtp.c index 6912c5d7e..ea75dfdfe 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/txtp.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/txtp.c @@ -1888,8 +1888,13 @@ static txtp_header* parse_txtp(STREAMFILE* sf) { /* skip BOM if needed */ if (file_size > 0 && - ((uint16_t)read_16bitLE(0x00, sf) == 0xFFFE || (uint16_t)read_16bitLE(0x00, sf) == 0xFEFF)) + (read_u16le(0x00, sf) == 0xFFFE || read_u16le(0x00, sf) == 0xFEFF)) { txt_offset = 0x02; + } + else if ((read_u32be(0x00, sf) & 0xFFFFFF00) == 0xEFBBBF00) { + txt_offset = 0x03; + } + /* read and parse lines */ while (txt_offset < file_size) { diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ubi_sb.c b/Frameworks/vgmstream/vgmstream/src/meta/ubi_sb.c index 5bc31a098..aef7d05b7 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ubi_sb.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ubi_sb.c @@ -7,6 +7,9 @@ #define SB_MAX_LAYER_COUNT 16 /* arbitrary max */ #define SB_MAX_CHAIN_COUNT 256 /* +150 exist in Tonic Trouble */ +#define LAYER_HIJACK_GRAW_X360 1 +#define LAYER_HIJACK_SCPT_PS2 2 + typedef enum { UBI_IMA, UBI_ADPCM, RAW_PCM, RAW_PSX, RAW_DSP, RAW_XBOX, FMT_VAG, FMT_AT3, RAW_AT3, FMT_XMA1, RAW_XMA1, FMT_OGG, FMT_CWAV, FMT_APM, FMT_MPDX, UBI_IMA_SCE } ubi_sb_codec; typedef enum { UBI_PC, UBI_DC, UBI_PS2, UBI_XBOX, UBI_GC, UBI_X360, UBI_PSP, UBI_PS3, UBI_WII, UBI_3DS } ubi_sb_platform; typedef enum { UBI_NONE = 0, UBI_AUDIO, UBI_LAYER, UBI_SEQUENCE, UBI_SILENCE } ubi_sb_type; @@ -14,6 +17,7 @@ typedef enum { UBI_NONE = 0, UBI_AUDIO, UBI_LAYER, UBI_SEQUENCE, UBI_SILENCE } u typedef struct { int map_version; size_t map_entry_size; + off_t map_name; size_t section1_entry_size; size_t section2_entry_size; size_t section3_entry_size; @@ -30,6 +34,7 @@ typedef struct { off_t audio_loop_flag; off_t audio_loc_flag; off_t audio_stereo_flag; + off_t audio_ram_streamed_flag; off_t audio_internal_flag; off_t audio_num_samples; off_t audio_num_samples2; @@ -45,9 +50,11 @@ typedef struct { int audio_subblock_and; int audio_loc_and; int audio_stereo_and; + int audio_ram_streamed_and; int audio_has_internal_names; size_t audio_interleave; int audio_fix_psx_samples; + int has_rs_files; off_t sequence_extra_offset; off_t sequence_sequence_loop; @@ -86,7 +93,6 @@ typedef struct { int is_padded_sectionX_offset; int is_padded_sounds_offset; int ignore_layer_error; - int default_codec_for_subblock0; } ubi_sb_config; typedef struct { @@ -161,8 +167,6 @@ typedef struct { uint32_t stream_type; /* rough codec value */ uint32_t subblock_id; /* internal id to reference in section3 */ uint8_t subbank_index; /* ID of the entry in DC bank */ - int is_localized; - int is_stereo; int loop_flag; /* stream loops (normally internal sfx, but also external music) */ int loop_start; /* usually 0 */ @@ -184,7 +188,9 @@ typedef struct { int is_streamed; /* sound is streamed from storage */ int is_cd_streamed; /* found in PS2 BNM */ + int is_ram_streamed; /* found in some PS2 games */ int is_external; /* sound is in an external file */ + int is_localized; /* found in old PS2 games, determines which file the sound is in */ char resource_name[0x28]; /* filename to the external stream, or internal stream info for some games */ char readable_name[255]; /* final subsong name */ @@ -345,7 +351,7 @@ VGMSTREAM* init_vgmstream_ubi_sm(STREAMFILE* sf) { sb.map_zero = read_32bit(offset + 0x04, sf); sb.map_offset = read_32bit(offset + 0x08, sf); sb.map_size = read_32bit(offset + 0x0c, sf); /* includes sbX header, but not internal streams */ - read_string(sb.map_name, sizeof(sb.map_name), offset + 0x10, sf); /* null-terminated and may contain garbage after null */ + read_string(sb.map_name, sizeof(sb.map_name), offset + sb.cfg.map_name, sf); /* null-terminated and may contain garbage after null */ if (sb.cfg.map_version >= 3) sb.map_unknown = read_32bit(offset + 0x30, sf); /* uncommon, id/config? longer name? mem garbage? */ @@ -984,7 +990,7 @@ static VGMSTREAM* init_vgmstream_ubi_sb_base(ubi_sb_header* sb, STREAMFILE* sf_h break; case UBI_IMA_SCE: - vgmstream->coding_type = coding_UBI_IMA; + vgmstream->coding_type = coding_UBI_SCE_IMA; vgmstream->layout_type = layout_blocked_ubi_sce; vgmstream->full_block_size = read_32bitLE(0x18, sf_data); @@ -995,10 +1001,12 @@ static VGMSTREAM* init_vgmstream_ubi_sb_base(ubi_sb_header* sb, STREAMFILE* sf_h case UBI_ADPCM: /* custom Ubi 4/6-bit ADPCM used in early games: - * - Splinter Cell (PC): 4-bit w/ 2ch (music), 6-bit w/ 1ch (sfx) - * - Batman: Vengeance (PC): 4-bit w/ 2ch (music), 6-bit w/ 1ch (sfx) - * - Myst IV (PC/Xbox): 4bit-1ch (amb), some files only (ex. sfx_si_puzzle_stream.sb2) - * - possibly others */ + * - Splinter Cell (PC): 4-bit w/ 1ch/2ch (all streams + menu music) + * - Batman: Vengeance (PC): 4-bit w/ 1ch/2ch (all streams) + * - Myst IV (PC/Xbox): 4-bit w/ 1ch (amb), some files only (ex. sfx_si_puzzle_stream.sb2) + * - The Jungle Book: Rhythm N'Groove (PC): 4-bit w/ 2ch (music/amb), 6-bit w/ 1ch (speech) + * - possibly others + * internal extension is .adp, maybe this can be called FMT_ADP */ /* skip extra header (some kind of id?) found in Myst IV */ if (read_32bitBE(start_offset + 0x00, sf_data) != 0x08000000 && @@ -1027,6 +1035,13 @@ static VGMSTREAM* init_vgmstream_ubi_sb_base(ubi_sb_header* sb, STREAMFILE* sf_h case RAW_PSX: vgmstream->coding_type = coding_PSX; vgmstream->layout_type = layout_interleave; + + if (sb->cfg.has_rs_files) { + /* SC:PT PS2 has extra 0x30 bytes, presumably from (missing) VAG header */ + sb->stream_size -= 0x30; + vgmstream->stream_size -= 0x30; + } + if (sb->is_ps2_bnm) { vgmstream->interleave_block_size = (sb->is_cd_streamed) ? sb->cfg.audio_interleave : @@ -1039,7 +1054,12 @@ static VGMSTREAM* init_vgmstream_ubi_sb_base(ubi_sb_header* sb, STREAMFILE* sf_h if (vgmstream->num_samples == 0) { /* early PS2 games may not set it for internal streams */ vgmstream->num_samples = ps_bytes_to_samples(sb->stream_size, sb->channels); - vgmstream->loop_end_sample = vgmstream->num_samples; + + if (sb->loop_start == 0) { + ps_find_loop_offsets(sf_data, sb->stream_offset, sb->stream_size, + sb->channels, vgmstream->interleave_block_size, + &vgmstream->loop_start_sample, &vgmstream->loop_end_sample); + } } /* late PS3 SBs have double sample count here for who knows why @@ -1108,7 +1128,7 @@ static VGMSTREAM* init_vgmstream_ubi_sb_base(ubi_sb_header* sb, STREAMFILE* sf_h break; } - //TODO: Ubi XMA1 (raw or fmt) is a bit strange, FFmpeg decodes some frames slightly wrong + // TODO: Ubi XMA1 (raw or fmt) is a bit strange, FFmpeg decodes some frames slightly wrong // XMA1 normally has a frame counter in the first nibble but Ubi's is always set to 0. // Probably a beta/custom encoder that creates some buggy frames, that a real X360 handles ok, but trips FFmpeg // xmaencode decodes correctly if counters are fixed (otherwise has clicks on every frame). @@ -1751,6 +1771,7 @@ static int parse_type_audio_ps2_old(ubi_sb_header* sb, off_t offset, STREAMFILE* int32_t(*read_32bit)(off_t, STREAMFILE*) = sb->big_endian ? read_32bitBE : read_32bitLE; uint32_t pitch; uint32_t test_sample_rate; + int is_stereo; sb->stream_size = read_32bit(offset + sb->cfg.audio_stream_size, sf); sb->stream_offset = read_32bit(offset + sb->cfg.audio_stream_offset, sf); @@ -1768,10 +1789,10 @@ static int parse_type_audio_ps2_old(ubi_sb_header* sb, off_t offset, STREAMFILE* sb->is_streamed = read_32bit(offset + sb->cfg.audio_streamed_flag, sf) & sb->cfg.audio_streamed_and; sb->loop_flag = read_32bit(offset + sb->cfg.audio_loop_flag, sf) & sb->cfg.audio_loop_and; sb->is_localized = read_32bit(offset + sb->cfg.audio_loc_flag, sf) & sb->cfg.audio_loc_and; - sb->is_stereo = read_32bit(offset + sb->cfg.audio_stereo_flag, sf) & sb->cfg.audio_stereo_and; + is_stereo = read_32bit(offset + sb->cfg.audio_stereo_flag, sf) & sb->cfg.audio_stereo_and; sb->num_samples = 0; /* calculate from size */ - sb->channels = sb->is_stereo ? 2 : 1; + sb->channels = is_stereo ? 2 : 1; sb->stream_size *= sb->channels; sb->subblock_id = 0; @@ -1791,6 +1812,7 @@ fail: static int parse_type_layer_ps2_old(ubi_sb_header* sb, off_t offset, STREAMFILE* sf) { int32_t(*read_32bit)(off_t, STREAMFILE*) = sb->big_endian ? read_32bitBE : read_32bitLE; + uint32_t pitch; /* much simpler than later iteration */ sb->layer_count = read_32bit(offset + sb->cfg.layer_layer_count, sf); @@ -1807,7 +1829,8 @@ static int parse_type_layer_ps2_old(ubi_sb_header* sb, off_t offset, STREAMFILE* goto fail; } - sb->sample_rate = ubi_ps2_pitch_to_freq(read_32bit(offset + sb->cfg.layer_pitch, sf)); + pitch = read_32bit(offset + sb->cfg.layer_pitch, sf); + sb->sample_rate = ubi_ps2_pitch_to_freq(pitch); sb->is_localized = read_32bit(offset + sb->cfg.layer_loc_flag, sf) & sb->cfg.layer_loc_and; sb->num_samples = 0; /* calculate from size */ @@ -1865,12 +1888,24 @@ static int parse_type_audio(ubi_sb_header* sb, off_t offset, STREAMFILE* sf) { /* apparently, there may also be other subblocks based on various flags but they were not seen so far */ if (sb->cfg.audio_subblock_flag && sb->cfg.audio_subblock_and) { + /* flag probably means "software decoded" */ int subblock_flag = read_32bit(offset + sb->cfg.audio_subblock_flag, sf) & sb->cfg.audio_subblock_and; sb->subblock_id = (!subblock_flag) ? 0 : 1; + + /* stream_type field is not used if the flag is not set (it even contains garbage in some versions) + * except for PS3 which has two hardware codecs (PSX and AT3) */ + if (!subblock_flag && sb->platform != UBI_PS3) + sb->stream_type = 0x00; } else { sb->subblock_id = (sb->stream_type == 0x01) ? 0 : 1; } + if (sb->cfg.has_rs_files && !sb->is_external) { + /* found in Splinter Cell: Pandora Tomorrow (PS2) */ + sb->is_ram_streamed = read_32bit(offset + sb->cfg.audio_ram_streamed_flag, sf) & sb->cfg.audio_ram_streamed_and; + sb->is_external = sb->is_ram_streamed; + } + sb->loop_flag = read_32bit(offset + sb->cfg.audio_loop_flag, sf) & sb->cfg.audio_loop_and; if (sb->loop_flag) { @@ -1896,7 +1931,9 @@ static int parse_type_audio(ubi_sb_header* sb, off_t offset, STREAMFILE* sf) { if (sb->cfg.audio_stream_name) { if (sb->is_dat && !sb->is_external) { sb->subbank_index = read_8bit(offset + sb->cfg.audio_stream_name + 0x01, sf); - } else { + } else if (sb->cfg.has_rs_files && sb->is_ram_streamed) { + strcpy(sb->resource_name, "MAPS.RS1"); + } else if (sb->is_external || sb->cfg.audio_has_internal_names) { read_string(sb->resource_name, sb->cfg.resource_name_size, offset + sb->cfg.audio_stream_name, sf); } } @@ -1955,8 +1992,7 @@ static int parse_type_sequence(ubi_sb_header* sb, off_t offset, STREAMFILE* sf) if (!sb->sequence_multibank) { sb->sequence_multibank = is_other_bank(sb, sf, bank_number); } - } - else { + } else { entry_number = entry_number & 0x3FFFFFFF; if (entry_number > sb->section2_num) { VGM_LOG("UBI SB: chain with wrong entry %i vs %i at %x\n", entry_number, sb->section2_num, (uint32_t)sb->extra_offset); @@ -2032,7 +2068,9 @@ static int parse_type_layer(ubi_sb_header* sb, off_t offset, STREAMFILE* sf) { if (sb->sample_rate != sample_rate || sb->stream_type != stream_type) { VGM_LOG("UBI SB: %i layer headers don't match at %x > %x\n", sb->layer_count, (uint32_t)offset, (uint32_t)table_offset); - if (!sb->cfg.ignore_layer_error) /* layers of different rates happens sometimes */ + /* Layers of different rates happens sometimes. From decompilations, first layer's sample rate + * looks used as main, though lower sample rate layer only seem to appear to after first. */ + if (!sb->cfg.ignore_layer_error) goto fail; } @@ -2122,6 +2160,12 @@ static int parse_type_random(ubi_sb_header* sb, off_t offset, STREAMFILE* sf) { VGM_LOG("UBI SB: random in other bank\n"); goto fail; } + } else { + entry_number = entry_number & 0x3FFFFFFF; + if (entry_number > sb->section2_num) { + VGM_LOG("UBI SB: random with wrong entry %i vs %i at %x\n", entry_number, sb->section2_num, (uint32_t)sb->extra_offset); + goto fail; + } } //todo make rand or stuff (old chance: int from 0 to 0x10000, new: float from 0.0 to 1.0) @@ -2138,7 +2182,7 @@ fail: return 0; } -static int set_default_codec_for_platform(ubi_sb_header *sb) { +static int set_hardware_codec_for_platform(ubi_sb_header *sb) { switch (sb->platform) { case UBI_PC: sb->codec = RAW_PCM; @@ -2162,16 +2206,11 @@ static int set_default_codec_for_platform(ubi_sb_header *sb) { case UBI_X360: sb->codec = RAW_XMA1; break; -#if 0 - case UBI_PS3: /* assumed, but no games seem to use it */ - sb->codec = RAW_AT3; - break; -#endif case UBI_3DS: sb->codec = FMT_CWAV; break; default: - VGM_LOG("UBI SB: unknown internal format\n"); + VGM_LOG("UBI SB: unknown hardware codec\n"); return 0; } @@ -2199,7 +2238,9 @@ static int parse_stream_codec(ubi_sb_header* sb) { if (sb->is_bnm || sb->version < 0x00000007) { /* bnm is ~v0 but some games have wonky versions */ switch (sb->stream_type) { case 0x01: - if (!set_default_codec_for_platform(sb)) + if (sb->is_streamed) + sb->codec = RAW_PCM; + else if (!set_hardware_codec_for_platform(sb)) goto fail; break; @@ -2211,10 +2252,14 @@ static int parse_stream_codec(ubi_sb_header* sb) { sb->codec = FMT_APM; break; - case 0x06: /* The Jungle Book (internal extension is .adp, maybe Ubi ADPCM can be considered FMT_ADP) */ + case 0x06: sb->codec = UBI_ADPCM; break; - +#if 0 + case 0x07: + sb->codec = FMT_PFK; /* not seen yet, some MPEG based codec, referred to as "PFK" in the code */ + break; +#endif case 0x08: sb->codec = UBI_IMA; /* Ubi IMA v2/v3 */ break; @@ -2226,7 +2271,9 @@ static int parse_stream_codec(ubi_sb_header* sb) { } else if (sb->version < 0x000A0000) { switch (sb->stream_type) { case 0x01: - if (!set_default_codec_for_platform(sb)) + if (sb->is_streamed) + sb->codec = RAW_PCM; + else if (!set_hardware_codec_for_platform(sb)) goto fail; break; @@ -2235,7 +2282,7 @@ static int parse_stream_codec(ubi_sb_header* sb) { break; #if 0 case 0x03: - sb->codec = FMT_MPDX; /* not seen yet, some MPEG based codec */ + sb->codec = FMT_PFK; /* not seen yet, some MPEG based codec, referred to as "PFK" in the code */ break; #endif case 0x04: @@ -2251,14 +2298,9 @@ static int parse_stream_codec(ubi_sb_header* sb) { goto fail; } } else { - /* some Xbox games default to codec 0 if subblock flag isn't set while the actual field contains garbage */ - if (sb->cfg.default_codec_for_subblock0 && sb->type == UBI_AUDIO && sb->subblock_id == 0) { - sb->stream_type = 0x00; - } - switch (sb->stream_type) { case 0x00: - if (!set_default_codec_for_platform(sb)) + if (!set_hardware_codec_for_platform(sb)) goto fail; break; @@ -2358,9 +2400,6 @@ static int parse_offsets(ubi_sb_header* sb, STREAMFILE* sf) { VGM_ASSERT(!sb->is_map && sb->section3_num > 2, "UBI SB: section3 > 2 found\n"); - if (sb->is_external) - return 1; - /* Internal sounds are split into subblocks, with their offsets being relative to subblock start. * A table contains sizes of each subblock, so we adjust offsets based on the subblock ID of our sound. * Headers normally only use 0 or 1, and section3 may only define id1 (which the internal sound would use). @@ -2384,6 +2423,9 @@ static int parse_offsets(ubi_sb_header* sb, STREAMFILE* sf) { * 0x0c - absolute subblock offset */ + if (sb->is_external && !sb->is_ram_streamed) + return 1; + for (i = 0; i < sb->section3_num; i++) { off_t offset = sb->section3_offset + 0x14 * i; off_t table_offset = read_32bit(offset + 0x04, sf) + sb->section3_offset; @@ -2396,6 +2438,9 @@ static int parse_offsets(ubi_sb_header* sb, STREAMFILE* sf) { if (index == sb->header_index) { sb->stream_offset = read_32bit(table_offset + 0x08 * j + 0x04, sf); + if (sb->is_ram_streamed) + break; + for (k = 0; k < table2_num; k++) { uint32_t id = read_32bit(table2_offset + 0x10 * k + 0x00, sf); @@ -2423,21 +2468,23 @@ static int parse_offsets(ubi_sb_header* sb, STREAMFILE* sf) { } } else { /* banks store internal sounds after all headers and adjusted by the subblock table, find the matching entry */ + off_t sounds_offset; - off_t sounds_offset = sb->section3_offset + sb->cfg.section3_entry_size*sb->section3_num; + if (sb->is_external) + return 1; + + sounds_offset = sb->section3_offset + sb->cfg.section3_entry_size*sb->section3_num; if (sb->cfg.is_padded_sounds_offset) sounds_offset = align_size_to_block(sounds_offset, 0x10); sb->stream_offset = sounds_offset + sb->stream_offset; - if (sb->section3_num > 1) { /* maybe should always test this? */ - for (i = 0; i < sb->section3_num; i++) { - off_t offset = sb->section3_offset + sb->cfg.section3_entry_size * i; + for (i = 0; i < sb->section3_num; i++) { + off_t offset = sb->section3_offset + sb->cfg.section3_entry_size * i; - /* table has unordered ids+size, so if our id doesn't match current data offset must be beyond */ - if (read_32bit(offset + 0x00, sf) == sb->subblock_id) - break; - sb->stream_offset += read_32bit(offset + 0x04, sf); - } + /* table has unordered ids+size, so if our id doesn't match current data offset must be beyond */ + if (read_32bit(offset + 0x00, sf) == sb->subblock_id) + break; + sb->stream_offset += read_32bit(offset + 0x04, sf); } } @@ -2639,28 +2686,6 @@ static void config_sb_audio_fb(ubi_sb_header* sb, off_t flag_bits, int streamed_ sb->cfg.audio_subblock_and = subblock_and; sb->cfg.audio_loop_and = loop_and; } -static void config_sb_audio_fb_ps2_bnm(ubi_sb_header *sb, off_t flag_bits, int streamed_and, int cd_streamed_and, int loop_and) { - /* audio header with standard flags */ - sb->cfg.audio_streamed_flag = flag_bits; - sb->cfg.audio_cd_streamed_flag = flag_bits; - sb->cfg.audio_loop_flag = flag_bits; - sb->cfg.audio_streamed_and = streamed_and; - sb->cfg.audio_cd_streamed_and = cd_streamed_and; - sb->cfg.audio_loop_and = loop_and; -} -static void config_sb_audio_ps2_old(ubi_sb_header* sb, off_t flag_bits, int streamed_and, int loop_and, int loc_and, int stereo_and, off_t pitch, off_t sample_rate) { - /* sample rate only, bit flags */ - sb->cfg.audio_streamed_flag = flag_bits; - sb->cfg.audio_loop_flag = flag_bits; - sb->cfg.audio_loc_flag = flag_bits; - sb->cfg.audio_stereo_flag = flag_bits; - sb->cfg.audio_streamed_and = streamed_and; - sb->cfg.audio_loop_and = loop_and; - sb->cfg.audio_loc_and = loc_and; - sb->cfg.audio_stereo_and = stereo_and; - sb->cfg.audio_pitch = pitch; - sb->cfg.audio_sample_rate = sample_rate; -} static void config_sb_audio_hs(ubi_sb_header* sb, off_t channels, off_t sample_rate, off_t num_samples, off_t num_samples2, off_t stream_name, off_t stream_type) { /* audio header with stream name */ sb->cfg.audio_channels = channels; @@ -2679,6 +2704,30 @@ static void config_sb_audio_he(ubi_sb_header* sb, off_t channels, off_t sample_r sb->cfg.audio_extra_name = extra_name; sb->cfg.audio_stream_type = stream_type; } +static void config_sb_audio_ps2_bnm(ubi_sb_header *sb, off_t flag_bits, int streamed_and, int cd_streamed_and, int loop_and, off_t channels, off_t sample_rate) { + /* bit flags, channels and sample rate */ + sb->cfg.audio_streamed_flag = flag_bits; + sb->cfg.audio_cd_streamed_flag = flag_bits; + sb->cfg.audio_loop_flag = flag_bits; + sb->cfg.audio_streamed_and = streamed_and; + sb->cfg.audio_cd_streamed_and = cd_streamed_and; + sb->cfg.audio_loop_and = loop_and; + sb->cfg.audio_channels = channels; + sb->cfg.audio_sample_rate = sample_rate; +} +static void config_sb_audio_ps2_old(ubi_sb_header *sb, off_t flag_bits, int streamed_and, int loop_and, int loc_and, int stereo_and, off_t pitch, off_t sample_rate) { + /* bit flags, sample rate only */ + sb->cfg.audio_streamed_flag = flag_bits; + sb->cfg.audio_loop_flag = flag_bits; + sb->cfg.audio_loc_flag = flag_bits; + sb->cfg.audio_stereo_flag = flag_bits; + sb->cfg.audio_streamed_and = streamed_and; + sb->cfg.audio_loop_and = loop_and; + sb->cfg.audio_loc_and = loc_and; + sb->cfg.audio_stereo_and = stereo_and; + sb->cfg.audio_pitch = pitch; + sb->cfg.audio_sample_rate = sample_rate; +} static void config_sb_sequence(ubi_sb_header* sb, off_t sequence_count, off_t entry_size) { /* sequence header and chain table */ sb->cfg.sequence_sequence_loop = sequence_count - 0x10; @@ -2762,6 +2811,7 @@ static int config_sb_version(ubi_sb_header* sb, STREAMFILE* sf) { int is_bia_ps2 = 0, is_biadd_psp = 0; int is_sc2_ps2_gc = 0; int is_sc4_pc_online = 0; + int is_myst4_pc = 0; /* Most of the format varies with almost every game + platform (struct serialization?). * Support is configured case-by-case as offsets/order/fields only change slightly, @@ -2879,6 +2929,7 @@ static int config_sb_version(ubi_sb_header* sb, STREAMFILE* sf) { sb->cfg.map_version = 3; sb->cfg.map_entry_size = (sb->cfg.map_version < 2) ? 0x30 : 0x34; + sb->cfg.map_name = 0x10; if (sb->is_blk) { sb->cfg.map_entry_size = 0x30; } @@ -2936,7 +2987,6 @@ static int config_sb_version(ubi_sb_header* sb, STREAMFILE* sf) { if (sb->is_bnm || sb->is_dat || sb->is_ps2_bnm) { //sb->allowed_types[0x0a] = 1; /* only needed inside sequences */ sb->allowed_types[0x0b] = 1; - sb->allowed_types[0x09] = 1; } #if 0 @@ -2966,7 +3016,6 @@ static int config_sb_version(ubi_sb_header* sb, STREAMFILE* sf) { sb->version = 0x00000000; } - /* Tonic Touble beta has garbage instead of version */ if (sb->is_bnm && sb->version > 0x00000000 && sb->platform == UBI_PC) { if (check_project_file(sf, "ED_MAIN.LCB", 0)) { @@ -3028,7 +3077,6 @@ static int config_sb_version(ubi_sb_header* sb, STREAMFILE* sf) { config_sb_sequence(sb, 0x2c, 0x1c); /* no layers */ - return 1; } @@ -3053,7 +3101,7 @@ static int config_sb_version(ubi_sb_header* sb, STREAMFILE* sf) { config_sb_audio_fs(sb, 0x2c, 0x00, 0x30); config_sb_audio_hs(sb, 0x42, 0x3c, 0x34, 0x34, 0x48, 0x44); - /* has internal names but they're partially overwritten by sound index */ + /* has internal names but they're partially overwritten by subbank index */ config_sb_sequence(sb, 0x24, 0x18); @@ -3066,12 +3114,11 @@ static int config_sb_version(ubi_sb_header* sb, STREAMFILE* sf) { /* Rayman 2: Revolution (2000)(PS2)-bnm */ /* Disney's Dinosaur (2000)(PS2)-bnm */ /* Hype: The Time Quest (2001)(PS2)-bnm */ - if (sb->version == 0x32787370 && sb->platform == UBI_PS2 && sb->is_ps2_bnm) { + if (sb->version == 0x32787370 && sb->platform == UBI_PS2) { sb->version = 0x00000000; /* for convenience */ config_sb_entry(sb, 0x1c, 0x44); - config_sb_audio_fb_ps2_bnm(sb, 0x18, (1 << 5), (1 << 6), (1 << 7)); - config_sb_audio_hs(sb, 0x20, 0x22, 0x00, 0x00, 0x00, 0x1c); + config_sb_audio_ps2_bnm(sb, 0x18, (1 << 5), (1 << 6), (1 << 7), 0x20, 0x22); sb->cfg.audio_interleave = 0x400; config_sb_sequence(sb, 0x24, 0x14); @@ -3143,20 +3190,16 @@ static int config_sb_version(ubi_sb_header* sb, STREAMFILE* sf) { return 1; } -#if 0 - //todo group flags and maybe num_samples for sfx are off /* Myst III: Exile (2001)(PS2)-map */ if (sb->version == 0x00000004 && sb->platform == UBI_PS2) { config_sb_entry(sb, 0x34, 0x70); - config_sb_audio_fb(sb, 0x1c, (1 << 3), (1 << 6), (1 << 4)); //??? - config_sb_audio_hs(sb, 0x24, 0x28, 0x2c, 0x34, 0x44, 0x6c); - sb->cfg.audio_streamed_flag = 0x6c; /* no streamed flag? use codec as flag */ + config_sb_audio_fb(sb, 0x1c, (1 << 4), 0, (1 << 5)); + config_sb_audio_hs(sb, 0x24, 0x28, 0x34, 0x3c, 0x44, 0x6c); config_sb_sequence(sb, 0x2c, 0x24); return 1; } -#endif /* Splinter Cell (2002)(PC)-map */ /* Splinter Cell: Pandora Tomorrow (2004)(PC)-map */ @@ -3207,7 +3250,7 @@ static int config_sb_version(ubi_sb_header* sb, STREAMFILE* sf) { if (sb->version == 0x00000007 && sb->platform == UBI_PS2) { config_sb_entry(sb, 0x40, 0x70); - config_sb_audio_fb(sb, 0x1c, (1 << 2), 0, (1 << 4)); + config_sb_audio_fb(sb, 0x1c, (1 << 2), 0, (1 << 3)); config_sb_audio_hs(sb, 0x24, 0x28, 0x34, 0x3c, 0x44, 0x6c); /* num_samples may be null */ config_sb_sequence(sb, 0x2c, 0x30); @@ -3217,8 +3260,13 @@ static int config_sb_version(ubi_sb_header* sb, STREAMFILE* sf) { if (is_sc2_ps2_gc) { sb->cfg.map_entry_size = 0x38; - /* some amb .ss2 have bad sizes with mixed random data, bad extraction/unused crap? */ - /* Pandora Tomorrow voices have bad offsets too */ + sb->cfg.map_name = 0x18; + sb->cfg.has_rs_files = 1; + sb->cfg.audio_ram_streamed_flag = 0x1c; + sb->cfg.audio_ram_streamed_and = (1 << 3); + sb->cfg.audio_loop_and = (1 << 4); + /* some RAM sounds have bad sizes (ex #252, #10874) */ + sb->cfg.layer_hijack = LAYER_HIJACK_SCPT_PS2; /* some amb .ss1 layers (ex. #226, not #1927) have mixed garbage */ } return 1; } @@ -3238,6 +3286,7 @@ static int config_sb_version(ubi_sb_header* sb, STREAMFILE* sf) { if (is_sc2_ps2_gc) { sb->cfg.map_entry_size = 0x38; + sb->cfg.map_name = 0x18; sb->cfg.audio_streamed_and = 0x01000000; /* did somebody forget about BE? */ } return 1; @@ -3313,7 +3362,7 @@ static int config_sb_version(ubi_sb_header* sb, STREAMFILE* sf) { /* Prince of Persia: The Sands of Time (2003)(PS2)-bank 0x000A0004 / 0x000A0002 (POP1 port/Demo) */ /* Tom Clancy's Rainbow Six 3 (2003)(PS2)-bank 0x000A0007 */ /* Tom Clancy's Ghost Recon 2 (2004)(PS2)-bank 0x000A0007 */ - /* Splinter Cell: Pandora Tomorrow (2004)(PS2)-bank 0x000A0008 (separate banks from main map) */ + /* Splinter Cell: Pandora Tomorrow-online (2004)(PS2)-bank 0x000A0008 */ /* Prince of Persia: Warrior Within (Demo)(2004)(PS2)-bank 0x00100000 */ /* Prince of Persia: Warrior Within (2004)(PS2)-bank 0x00120009 */ if ((sb->version == 0x000A0002 && sb->platform == UBI_PS2) || @@ -3364,7 +3413,6 @@ static int config_sb_version(ubi_sb_header* sb, STREAMFILE* sf) { config_sb_audio_fs(sb, 0x24, 0x28, 0x34); config_sb_audio_hs(sb, 0x52, 0x4c, 0x38, 0x40, 0x58, 0x54); sb->cfg.audio_has_internal_names = 1; - sb->cfg.default_codec_for_subblock0 = 1; config_sb_sequence(sb, 0x28, 0x14); @@ -3381,8 +3429,6 @@ static int config_sb_version(ubi_sb_header* sb, STREAMFILE* sf) { config_sb_audio_fs(sb, 0x24, 0x28, 0x2c); config_sb_audio_hs(sb, 0x4a, 0x44, 0x30, 0x38, 0x50, 0x4c); - sb->cfg.audio_has_internal_names = 1; - sb->cfg.default_codec_for_subblock0 = 1; config_sb_sequence(sb, 0x28, 0x14); @@ -3418,7 +3464,6 @@ static int config_sb_version(ubi_sb_header* sb, STREAMFILE* sf) { config_sb_audio_fs(sb, 0x24, 0x28, 0x40); config_sb_audio_hs(sb, 0x5e, 0x58, 0x44, 0x4c, 0x64, 0x60); sb->cfg.audio_has_internal_names = 1; - sb->cfg.default_codec_for_subblock0 = 1; config_sb_sequence(sb, 0x28, 0x14); @@ -3429,7 +3474,7 @@ static int config_sb_version(ubi_sb_header* sb, STREAMFILE* sf) { return 1; } - /* Myst IV (Demo)(2004)(PC)-bank */ + /* Myst IV: Revelation (Demo)(2004)(PC)-bank */ if (sb->version == 0x00100000 && sb->platform == UBI_PC) { config_sb_entry(sb, 0x68, 0xa4); @@ -3439,11 +3484,21 @@ static int config_sb_version(ubi_sb_header* sb, STREAMFILE* sf) { return 1; } + /* two configs with same id; use project file as identifier */ + if (sb->version == 0x00120006 && sb->platform == UBI_PC) { + if (check_project_file(sf, "gamesnd_myst4.sp0", 1)) { + is_myst4_pc = 1; + } + } + + /* Myst IV: Revelation (2004)(PC)-bank 0x00120006 */ /* Prince of Persia: Warrior Within (Demo)(2004)(PC)-bank 0x00120006 */ /* Prince of Persia: Warrior Within (2004)(PC)-bank 0x00120009 */ if ((sb->version == 0x00120006 && sb->platform == UBI_PC) || (sb->version == 0x00120009 && sb->platform == UBI_PC)) { config_sb_entry(sb, 0x6c, 0x84); + if (is_myst4_pc) + config_sb_entry(sb, 0x6c, 0xa4); config_sb_audio_fs(sb, 0x24, 0x2c, 0x28); config_sb_audio_hs(sb, 0x4c, 0x44, 0x30, 0x38, 0x54, 0x50); @@ -3460,7 +3515,6 @@ static int config_sb_version(ubi_sb_header* sb, STREAMFILE* sf) { config_sb_audio_fs(sb, 0x24, 0x28, 0x40); config_sb_audio_hs(sb, 0x60, 0x58, 0x44, 0x4c, 0x68, 0x64); sb->cfg.audio_has_internal_names = 1; - sb->cfg.default_codec_for_subblock0 = 1; config_sb_sequence(sb, 0x28, 0x14); return 1; @@ -3489,6 +3543,8 @@ static int config_sb_version(ubi_sb_header* sb, STREAMFILE* sf) { /* Beowulf: The Game (2007)(PSP)-map */ if (sb->version == 0x0012000C && sb->platform == UBI_PSP && !is_biadd_psp) { config_sb_entry(sb, 0x68, 0x84); + if (is_biadd_psp) + config_sb_entry(sb, 0x80, 0x94); config_sb_audio_fs(sb, 0x24, 0x2c, 0x28); config_sb_audio_hs(sb, 0x4c, 0x44, 0x30, 0x38, 0x54, 0x50); @@ -3498,17 +3554,7 @@ static int config_sb_version(ubi_sb_header* sb, STREAMFILE* sf) { config_sb_layer_hs(sb, 0x1c, 0x60, 0x64, 0x30); config_sb_layer_sh(sb, 0x18, 0x00, 0x08, 0x0c, 0x14); - return 1; - } - - //todo some .sbX have bad external stream offsets, but not all (ex. offset 0xE3641 but should be 0x0A26) - /* Brothers in Arms: D-Day (2006)(PSP)-bank */ - if (sb->version == 0x0012000C && sb->platform == UBI_PSP && is_biadd_psp) { - config_sb_entry(sb, 0x80, 0x94); - - config_sb_audio_fs(sb, 0x24, 0x2c, 0x28); - config_sb_audio_hs(sb, 0x4c, 0x44, 0x30, 0x38, 0x54, 0x50); - sb->cfg.audio_has_internal_names = 1; + //todo some .sbX in BiA:DD have bad external stream offsets, but not all (ex. offset 0xE3641 but should be 0x0A26) return 1; } @@ -3523,7 +3569,7 @@ static int config_sb_version(ubi_sb_header* sb, STREAMFILE* sf) { return 1; } - /* Myst IV: Revelation (2005)(PC)-bank */ + /* Myst IV: Revelation (2005)(Xbox)-bank */ /* Splinter Cell: Chaos Theory (2005)(Xbox)-map */ if (sb->version == 0x00120012 && sb->platform == UBI_XBOX) { config_sb_entry(sb, 0x48, 0x4c); @@ -3535,6 +3581,20 @@ static int config_sb_version(ubi_sb_header* sb, STREAMFILE* sf) { return 1; } + /* Splinter Cell: Chaos Theory (2005)(PS2)-map */ + if (sb->version == 0x00130001 && sb->platform == UBI_PS2) { + config_sb_entry(sb, 0x48, 0x4c); + + config_sb_audio_fb(sb, 0x18, (1 << 2), (1 << 3), (1 << 4)); + config_sb_audio_he(sb, 0x20, 0x24, 0x30, 0x38, 0x40, 0x44); + + config_sb_sequence(sb, 0x28, 0x10); + + //config_sb_layer_he(sb, 0x1c, 0x28, 0x30, 0x34); + //config_sb_layer_sh(sb, 0x18, 0x00, 0x08, 0x0c, 0x14); + return 1; + } + /* Splinter Cell: Chaos Theory (2005)(GC)-map */ if (sb->version == 0x00130001 && sb->platform == UBI_GC) { config_sb_entry(sb, 0x68, 0x54); @@ -3689,7 +3749,7 @@ static int config_sb_version(ubi_sb_header* sb, STREAMFILE* sf) { config_sb_layer_he(sb, 0x20, 0x38, 0x40, 0x48); config_sb_layer_sh(sb, 0x30, 0x00, 0x08, 0x0c, 0x14); - sb->cfg.layer_hijack = 1; /* WTF!!! layer format different from other layers using same id!!! */ + sb->cfg.layer_hijack = LAYER_HIJACK_GRAW_X360; /* WTF!!! layer format different from other layers using same id!!! */ return 1; } @@ -3749,23 +3809,11 @@ static int config_sb_version(ubi_sb_header* sb, STREAMFILE* sf) { } } - /* Splinter Cell: Double Agent (2006)(PC)-map (offline) */ - if (sb->version == 0x00180006 && sb->platform == UBI_PC && !is_sc4_pc_online) { + /* Splinter Cell: Double Agent (2006)(PC)-map */ + if (sb->version == 0x00180006 && sb->platform == UBI_PC) { config_sb_entry(sb, 0x68, 0x7c); - - config_sb_audio_fs(sb, 0x2c, 0x34, 0x30); - config_sb_audio_he(sb, 0x5c, 0x54, 0x40, 0x48, 0x64, 0x60); - - config_sb_sequence(sb, 0x2c, 0x14); - - config_sb_layer_he(sb, 0x20, 0x38, 0x3c, 0x44); - config_sb_layer_sh(sb, 0x34, 0x00, 0x08, 0x0c, 0x14); - return 1; - } - - /* Splinter Cell: Double Agent (2006)(PC)-map (online) */ - if (sb->version == 0x00180006 && sb->platform == UBI_PC && is_sc4_pc_online) { - config_sb_entry(sb, 0x68, 0x78); + if (is_sc4_pc_online) + config_sb_entry(sb, 0x68, 0x78); config_sb_audio_fs(sb, 0x2c, 0x34, 0x30); config_sb_audio_he(sb, 0x5c, 0x54, 0x40, 0x48, 0x64, 0x60); @@ -3819,7 +3867,6 @@ static int config_sb_version(ubi_sb_header* sb, STREAMFILE* sf) { sb->cfg.audio_xma_offset = 0x68; config_sb_sequence(sb, 0x2c, 0x14); - return 1; } @@ -3899,7 +3946,6 @@ static int config_sb_version(ubi_sb_header* sb, STREAMFILE* sf) { config_sb_layer_he(sb, 0x20, 0x34, 0x38, 0x40); config_sb_layer_sh(sb, 0x30, 0x00, 0x04, 0x08, 0x10); - return 1; } @@ -3939,7 +3985,6 @@ static int config_sb_version(ubi_sb_header* sb, STREAMFILE* sf) { config_sb_audio_fs(sb, 0x2c, 0x30, 0x34); config_sb_audio_he(sb, 0x40, 0x44, 0x4c, 0x54, 0x5c, 0x60); - return 1; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ubi_sb_garbage_streamfile.h b/Frameworks/vgmstream/vgmstream/src/meta/ubi_sb_garbage_streamfile.h new file mode 100644 index 000000000..aeff5045d --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/ubi_sb_garbage_streamfile.h @@ -0,0 +1,41 @@ +#ifndef _UBI_SB_GARBAGE_STREAMFILE_H_ +#define _UBI_SB_GARBAGE_STREAMFILE_H_ +#include "deblock_streamfile.h" + + +/* In typical Ubisoft-insane fashion, some SC:PT PS2 (not GC) streams have mixed garbage (after 0x6B00 bytes has 0x4240). + * No apparent flag but seems to be related to stream sizes or samples (only files of +10MB, but not all). + * + * Since garbage is consistent between all files we can detect by checking expected crap. stream_sizes do take + * into account extra crap, while layers don't (offset field assumes no crap), so we use a separate de-garbage + * streamfile to simulate. */ +static int is_garbage_stream(STREAMFILE* sf) { + /* must test from file's beginning, not stream's */ + return get_streamfile_size(sf) >= 0x00800000 && + read_u32be(0x6B00, sf) == 0x6047BF7F && + read_u32be(0x6B04, sf) == 0x94FACC01; +} + +//static size_t get_garbage_stream_size(off_t offset, size_t size) { +// /* readjust size removing all possible garbage taking into account offset */ +//} + +static void block_callback(STREAMFILE* sf, deblock_io_data* data) { + data->block_size = 0x6b00 + 0x4240; + data->data_size = 0x6b00; +} + +static STREAMFILE* setup_ubi_sb_garbage_streamfile_f(STREAMFILE* new_sf) { + //STREAMFILE *new_sf = NULL; + deblock_config_t cfg = {0}; + + cfg.stream_start = 0; + cfg.block_callback = block_callback; + + /* setup sf */ + //new_sf = open_wrap_streamfile(sf); + new_sf = open_io_deblock_streamfile_f(new_sf, &cfg); + return new_sf; +} + +#endif /* _UBI_SB_GARBAGE_STREAMFILE_H_ */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ubi_sb_streamfile.h b/Frameworks/vgmstream/vgmstream/src/meta/ubi_sb_streamfile.h index a12f415ab..1829e9914 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ubi_sb_streamfile.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/ubi_sb_streamfile.h @@ -1,424 +1,417 @@ -#ifndef _UBI_SB_STREAMFILE_H_ -#define _UBI_SB_STREAMFILE_H_ -#include "../streamfile.h" - - -typedef struct { - /* config */ - off_t stream_offset; - off_t stream_size; - int layer_number; - int layer_count; - int layer_max; - int big_endian; - int layer_hijack; - - /* internal config */ - off_t header_next_start; /* offset to header field */ - off_t header_sizes_start; /* offset to header table */ - off_t header_data_start; /* offset to header data */ - off_t block_next_start; /* offset to block field */ - off_t block_sizes_start; /* offset to block table */ - off_t block_data_start; /* offset to block data */ - size_t header_size; /* derived */ - - /* state */ - off_t logical_offset; /* fake offset */ - off_t physical_offset; /* actual offset */ - size_t block_size; /* current size */ - size_t next_block_size; /* next size */ - size_t skip_size; /* size from block start to reach data */ - size_t data_size; /* usable size in a block */ - - size_t logical_size; -} ubi_sb_io_data; - - -static size_t ubi_sb_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length, ubi_sb_io_data* data) { - int32_t(*read_32bit)(off_t, STREAMFILE*) = data->big_endian ? read_32bitBE : read_32bitLE; - size_t total_read = 0; - int i; - - - /* re-start when previous offset (can't map logical<>physical offsets) */ - if (data->logical_offset < 0 || offset < data->logical_offset) { - data->physical_offset = data->stream_offset; - data->logical_offset = 0x00; - data->data_size = 0; - - /* process header block (slightly different and data size may be 0) */ - { - data->block_size = data->header_size; - data->next_block_size = read_32bit(data->physical_offset + data->header_next_start, streamfile); - - if (data->header_sizes_start) { - data->skip_size = data->header_data_start; - for (i = 0; i < data->layer_number; i++) { - data->skip_size += read_32bit(data->physical_offset + data->header_sizes_start + i*0x04, streamfile); - } - data->data_size = read_32bit(data->physical_offset + data->header_sizes_start + data->layer_number*0x04, streamfile); - } - - if (data->data_size == 0) { - data->physical_offset += data->block_size; - } - } - } - - - /* read blocks */ - while (length > 0) { - - /* ignore EOF */ - if (offset < 0 || data->physical_offset >= data->stream_offset + data->stream_size) { - break; - } - - /* process new block */ - if (data->data_size == 0) { - data->block_size = data->next_block_size; - if (data->block_next_start) /* not set when fixed block size */ - data->next_block_size = read_32bit(data->physical_offset + data->block_next_start, streamfile); - - data->skip_size = data->block_data_start; - for (i = 0; i < data->layer_number; i++) { - data->skip_size += read_32bit(data->physical_offset + data->block_sizes_start + i*0x04, streamfile); - } - data->data_size = read_32bit(data->physical_offset + data->block_sizes_start + data->layer_number*0x04, streamfile); - } - - /* move to next block */ - if (offset >= data->logical_offset + data->data_size) { - if (data->block_size == 0 || data->block_size == 0xFFFFFFFF) - break; - data->physical_offset += data->block_size; - data->logical_offset += data->data_size; - data->data_size = 0; - continue; - } - - /* read data */ - { - size_t bytes_consumed, bytes_done, to_read; - - bytes_consumed = offset - data->logical_offset; - to_read = data->data_size - bytes_consumed; - if (to_read > length) - to_read = length; - bytes_done = read_streamfile(dest, data->physical_offset + data->skip_size + bytes_consumed, to_read, streamfile); - - total_read += bytes_done; - dest += bytes_done; - offset += bytes_done; - length -= bytes_done; - - if (bytes_done != to_read || bytes_done == 0) { - break; /* error/EOF */ - } - } - } - - return total_read; -} - -static size_t ubi_sb_io_size(STREAMFILE *streamfile, ubi_sb_io_data* data) { - uint8_t buf[1]; - - if (data->logical_size) - return data->logical_size; - - /* force a fake read at max offset, to get max logical_offset (will be reset next read) */ - ubi_sb_io_read(streamfile, buf, 0x7FFFFFFF, 1, data); - data->logical_size = data->logical_offset; - - return data->logical_size; -} - - -static int ubi_sb_io_init(STREAMFILE *streamfile, ubi_sb_io_data* data) { - int32_t (*read_32bit)(off_t,STREAMFILE*) = data->big_endian ? read_32bitBE : read_32bitLE; - off_t offset = data->stream_offset; - uint32_t version; - int i; - - if (data->stream_offset + data->stream_size > get_streamfile_size(streamfile)) { - VGM_LOG("UBI SB: bad size\n"); - goto fail; - } - - /* Layers have a main header, then headered blocks with data. - * We configure stuff to unify parsing of all variations. */ - version = (uint32_t)read_32bit(offset+0x00, streamfile); - - /* it was bound to happen... orz */ - if (data->layer_hijack == 1 && version == 0x000B0008) - version = 0xFFFF0007; - - switch(version) { - case 0x00000002: /* Splinter Cell */ - /* - layer header - * 0x04: layer count - * 0x08: stream size - * 0x0c: block header size - * 0x10: block size (fixed) - * 0x14: min layer size? - * - block header - * 0x00: block number - * 0x04: block offset - * 0x08+(04*N): layer size per layer - * 0xNN: layer data per layer */ - data->layer_max = read_32bit(offset+0x04, streamfile); - - data->header_next_start = 0x10; - data->header_sizes_start = 0; - data->header_data_start = 0x18; - - data->block_next_start = 0; - data->block_sizes_start = 0x08; - data->block_data_start = 0x08 + data->layer_max*0x04; - break; - - case 0x00000003: /* Rainbow Six 3 */ - /* - layer header - * 0x04: layer count - * 0x08: stream size - * 0x0c: block header size - * 0x10: block size (fixed) - * 0x14: min layer data? - * 0x18: size of header sizes and headers - * 0x1c+(04*N): header size per layer - * - block header - * 0x00: block number - * 0x04: block offset - * 0x08+(04*N): layer size per layer - * 0xNN: layer data per layer */ - data->layer_max = read_32bit(offset+0x04, streamfile); - - data->header_next_start = 0x10; - data->header_sizes_start = 0x1c; - data->header_data_start = 0x1c + data->layer_max*0x04; - - data->block_next_start = 0; - data->block_sizes_start = 0x08; - data->block_data_start = 0x08 + data->layer_max*0x04; - break; - - case 0x00000004: /* Prince of Persia: Sands of Time, Batman: Rise of Sin Tzu */ - /* - layer header - * 0x04: layer count - * 0x08: stream size - * 0x0c: block count - * 0x10: block header size - * 0x14: block size (fixed) - * 0x18: min layer data? - * 0x1c: size of header sizes and headers - * 0x20+(04*N): header size per layer - * - block header - * 0x00: block number - * 0x04: block offset - * 0x08: always 0x03 - * 0x0c+(04*N): layer size per layer - * 0xNN: layer data per layer */ - data->layer_max = read_32bit(offset+0x04, streamfile); - - data->header_next_start = 0x14; - data->header_sizes_start = 0x20; - data->header_data_start = 0x20 + data->layer_max*0x04; - - data->block_next_start = 0; - data->block_sizes_start = 0x0c; - data->block_data_start = 0x0c + data->layer_max*0x04; - break; - - case 0x00000007: /* Splinter Cell: Essentials, Splinter Cell 3D */ - /* - layer header - * 0x04: config? - * 0x08: layer count - * 0x0c: stream size - * 0x10: block count - * 0x14: block header size - * 0x18: block size (fixed) - * 0x1c+(04*8): min layer data? for 8 layers (-1 after layer count) - * 0x3c: size of header sizes and headers - * 0x40+(04*N): header size per layer - * 0xNN: header data per layer - * - block header - * 0x00: block number - * 0x04: block offset - * 0x08: always 0x03 - * 0x0c+(04*N): layer size per layer - * 0xNN: layer data per layer */ - data->layer_max = read_32bit(offset+0x08, streamfile); - - data->header_next_start = 0x18; - data->header_sizes_start = 0x40; - data->header_data_start = 0x40 + data->layer_max*0x04; - - data->block_next_start = 0; - data->block_sizes_start = 0x0c; - data->block_data_start = 0x0c + data->layer_max*0x04; - break; - - case 0xFFFF0007: /* Ghost Recon Advanced Warfighter (X360) */ - /* - layer header - * 0x04: config? - * 0x08: layer count - * 0x0c: stream size - * 0x10: block count - * 0x14: block header size - * 0x18: block size (fixed) - * 0x1c+(04*11): min layer data? for 11 layers (-1 after layer count) - * 0x48: size of header sizes and headers - * 0x4c+(04*N): header size per layer - * 0xNN: header data per layer - * - block header - * 0x00: block number - * 0x04: block offset - * 0x08: always 0x03 - * 0x0c+(04*N): layer size per layer - * 0xNN: layer data per layer */ - data->layer_max = read_32bit(offset+0x08, streamfile); - - data->header_next_start = 0x18; - data->header_sizes_start = 0x4c; - data->header_data_start = 0x4c + data->layer_max*0x04; - - data->block_next_start = 0; - data->block_sizes_start = 0x0c; - data->block_data_start = 0x0c + data->layer_max*0x04; - break; - - case 0x00040008: /* Assassin's Creed */ - case 0x000B0008: /* Open Season, Surf's Up, TMNT, Splinter Cell HD */ - case 0x000C0008: /* Splinter Cell: Double Agent */ - case 0x00100008: /* Rainbow Six 2 */ - /* - layer header - * 0x04: config? - * 0x08: layer count - * 0x0c: blocks count - * 0x10: block header size - * 0x14: size of header sizes and headers/data - * 0x18: next block size - * 0x1c+(04*N): layer header size - * 0xNN: header data per layer - * - block header: - * 0x00: always 0x03 - * 0x04: next block size - * 0x08+(04*N): layer size per layer - * 0xNN: layer data per layer */ - data->layer_max = read_32bit(offset+0x08, streamfile); - - data->header_next_start = 0x18; - data->header_sizes_start = 0x1c; - data->header_data_start = 0x1c + data->layer_max*0x04; - - data->block_next_start = 0x04; - data->block_sizes_start = 0x08; - data->block_data_start = 0x08 + data->layer_max*0x04; - break; - - case 0x00100009: /* Splinter Cell: Pandora Tomorrow HD, Prince of Persia 2008, Scott Pilgrim */ - /* - layer header - * 0x04: config? - * 0x08: layer count - * 0x0c: blocks count - * 0x10: block header size - * 0x14: size of header sizes and headers/data - * 0x18: next block size - * 0x1c+(04*10): usable size per layer - * 0x5c+(04*N): layer header size - * 0xNN: header data per layer - * - block header: - * 0x00: always 0x03 - * 0x04: next block size - * 0x08+(04*N): layer size per layer - * 0xNN: layer data per layer */ - data->layer_max = read_32bit(offset+0x08, streamfile); - - data->header_next_start = 0x18; - data->header_sizes_start = 0x5c; - data->header_data_start = 0x5c + data->layer_max*0x04; - - data->block_next_start = 0x04; - data->block_sizes_start = 0x08; - data->block_data_start = 0x08 + data->layer_max*0x04; - break; - - default: - VGM_LOG("UBI SB: unknown layer header %08x\n", version); - goto fail; - } - - /* get base size to simplify later parsing */ - data->header_size = data->header_data_start; - if (data->header_sizes_start) { - for (i = 0; i < data->layer_max; i++) { - data->header_size += read_32bit(offset + data->header_sizes_start + i*0x04, streamfile); - } - } - - /* force read header block */ - data->logical_offset = -1; - - /* just in case some headers may use less layers that stream has */ - VGM_ASSERT(data->layer_count != data->layer_max, "UBI SB: non-matching layer counts\n"); - if (data->layer_count > data->layer_max) { - VGM_LOG("UBI SB: layer count bigger than layer max\n"); - goto fail; - } - - /* Common layer quirks: - * - layer format depends on its own version and not on platform or DARE engine version - * - codec header may be in the layer header, or in the first block - * - stream size doesn't include padding - * - block number goes from 1 to block_count - * - block offset is relative to layer start - * - blocks data size varies between blocks and between layers in the same block - * - "config?" is a small value that varies between streams of the same game - * - next block size is 0 at last block - * - both Ubi SB and Ubi BAO use same-version layers - */ - - return 1; -fail: - return 0; -} - - -/* Handles deinterleaving of Ubisoft's headered+blocked 'multitrack' streams */ -static STREAMFILE* setup_ubi_sb_streamfile(STREAMFILE *streamFile, off_t stream_offset, size_t stream_size, int layer_number, int layer_count, int big_endian, int layer_hijack) { - STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL; - ubi_sb_io_data io_data = {0}; - size_t io_data_size = sizeof(ubi_sb_io_data); - - io_data.stream_offset = stream_offset; - io_data.stream_size = stream_size; - io_data.layer_number = layer_number; - io_data.layer_count = layer_count; - io_data.big_endian = big_endian; - io_data.layer_hijack = layer_hijack; - - if (!ubi_sb_io_init(streamFile, &io_data)) - goto fail; - - /* setup subfile */ - new_streamFile = open_wrap_streamfile(streamFile); - if (!new_streamFile) goto fail; - temp_streamFile = new_streamFile; - - new_streamFile = open_io_streamfile(new_streamFile, &io_data,io_data_size, ubi_sb_io_read,ubi_sb_io_size); - if (!new_streamFile) goto fail; - temp_streamFile = new_streamFile; - - new_streamFile = open_buffer_streamfile(new_streamFile,0); - if (!new_streamFile) goto fail; - temp_streamFile = new_streamFile; - - return temp_streamFile; - -fail: - close_streamfile(temp_streamFile); - return NULL; -} - -#endif /* _UBI_SB_STREAMFILE_H_ */ +#ifndef _UBI_SB_STREAMFILE_H_ +#define _UBI_SB_STREAMFILE_H_ +#include "../streamfile.h" +#include "ubi_sb_garbage_streamfile.h" + + +typedef struct { + /* config */ + off_t stream_offset; + off_t stream_size; + int layer_number; + int layer_count; + int layer_max; + int big_endian; + int layer_hijack; + + /* internal config */ + off_t header_next_start; /* offset to header field */ + off_t header_sizes_start; /* offset to header table */ + off_t header_data_start; /* offset to header data */ + off_t block_next_start; /* offset to block field */ + off_t block_sizes_start; /* offset to block table */ + off_t block_data_start; /* offset to block data */ + size_t header_size; /* derived */ + + /* state */ + off_t logical_offset; /* fake offset */ + off_t physical_offset; /* actual offset */ + size_t block_size; /* current size */ + size_t next_block_size; /* next size */ + size_t skip_size; /* size from block start to reach data */ + size_t data_size; /* usable size in a block */ + + size_t logical_size; +} ubi_sb_io_data; + + +static size_t ubi_sb_io_read(STREAMFILE* sf, uint8_t* dest, off_t offset, size_t length, ubi_sb_io_data* data) { + uint32_t(*read_u32)(off_t, STREAMFILE*) = data->big_endian ? read_u32be : read_u32le; + size_t total_read = 0; + int i; + + + /* re-start when previous offset (can't map logical<>physical offsets) */ + if (data->logical_offset < 0 || offset < data->logical_offset) { + data->physical_offset = data->stream_offset; + data->logical_offset = 0x00; + data->data_size = 0; + + /* process header block (slightly different and data size may be 0) */ + { + data->block_size = data->header_size; + data->next_block_size = read_u32(data->physical_offset + data->header_next_start, sf); + + if (data->header_sizes_start) { + data->skip_size = data->header_data_start; + for (i = 0; i < data->layer_number; i++) { + data->skip_size += read_u32(data->physical_offset + data->header_sizes_start + i*0x04, sf); + } + data->data_size = read_u32(data->physical_offset + data->header_sizes_start + data->layer_number*0x04, sf); + } + + if (data->data_size == 0) { + data->physical_offset += data->block_size; + } + } + } + + + /* read blocks */ + while (length > 0) { + + /* ignore EOF */ + if (offset < 0 || data->physical_offset >= data->stream_offset + data->stream_size) { + break; + } + + /* process new block */ + if (data->data_size == 0) { + data->block_size = data->next_block_size; + if (data->block_next_start) /* not set when fixed block size */ + data->next_block_size = read_u32(data->physical_offset + data->block_next_start, sf); + + data->skip_size = data->block_data_start; + for (i = 0; i < data->layer_number; i++) { + data->skip_size += read_u32(data->physical_offset + data->block_sizes_start + i*0x04, sf); + } + data->data_size = read_u32(data->physical_offset + data->block_sizes_start + data->layer_number*0x04, sf); + } + + /* move to next block */ + if (offset >= data->logical_offset + data->data_size) { + if (data->block_size == 0 || data->block_size == 0xFFFFFFFF) + break; + data->physical_offset += data->block_size; + data->logical_offset += data->data_size; + data->data_size = 0; + continue; + } + + /* read data */ + { + size_t bytes_consumed, bytes_done, to_read; + + bytes_consumed = offset - data->logical_offset; + to_read = data->data_size - bytes_consumed; + if (to_read > length) + to_read = length; + bytes_done = read_streamfile(dest, data->physical_offset + data->skip_size + bytes_consumed, to_read, sf); + + total_read += bytes_done; + dest += bytes_done; + offset += bytes_done; + length -= bytes_done; + + if (bytes_done != to_read || bytes_done == 0) { + break; /* error/EOF */ + } + } + } + + return total_read; +} + +static size_t ubi_sb_io_size(STREAMFILE* sf, ubi_sb_io_data* data) { + uint8_t buf[1]; + + if (data->logical_size) + return data->logical_size; + + /* force a fake read at max offset, to get max logical_offset (will be reset next read) */ + ubi_sb_io_read(sf, buf, 0x7FFFFFFF, 1, data); + data->logical_size = data->logical_offset; + + return data->logical_size; +} + + +static int ubi_sb_io_init(STREAMFILE* sf, ubi_sb_io_data* data) { + uint32_t(*read_u32)(off_t, STREAMFILE*) = data->big_endian ? read_u32be : read_u32le; + off_t offset = data->stream_offset; + uint32_t version; + int i; + + if (data->stream_offset + data->stream_size > get_streamfile_size(sf)) { + VGM_LOG("UBI SB: bad size\n"); + goto fail; + } + + /* Layers have a main header, then headered blocks with data. + * We configure stuff to unify parsing of all variations. */ + version = read_u32(offset+0x00, sf); + + /* it was bound to happen... orz */ + if (data->layer_hijack == 1 && version == 0x000B0008) + version = 0xFFFF0007; + + switch(version) { + case 0x00000002: /* Splinter Cell */ + /* - layer header + * 0x04: layer count + * 0x08: stream size + * 0x0c: block header size + * 0x10: block size (fixed) + * 0x14: min layer size? + * - block header + * 0x00: block number + * 0x04: block offset + * 0x08+(04*N): layer size per layer + * 0xNN: layer data per layer */ + data->layer_max = read_u32(offset+0x04, sf); + + data->header_next_start = 0x10; + data->header_sizes_start = 0; + data->header_data_start = 0x18; + + data->block_next_start = 0; + data->block_sizes_start = 0x08; + data->block_data_start = 0x08 + data->layer_max*0x04; + break; + + case 0x00000003: /* Rainbow Six 3 */ + /* - layer header + * 0x04: layer count + * 0x08: stream size + * 0x0c: block header size + * 0x10: block size (fixed) + * 0x14: min layer data? + * 0x18: size of header sizes and headers + * 0x1c+(04*N): header size per layer + * - block header + * 0x00: block number + * 0x04: block offset + * 0x08+(04*N): layer size per layer + * 0xNN: layer data per layer */ + data->layer_max = read_u32(offset+0x04, sf); + + data->header_next_start = 0x10; + data->header_sizes_start = 0x1c; + data->header_data_start = 0x1c + data->layer_max*0x04; + + data->block_next_start = 0; + data->block_sizes_start = 0x08; + data->block_data_start = 0x08 + data->layer_max*0x04; + break; + + case 0x00000004: /* Prince of Persia: Sands of Time, Batman: Rise of Sin Tzu */ + /* - layer header + * 0x04: layer count + * 0x08: stream size + * 0x0c: block count + * 0x10: block header size + * 0x14: block size (fixed) + * 0x18: min layer data? + * 0x1c: size of header sizes and headers + * 0x20+(04*N): header size per layer + * - block header + * 0x00: block number + * 0x04: block offset + * 0x08: always 0x03 + * 0x0c+(04*N): layer size per layer + * 0xNN: layer data per layer */ + data->layer_max = read_u32(offset+0x04, sf); + + data->header_next_start = 0x14; + data->header_sizes_start = 0x20; + data->header_data_start = 0x20 + data->layer_max*0x04; + + data->block_next_start = 0; + data->block_sizes_start = 0x0c; + data->block_data_start = 0x0c + data->layer_max*0x04; + break; + + case 0x00000007: /* Splinter Cell: Essentials, Splinter Cell 3D */ + /* - layer header + * 0x04: config? + * 0x08: layer count + * 0x0c: stream size + * 0x10: block count + * 0x14: block header size + * 0x18: block size (fixed) + * 0x1c+(04*8): min layer data? for 8 layers (-1 after layer count) + * 0x3c: size of header sizes and headers + * 0x40+(04*N): header size per layer + * 0xNN: header data per layer + * - block header + * 0x00: block number + * 0x04: block offset + * 0x08: always 0x03 + * 0x0c+(04*N): layer size per layer + * 0xNN: layer data per layer */ + data->layer_max = read_u32(offset+0x08, sf); + + data->header_next_start = 0x18; + data->header_sizes_start = 0x40; + data->header_data_start = 0x40 + data->layer_max*0x04; + + data->block_next_start = 0; + data->block_sizes_start = 0x0c; + data->block_data_start = 0x0c + data->layer_max*0x04; + break; + + case 0xFFFF0007: /* Ghost Recon Advanced Warfighter (X360) */ + /* - layer header + * 0x04: config? + * 0x08: layer count + * 0x0c: stream size + * 0x10: block count + * 0x14: block header size + * 0x18: block size (fixed) + * 0x1c+(04*11): min layer data? for 11 layers (-1 after layer count) + * 0x48: size of header sizes and headers + * 0x4c+(04*N): header size per layer + * 0xNN: header data per layer + * - block header + * 0x00: block number + * 0x04: block offset + * 0x08: always 0x03 + * 0x0c+(04*N): layer size per layer + * 0xNN: layer data per layer */ + data->layer_max = read_u32(offset+0x08, sf); + + data->header_next_start = 0x18; + data->header_sizes_start = 0x4c; + data->header_data_start = 0x4c + data->layer_max*0x04; + + data->block_next_start = 0; + data->block_sizes_start = 0x0c; + data->block_data_start = 0x0c + data->layer_max*0x04; + break; + + case 0x00040008: /* Assassin's Creed */ + case 0x000B0008: /* Open Season, Surf's Up, TMNT, Splinter Cell HD */ + case 0x000C0008: /* Splinter Cell: Double Agent */ + case 0x00100008: /* Rainbow Six 2 */ + /* - layer header + * 0x04: config? + * 0x08: layer count + * 0x0c: blocks count + * 0x10: block header size + * 0x14: size of header sizes and headers/data + * 0x18: next block size + * 0x1c+(04*N): layer header size + * 0xNN: header data per layer + * - block header: + * 0x00: always 0x03 + * 0x04: next block size + * 0x08+(04*N): layer size per layer + * 0xNN: layer data per layer */ + data->layer_max = read_u32(offset+0x08, sf); + + data->header_next_start = 0x18; + data->header_sizes_start = 0x1c; + data->header_data_start = 0x1c + data->layer_max*0x04; + + data->block_next_start = 0x04; + data->block_sizes_start = 0x08; + data->block_data_start = 0x08 + data->layer_max*0x04; + break; + + case 0x00100009: /* Splinter Cell: Pandora Tomorrow HD, Prince of Persia 2008, Scott Pilgrim */ + /* - layer header + * 0x04: config? + * 0x08: layer count + * 0x0c: blocks count + * 0x10: block header size + * 0x14: size of header sizes and headers/data + * 0x18: next block size + * 0x1c+(04*10): usable size per layer + * 0x5c+(04*N): layer header size + * 0xNN: header data per layer + * - block header: + * 0x00: always 0x03 + * 0x04: next block size + * 0x08+(04*N): layer size per layer + * 0xNN: layer data per layer */ + data->layer_max = read_u32(offset+0x08, sf); + + data->header_next_start = 0x18; + data->header_sizes_start = 0x5c; + data->header_data_start = 0x5c + data->layer_max*0x04; + + data->block_next_start = 0x04; + data->block_sizes_start = 0x08; + data->block_data_start = 0x08 + data->layer_max*0x04; + break; + + default: + VGM_LOG("UBI SB: unknown layer header %08x\n", version); + goto fail; + } + + /* get base size to simplify later parsing */ + data->header_size = data->header_data_start; + if (data->header_sizes_start) { + for (i = 0; i < data->layer_max; i++) { + data->header_size += read_u32(offset + data->header_sizes_start + i*0x04, sf); + } + } + + /* force read header block */ + data->logical_offset = -1; + + /* just in case some headers may use less layers that stream has */ + VGM_ASSERT(data->layer_count != data->layer_max, "UBI SB: non-matching layer counts\n"); + if (data->layer_count > data->layer_max) { + VGM_LOG("UBI SB: layer count bigger than layer max\n"); + goto fail; + } + + /* Common layer quirks: + * - layer format depends on its own version and not on platform or DARE engine version + * - codec header may be in the layer header, or in the first block + * - stream size doesn't include padding + * - block number goes from 1 to block_count + * - block offset is relative to layer start + * - blocks data size varies between blocks and between layers in the same block + * - "config?" is a small value that varies between streams of the same game + * - next block size is 0 at last block + * - both Ubi SB and Ubi BAO use same-version layers + */ + + return 1; +fail: + return 0; +} + + +/* Handles deinterleaving of Ubisoft's headered+blocked 'multitrack' streams */ +static STREAMFILE* setup_ubi_sb_streamfile(STREAMFILE* sf, off_t stream_offset, size_t stream_size, int layer_number, int layer_count, int big_endian, int layer_hijack) { + STREAMFILE* new_sf = NULL; + ubi_sb_io_data io_data = {0}; + + io_data.stream_offset = stream_offset; + io_data.stream_size = stream_size; + io_data.layer_number = layer_number; + io_data.layer_count = layer_count; + io_data.big_endian = big_endian; + io_data.layer_hijack = layer_hijack; + + if (!ubi_sb_io_init(sf, &io_data)) + goto fail; + + /* setup subfile */ + new_sf = open_wrap_streamfile(sf); + if (layer_hijack == 2 && is_garbage_stream(sf)) { + new_sf = setup_ubi_sb_garbage_streamfile_f(new_sf); + //io_data.stream_size = get_garbage_stream_size(stream_offset, stream_size); //todo must do relative calcs, doesn't seem needed + } + new_sf = open_io_streamfile_f(new_sf, &io_data, sizeof(ubi_sb_io_data), ubi_sb_io_read,ubi_sb_io_size); + new_sf = open_buffer_streamfile_f(new_sf,0); + return new_sf; +fail: + return NULL; +} + +#endif /* _UBI_SB_STREAMFILE_H_ */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/wii_bns.c b/Frameworks/vgmstream/vgmstream/src/meta/wii_bns.c index 34cfda301..aa4b086f2 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/wii_bns.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/wii_bns.c @@ -3,72 +3,76 @@ #include "../util.h" /* BNS - Wii "Banner Sound" disc jingle */ -VGMSTREAM * init_vgmstream_wii_bns(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; - off_t BNS_offset; - uint32_t info_offset=0,data_offset=0; +VGMSTREAM* init_vgmstream_wii_bns(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + off_t bns_offset; + uint32_t info_offset = 0, data_offset = 0; uint32_t channel_info_offset_list_offset; - int channel_count; - int loop_flag; - uint16_t sample_rate; + int channels, loop_flag, sample_rate; uint32_t sample_count, loop_start; - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("bns",filename_extension(filename))) goto fail; + /* checks */ + /* .bin: actual extension + * .bns: header id */ + if (!check_extensions(sf, "bin,lbin,bns")) + goto fail; - // check header - BNS_offset = 0; - if (read_32bitBE(BNS_offset,streamFile) == 0x494D4435) // IMD5 - { - // Skip IMD5 header if present - BNS_offset = 0x20; + bns_offset = 0; + if (is_id32be(bns_offset + 0x40, sf, "IMET")) { + /* regular .bnr, find sound.bin offset */ + bns_offset = read_u32be(bns_offset + 0x44,sf); + + /* tables, probably not ok for all cases */ + bns_offset += read_u32be(bns_offset + 0x54,sf); } - if (read_32bitBE(BNS_offset+0x00,streamFile) != 0x424E5320) goto fail; // "BNS " - if ((uint32_t)read_32bitBE(BNS_offset+0x04,streamFile) != 0xFEFF0100u) goto fail; + if (is_id32be(bns_offset + 0x00, sf, "IMD5")) { + /* skip IMD5 header if present */ + bns_offset += 0x20; + } - // find chunks, verify + if (!is_id32be(bns_offset + 0x00,sf, "BNS ")) + goto fail; + if (read_u32be(bns_offset + 0x04,sf) != 0xFEFF0100u) + goto fail; + + /* find chunks */ { - // file size as claimed by header - uint32_t header_file_size = read_32bitBE(BNS_offset+0x08,streamFile); - - uint32_t header_size = read_16bitBE(BNS_offset+0xc,streamFile); - uint16_t chunk_count = read_16bitBE(BNS_offset+0xe,streamFile); - + uint32_t file_size = read_u32be(bns_offset+0x08,sf); + uint32_t header_size = read_u16be(bns_offset+0x0c,sf); + uint16_t chunk_count = read_u16be(bns_offset+0x0e,sf); int i; - // assume BNS is the last thing in the file - if (header_file_size + BNS_offset != get_streamfile_size(streamFile)) + /* assume BNS is the last thing in the file */ + if (file_size + bns_offset != get_streamfile_size(sf)) goto fail; for (i = 0; i < chunk_count; i++) { - uint32_t chunk_info_offset = BNS_offset+0x10+i*8; + uint32_t chunk_info_offset = bns_offset+0x10+i*0x08; uint32_t chunk_offset, chunk_size; // ensure chunk info is within header - if (chunk_info_offset+8 > BNS_offset+header_size) goto fail; + if (chunk_info_offset+8 > bns_offset+header_size) goto fail; - chunk_offset = BNS_offset + (uint32_t)read_32bitBE(chunk_info_offset,streamFile); - chunk_size = (uint32_t)read_32bitBE(chunk_info_offset+4,streamFile); + chunk_offset = bns_offset + (uint32_t)read_32bitBE(chunk_info_offset,sf); + chunk_size = (uint32_t)read_32bitBE(chunk_info_offset+4,sf); // ensure chunk is within file - if (chunk_offset < BNS_offset+header_size || - chunk_offset+chunk_size > BNS_offset+header_file_size) goto fail; + if (chunk_offset < bns_offset+header_size || + chunk_offset+chunk_size > bns_offset+file_size) goto fail; // ensure chunk size in header matches that listed in chunk // Note: disabled for now, as the Homebrew Channel BNS has a DATA // chunk that doesn't include the header size - //if ((uint32_t)read_32bitBE(chunk_offset+4,streamFile) != chunk_size) goto fail; + //if ((uint32_t)read_32bitBE(chunk_offset+4,sf) != chunk_size) goto fail; // handle each chunk type - switch (read_32bitBE(chunk_offset,streamFile)) { + switch (read_32bitBE(chunk_offset,sf)) { case 0x494E464F: // INFO - info_offset = chunk_offset+8; + info_offset = chunk_offset+0x08; break; case 0x44415441: // DATA - data_offset = chunk_offset+8; + data_offset = chunk_offset+0x08; break; default: goto fail; @@ -82,72 +86,65 @@ VGMSTREAM * init_vgmstream_wii_bns(STREAMFILE *streamFile) { /* parse out basic stuff in INFO */ { /* only seen this zero, specifies DSP format? */ - if (read_8bit(info_offset+0x00,streamFile) != 0) goto fail; + if (read_8bit(info_offset+0x00,sf) != 0) goto fail; - loop_flag = read_8bit(info_offset+0x01,streamFile); - channel_count = read_8bit(info_offset+0x02,streamFile); + loop_flag = read_8bit(info_offset+0x01,sf); + channels = read_8bit(info_offset+0x02,sf); /* only seen zero, padding? */ - if (read_8bit(info_offset+0x03,streamFile) != 0) goto fail; + if (read_8bit(info_offset+0x03,sf) != 0) goto fail; - sample_rate = (uint16_t)read_16bitBE(info_offset+0x04,streamFile); + sample_rate = read_u16be(info_offset+0x04,sf); /* only seen this zero, padding? */ - if (read_16bitBE(info_offset+0x06,streamFile) != 0) goto fail; + if (read_u16be(info_offset+0x06,sf) != 0) goto fail; - loop_start = read_32bitBE(info_offset+0x08,streamFile); + loop_start = read_s32be(info_offset+0x08,sf); - sample_count = read_32bitBE(info_offset+0x0c,streamFile); + sample_count = read_s32be(info_offset+0x0c,sf); - channel_info_offset_list_offset = info_offset + (uint32_t)read_32bitBE(info_offset+0x10,streamFile); + channel_info_offset_list_offset = info_offset + read_u32be(info_offset+0x10,sf); } /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); + vgmstream = allocate_vgmstream(channels, loop_flag); if (!vgmstream) goto fail; - /* fill in the vital statistics */ - vgmstream->channels = channel_count; - vgmstream->sample_rate = sample_rate; - vgmstream->coding_type = coding_NGC_DSP; - vgmstream->num_samples = sample_count; - vgmstream->layout_type = layout_none; vgmstream->meta_type = meta_WII_BNS; + vgmstream->sample_rate = sample_rate; + vgmstream->num_samples = sample_count; + vgmstream->loop_start_sample = loop_start; + vgmstream->loop_end_sample = sample_count; - if (loop_flag) - { - vgmstream->loop_start_sample = loop_start; - vgmstream->loop_end_sample = sample_count; - } + vgmstream->coding_type = coding_NGC_DSP; + vgmstream->layout_type = layout_none; + //todo cleanup /* open the file for reading */ { - int i; - for (i=0;ich[i].streamfile = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); + vgmstream->ch[i].streamfile = reopen_streamfile(sf, 0); if (!vgmstream->ch[i].streamfile) goto fail; + vgmstream->ch[i].channel_start_offset = vgmstream->ch[i].offset = channel_data_offset; - vgmstream->ch[i].channel_start_offset= - vgmstream->ch[i].offset=channel_data_offset; - - for (j=0;j<16;j++) - vgmstream->ch[i].adpcm_coef[j] = - read_16bitBE(channel_dsp_offset+j*2,streamFile); + for (j = 0; j < 16; j++) { + vgmstream->ch[i].adpcm_coef[j] = read_s16be(channel_dsp_offset + j*0x02,sf); + } } } + //if (!vgmstream_open_stream(vgmstream, sf, start_offset)) + // goto fail; return vgmstream; - - /* clean up anything we may have opened */ fail: - if (vgmstream) close_vgmstream(vgmstream); + close_vgmstream(vgmstream); return NULL; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/wwise.c b/Frameworks/vgmstream/vgmstream/src/meta/wwise.c index 815650ee0..f689e5617 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/wwise.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/wwise.c @@ -9,7 +9,7 @@ * Some info: https://www.audiokinetic.com/en/library/edge/ * .bnk (dynamic music/loop) info: https://github.com/bnnm/wwiser */ -typedef enum { PCM, IMA, VORBIS, DSP, XMA2, XWMA, AAC, HEVAG, ATRAC9, OPUSNX, OPUS, OPUSWW, PTADPCM } wwise_codec; +typedef enum { PCM, IMA, VORBIS, DSP, XMA2, XWMA, AAC, HEVAG, ATRAC9, OPUSNX, OPUS, OPUSCPR, OPUSWW, PTADPCM } wwise_codec; typedef struct { int big_endian; size_t file_size; @@ -492,6 +492,22 @@ VGMSTREAM* init_vgmstream_wwise(STREAMFILE* sf) { break; } + case OPUSCPR: { /* CD Project RED's Ogg Opus masquerading as PCMEX [Cyberpunk 2077 (PC)] */ + if (ww.bits_per_sample != 16) goto fail; + + /* original data_size doesn't look related to samples or anything */ + ww.data_size = ww.file_size - ww.data_offset; + + vgmstream->codec_data = init_ffmpeg_offset(sf, ww.data_offset, ww.data_size); + if (!vgmstream->codec_data) goto fail; + vgmstream->coding_type = coding_FFmpeg; + vgmstream->layout_type = layout_none; + + /* FFmpeg's samples seem correct, otherwise see ogg_opus.c for getting samples. */ + vgmstream->num_samples = (int32_t)((ffmpeg_codec_data*)vgmstream->codec_data)->totalSamples; + break; + } + case OPUSWW: { /* updated Opus [Assassin's Creed Valhalla (PC)] */ int mapping; opus_config cfg = {0}; @@ -884,6 +900,16 @@ static int parse_wwise(STREAMFILE* sf, wwise_header* ww) { } } + + /* Cyberpunk 2077 has some mutant .wem, with proper Wwise header and PCMEX but data is standard OPUS. + * 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->truncated) { + if (read_u32be(ww->data_offset + 0x00, sf) == 0x4F676753) { + ww->codec = OPUSCPR; + } + } + return 1; fail: return 0; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/xmv_valve.c b/Frameworks/vgmstream/vgmstream/src/meta/xmv_valve.c index cf37a03ba..2e61b64d2 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/xmv_valve.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/xmv_valve.c @@ -178,7 +178,7 @@ VGMSTREAM *init_vgmstream_xmv_valve(STREAMFILE *streamFile) { break; } #endif - case 0x02: /* ADPCM (not actually implemented, was probably supposed to be Microsoft ADPCM) */ + case 0x02: /* ADPCM (not actually implemented, was probably supposed to be Microsoft ADPCM or Xbox IMA) */ default: goto fail; } diff --git a/Frameworks/vgmstream/vgmstream/src/mixing.c b/Frameworks/vgmstream/vgmstream/src/mixing.c index a88c36ef4..ce0257bc1 100644 --- a/Frameworks/vgmstream/vgmstream/src/mixing.c +++ b/Frameworks/vgmstream/vgmstream/src/mixing.c @@ -664,7 +664,7 @@ void mixing_push_fade(VGMSTREAM* vgmstream, int ch_dst, double vol_start, double * pre1 start1 end1 post1 * - when pre and post are set nothing is done (fade is exact and multiple fades may overlap) * - when previous fade's post or current fade's pre are negative (meaning file end/start) - * they should cancel each other (to allow chaning fade-in + fade-out + fade-in + etc): + * they should cancel each other (to allow chaining fade-in + fade-out + fade-in + etc): * <----------|----------|----------| |----------|----------|----------> * pre1 start1 end1 post1 pre2 start2 end2 post2 * - other cases (previous fade is actually after/in-between current fade) are ignored diff --git a/Frameworks/vgmstream/vgmstream/src/plugins.c b/Frameworks/vgmstream/vgmstream/src/plugins.c index f9199a3ce..af03eed3f 100644 --- a/Frameworks/vgmstream/vgmstream/src/plugins.c +++ b/Frameworks/vgmstream/vgmstream/src/plugins.c @@ -81,7 +81,7 @@ void vgmstream_get_title(char* buf, int buf_len, const char* filename, VGMSTREAM strncpy(buf, pos, buf_len); /* name without extension */ - if (cfg->remove_extension) { + if (cfg && cfg->remove_extension) { pos2 = strrchr(buf, '.'); if (pos2 && strlen(pos2) < 15) /* too big extension = file name probably has a dot in the middle */ pos2[0] = '\0'; @@ -167,14 +167,14 @@ static void load_default_config(play_config_t* def, play_config_t* tcfg) { def->fade_time_set = 1; } - /* loop priority: #i > #e > #E */ + /* loop priority: #i > #e > #E (respect player's ignore too) */ if (tcfg->really_force_loop) { - def->ignore_loop = 0; + //def->ignore_loop = 0; def->force_loop = 0; def->really_force_loop = 1; } if (tcfg->force_loop) { - def->ignore_loop = 0; + //def->ignore_loop = 0; def->force_loop = 1; def->really_force_loop = 0; } diff --git a/Frameworks/vgmstream/vgmstream/src/streamfile.h b/Frameworks/vgmstream/vgmstream/src/streamfile.h index 83d684171..d5c4c4eeb 100644 --- a/Frameworks/vgmstream/vgmstream/src/streamfile.h +++ b/Frameworks/vgmstream/vgmstream/src/streamfile.h @@ -307,6 +307,10 @@ static inline int min_s32(int32_t a, int32_t b) { return a < b ? a : b; } //align32, align16, clamp16, etc #endif +static inline const int is_id32be(off_t offset, STREAMFILE* sf, const char* s) { + return read_u32be(offset, sf) == get_id32be(s); +} + //TODO: maybe move to streamfile.c /* guess byte endianness from a given value, return true if big endian and false if little endian */ static inline int guess_endianness16bit(off_t offset, STREAMFILE* sf) { diff --git a/Frameworks/vgmstream/vgmstream/src/util.h b/Frameworks/vgmstream/vgmstream/src/util.h index f27a8a400..87b903470 100644 --- a/Frameworks/vgmstream/vgmstream/src/util.h +++ b/Frameworks/vgmstream/vgmstream/src/util.h @@ -98,6 +98,10 @@ static inline int clamp16(int32_t val) { else return val; } +static inline const uint32_t get_id32be(const char* s) { + return (uint32_t)(s[0] << 24) | (s[1] << 16) | (s[2] << 8) | (s[3] << 0); +} + /* less common functions, no need to inline */ int round10(int val); diff --git a/Frameworks/vgmstream/vgmstream/src/vgmstream.c b/Frameworks/vgmstream/vgmstream/src/vgmstream.c index ad6cf472d..dac2703c4 100644 --- a/Frameworks/vgmstream/vgmstream/src/vgmstream.c +++ b/Frameworks/vgmstream/vgmstream/src/vgmstream.c @@ -200,7 +200,7 @@ VGMSTREAM* (*init_vgmstream_functions[])(STREAMFILE* sf) = { init_vgmstream_redspark, init_vgmstream_ivaud, init_vgmstream_wii_wsd, - init_vgmstream_wii_ndp, + init_vgmstream_dsp_ndp, init_vgmstream_ps2_sps, init_vgmstream_ps2_xa2_rrp, init_vgmstream_nds_hwas, @@ -512,6 +512,10 @@ VGMSTREAM* (*init_vgmstream_functions[])(STREAMFILE* sf) = { init_vgmstream_xws, init_vgmstream_cpk, init_vgmstream_opus_nsopus, + init_vgmstream_sbk, + init_vgmstream_dsp_wiiadpcm, + init_vgmstream_dsp_cwac, + init_vgmstream_ifs, /* lowest priority metas (should go after all metas, and TXTH should go before raw formats) */ init_vgmstream_txth, /* proper parsers should supersede TXTH, once added */ diff --git a/Frameworks/vgmstream/vgmstream/src/vgmstream.h b/Frameworks/vgmstream/vgmstream/src/vgmstream.h index d907be423..939bd8030 100644 --- a/Frameworks/vgmstream/vgmstream/src/vgmstream.h +++ b/Frameworks/vgmstream/vgmstream/src/vgmstream.h @@ -146,6 +146,7 @@ typedef enum { coding_REF_IMA, /* Reflections IMA ADPCM */ coding_AWC_IMA, /* Rockstar AWC IMA ADPCM */ coding_UBI_IMA, /* Ubisoft IMA ADPCM */ + coding_UBI_SCE_IMA, /* Ubisoft SCE IMA ADPCM */ coding_H4M_IMA, /* H4M IMA ADPCM (stereo or mono, high nibble first) */ coding_MTF_IMA, /* Capcom MT Framework IMA ADPCM */ coding_CD_IMA, /* Crystal Dynamics IMA ADPCM */ @@ -745,6 +746,9 @@ typedef enum { meta_WADY, meta_DSP_SQEX, meta_DSP_WIIVOICE, + meta_SBK, + meta_DSP_WIIADPCM, + meta_DSP_CWAC, } meta_t; /* standard WAVEFORMATEXTENSIBLE speaker positions */