diff --git a/cli/vgmstream_cli.c b/cli/vgmstream_cli.c index 1a4ee775..1fe89833 100644 --- a/cli/vgmstream_cli.c +++ b/cli/vgmstream_cli.c @@ -77,7 +77,7 @@ int main(int argc, char ** argv) { int print_adxencd = 0; int print_oggenc = 0; int print_batchvar = 0; - int write_lwav = 0; + int write_lwav = 0, write_lwav_loop_start = 0, write_lwav_loop_end = 0; int only_stereo = -1; int stream_index = 0; double loop_count = 2.0; @@ -220,6 +220,12 @@ int main(int argc, char ** argv) { vgmstream_force_loop(vgmstream, 0, 0,0); } + if (write_lwav) { + write_lwav_loop_start = vgmstream->loop_start_sample; + write_lwav_loop_end = vgmstream->loop_end_sample; + vgmstream_force_loop(vgmstream, 0, 0,0); + } + if (play_sdtout) { if (outfilename) { fprintf(stderr,"either -p or -o, make up your mind\n"); @@ -318,17 +324,11 @@ int main(int argc, char ** argv) { { uint8_t wav_buf[0x100]; int channels = (only_stereo != -1) ? 2 : vgmstream->channels; - int smpl_chunk = (write_lwav && vgmstream->loop_flag); size_t bytes_done; bytes_done = make_wav_header(wav_buf,0x100, len_samples, vgmstream->sample_rate, channels, - smpl_chunk, vgmstream->loop_start_sample, vgmstream->loop_end_sample); - - /* once "smpl" with loops is written we don't want actual file looping */ - if (smpl_chunk) { - vgmstream_force_loop(vgmstream, 0, 0,0); - } + write_lwav, write_lwav_loop_start, write_lwav_loop_end); fwrite(wav_buf,sizeof(uint8_t),bytes_done,outfile); } @@ -407,26 +407,22 @@ int main(int argc, char ** argv) { vgmstream_force_loop(vgmstream, 0, 0,0); } + if (write_lwav) { + write_lwav_loop_start = vgmstream->loop_start_sample; + write_lwav_loop_end = vgmstream->loop_end_sample; + vgmstream_force_loop(vgmstream, 0, 0,0); + } + /* slap on a .wav header */ { uint8_t wav_buf[0x100]; int channels = (only_stereo != -1) ? 2 : vgmstream->channels; - int smpl_chunk = (write_lwav && vgmstream->loop_flag); size_t bytes_done; bytes_done = make_wav_header(wav_buf,0x100, len_samples, vgmstream->sample_rate, channels, - smpl_chunk, vgmstream->loop_start_sample, vgmstream->loop_end_sample); + write_lwav, write_lwav_loop_start, write_lwav_loop_end); - /* once "smpl" with looping is written we don't want actual file looping */ - if (smpl_chunk) { - vgmstream_force_loop(vgmstream, 0, 0,0); - } - - if (write_lwav && vgmstream->loop_flag) { // Adding space for smpl chunk at end - int32_t bytecount = get_32bitLE((uint8_t*)buf + 4); - put_32bitLE((uint8_t*)buf + 4, bytecount + 0x44); - } fwrite(wav_buf,sizeof(uint8_t),bytes_done,outfile); } @@ -499,7 +495,7 @@ static size_t make_wav_header(uint8_t * buf, size_t buf_size, int32_t sample_cou data_size = sample_count*channels*sizeof(sample); header_size = 0x2c; - if (smpl_chunk) + if (smpl_chunk && loop_end) header_size += 0x3c+ 0x08; if (header_size > buf_size) @@ -519,7 +515,7 @@ static size_t make_wav_header(uint8_t * buf, size_t buf_size, int32_t sample_cou put_16bitLE(buf+0x20, (int16_t)(channels*sizeof(sample))); /* block align */ put_16bitLE(buf+0x22, sizeof(sample)*8); /* significant bits per sample */ - if (smpl_chunk) { + if (smpl_chunk && loop_end) { make_smpl_chunk(buf+0x24, loop_start, loop_end); memcpy(buf+0x24+0x3c+0x08, "data", 0x04); /* WAVE data chunk */ put_32bitLE(buf+0x28+0x3c+0x08, (int32_t)data_size); /* size of WAVE data chunk */ diff --git a/src/coding/mpeg_custom_utils.c b/src/coding/mpeg_custom_utils.c index f225f931..a7737ddf 100644 --- a/src/coding/mpeg_custom_utils.c +++ b/src/coding/mpeg_custom_utils.c @@ -58,7 +58,11 @@ int mpeg_custom_setup_init_default(STREAMFILE *streamFile, off_t start_offset, m break; case MPEG_LYN: - goto fail; /* not fully implemented */ + if (data->config.interleave <= 0) + goto fail; /* needs external fixed size */ + data->default_buffer_size = data->config.interleave; + //todo simplify/unify XVAG/P3D/SCD/LYN and just feed arbitrary chunks to the decoder + break; case MPEG_STANDARD: case MPEG_AHX: @@ -137,9 +141,9 @@ int mpeg_custom_parse_frame_default(VGMSTREAMCHANNEL *stream, mpeg_codec_data *d case MPEG_P3D: /* fixed interleave, not frame-aligned (ie. blocks may end/start in part of a frame) */ case MPEG_SCD: + case MPEG_LYN: current_interleave = data->config.interleave; -#if 1 /* check if current interleave block is short */ { off_t block_offset = stream->offset - stream->channel_start_offset; @@ -148,7 +152,7 @@ int mpeg_custom_parse_frame_default(VGMSTREAMCHANNEL *stream, mpeg_codec_data *d if (data->config.data_size && block_offset + next_block >= data->config.data_size) current_interleave = (data->config.data_size % next_block) / data->streams_size; /* short_interleave*/ } -#endif + current_interleave_pre = current_interleave*num_stream; current_interleave_post = current_interleave*(data->streams_size-1) - current_interleave_pre; @@ -162,7 +166,7 @@ int mpeg_custom_parse_frame_default(VGMSTREAMCHANNEL *stream, mpeg_codec_data *d break; } if (!current_data_size || current_data_size > ms->buffer_size) { - VGM_LOG("MPEG: incorrect data_size 0x%x\n", current_data_size); + VGM_LOG("MPEG: incorrect data_size 0x%x vs buffer 0x%x\n", current_data_size, ms->buffer_size); goto fail; } diff --git a/src/coding/mpeg_custom_utils_ealayer3.c b/src/coding/mpeg_custom_utils_ealayer3.c index c8d827f1..a4f4e9c6 100644 --- a/src/coding/mpeg_custom_utils_ealayer3.c +++ b/src/coding/mpeg_custom_utils_ealayer3.c @@ -638,7 +638,7 @@ fail: } -/* Skip EA-frames from other streams for multichannel (interleaved 1 EA-frame per stream). +/* Skip EA-frames from other streams for .sns/sps multichannel (interleaved 1 EA-frame per stream). * Due to EALayer3 being in blocks and other complexities (we can't go past a block) all * streams's offsets should start in the first stream's EA-frame. * @@ -649,6 +649,8 @@ fail: * - skip one EA-frame per following streams until offset is in first stream's EA-frame * (ie. 1st stream skips 2, 2nd stream skips 1, 3rd stream skips 0) * - repeat again for granule1 + * + * EALayer3 v1 in SCHl uses external offsets and 1ch multichannel instead. */ static int ealayer3_skip_data(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data, int num_stream, int at_start) { int ok, i; @@ -657,6 +659,9 @@ static int ealayer3_skip_data(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data, i uint8_t ibuf[EALAYER3_EA_FRAME_BUFFER_SIZE]; int skips = at_start ? num_stream : data->streams_size - 1 - num_stream; + /* v1 does multichannel with set offsets */ + if (data->type == MPEG_EAL31) + return 1; for (i = 0; i < skips; i++) { is.buf = ibuf; diff --git a/src/coding/mpeg_decoder.c b/src/coding/mpeg_decoder.c index 165c21cd..7f0ca49c 100644 --- a/src/coding/mpeg_decoder.c +++ b/src/coding/mpeg_decoder.c @@ -131,6 +131,8 @@ mpeg_codec_data *init_mpeg_custom(STREAMFILE *streamFile, off_t start_offset, co memcpy(&data->config, config, sizeof(mpeg_custom_config)); data->config.channels = channels; + data->default_buffer_size = MPEG_DATA_BUFFER_SIZE; + /* init per subtype */ switch(data->type) { case MPEG_EAL31: @@ -145,6 +147,7 @@ mpeg_codec_data *init_mpeg_custom(STREAMFILE *streamFile, off_t start_offset, co if (channels <= 0 || channels > 16) goto fail; /* arbitrary max */ if (channels < data->channels_per_frame) goto fail; + if (data->default_buffer_size > 0x8000) goto fail; /* init streams */ @@ -161,7 +164,7 @@ mpeg_codec_data *init_mpeg_custom(STREAMFILE *streamFile, off_t start_offset, co if (!data->streams[i]->output_buffer) goto fail; /* one per stream as sometimes mpg123 can't read the whole buffer in one pass */ - data->streams[i]->buffer_size = MPEG_DATA_BUFFER_SIZE; + data->streams[i]->buffer_size = data->default_buffer_size; data->streams[i]->buffer = calloc(sizeof(uint8_t), data->streams[i]->buffer_size); if (!data->streams[i]->buffer) goto fail; } diff --git a/src/formats.c b/src/formats.c index 015fdf57..2fcf59db 100644 --- a/src/formats.c +++ b/src/formats.c @@ -198,7 +198,9 @@ static const char* extension_list[] = { //"mpc", //FFmpeg, not parsed (musepack) //common "mpdsp", "mpds", + "ms", "msa", + "msb", "msd", "msf", "mss", @@ -241,7 +243,7 @@ static const char* extension_list[] = { "ps2stm", //fake extension for .stm (to be removed) "psh", // fake extension for VSV(?) Dawn of Mana needs to be checked again "psnd", - "psw", + "psw", //fake extension for .wam "r", "rac", //txth/reserved [Manhunt (Xbox)] @@ -292,6 +294,7 @@ static const char* extension_list[] = { "sgd", "sgx", "sl3", + "slb", //txth/reserved [THE Nekomura no Hitobito (PS2)] "sli", "smp", "smpl", //fake extension (to be removed) @@ -303,6 +306,7 @@ static const char* extension_list[] = { "snr", "sns", "snu", + "son", "spd", "spm", "sps", @@ -346,6 +350,7 @@ static const char* extension_list[] = { "vawx", "vb", "vbk", + "vbx", //txth/reserved [THE Taxi 2 (PS2)] "vds", "vdm", "vgs", @@ -682,7 +687,7 @@ static const meta_info meta_info_list[] = { {meta_AUS, "Capcom AUS Header"}, {meta_RWS, "RenderWare RWS header"}, {meta_EA_1SNH, "Electronic Arts 1SNh/EACS header"}, - {meta_SL3, "SL3 Header"}, + {meta_SL3, "Atari Melbourne House SL3 header"}, {meta_FSB1, "FMOD Sample Bank (FSB1) Header"}, {meta_FSB2, "FMOD Sample Bank (FSB2) Header"}, {meta_FSB3, "FMOD Sample Bank (FSB3) Header"}, @@ -719,7 +724,6 @@ static const meta_info meta_info_list[] = { {meta_SCD_PCM, "Lunar: Eternal Blue .PCM header"}, {meta_PS2_PCM, "Konami KCEJ East .PCM header"}, {meta_PS2_RKV, "Legacy of Kain - Blood Omen 2 RKV PS2 header"}, - {meta_PS2_PSW, "Rayman Raving Rabbids Riff Container File"}, {meta_PS2_VAS, "Pro Baseball Spirits 5 VAS Header"}, {meta_PS2_TEC, "assumed TECMO badflagged stream by .tec extension"}, {meta_XBOX_WVS, "Metal Arms WVS Header (XBOX)"}, @@ -764,7 +768,7 @@ static const meta_info meta_info_list[] = { {meta_FFXI_SPW, "SPW SeWave header"}, {meta_PS2_ASS, "ASS Header"}, {meta_IDSP, "IDSP Header"}, - {meta_WAA_WAC_WAD_WAM, "WAA/WAC/WAD/WAM RIFF Header"}, + {meta_UBI_JADE, "Ubisoft Jade RIFF header"}, {meta_PS2_SEG, "SEG (PS2) Header"}, {meta_XBOX_SEG, "SEG (XBOX) Header"}, {meta_NDS_STRM_FFTA2, "Final Fantasy Tactics A2 RIFF Header"}, @@ -790,7 +794,6 @@ static const meta_info meta_info_list[] = { {meta_NGC_DSP_IADP, "IADP Header"}, {meta_RSTM_shrunken, "Nintendo RSTM header, corrupted by Atlus"}, {meta_RIFF_WAVE_MWV, "RIFF WAVE header with .mwv flavoring"}, - {meta_RIFF_WAVE_SNS, "RIFF WAVE header with .sns flavoring"}, {meta_FFCC_STR, "Final Fantasy: Crystal Chronicles STR header"}, {meta_SAT_BAKA, "BAKA header from Crypt Killer"}, {meta_NDS_SWAV, "SWAV Header"}, @@ -984,6 +987,9 @@ static const meta_info meta_info_list[] = { {meta_MP4, "MP4/AAC header"}, {meta_PCM_SRE, "Capcom .PCM+SRE header"}, {meta_DSP_MCADPCM, "Bethesda .mcadpcm header"}, + {meta_UBI_LYN, "Ubisoft LyN RIFF header"}, + {meta_MSB_MSH, "Sony MSB+MSH header"}, + {meta_OGG_RPGMV, "Ogg Vorbis (RPGMV header)"}, #ifdef VGM_USE_FFMPEG {meta_FFmpeg, "FFmpeg supported file format"}, diff --git a/src/layout/blocked_ea_schl.c b/src/layout/blocked_ea_schl.c index 68e4ffef..9a9c5f3b 100644 --- a/src/layout/blocked_ea_schl.c +++ b/src/layout/blocked_ea_schl.c @@ -126,14 +126,22 @@ void block_update_ea_schl(off_t block_offset, VGMSTREAM * vgmstream) { break; #ifdef VGM_USE_MPEG - /* id, size, samples, offset?, unknown (null for MP2, some constant for all blocks for EALayer3) */ + /* id, size, samples, offsets, unknown (null for MP2, some size/config for EALayer3; only if not >2ch) */ case coding_MPEG_custom: case coding_MPEG_layer1: case coding_MPEG_layer2: case coding_MPEG_layer3: case coding_MPEG_ealayer3: for (i = 0; i < vgmstream->channels; i++) { - off_t channel_start = read_32bit(block_offset + 0x0C,streamFile); + off_t channel_start; + + /* EALayer3 6ch uses 1ch*6 with offsets, no flag in header [Medal of Honor 2010 (PC) movies] */ + if (vgmstream->channels > 2) { + channel_start = read_32bit(block_offset + 0x0C + 0x04*i,streamFile); + } else { + channel_start = read_32bit(block_offset + 0x0C,streamFile); + } + vgmstream->ch[i].offset = block_offset + 0x0C + (0x04*vgmstream->channels) + channel_start; } diff --git a/src/libvgmstream.vcproj b/src/libvgmstream.vcproj index eeac4134..d8db6afd 100644 --- a/src/libvgmstream.vcproj +++ b/src/libvgmstream.vcproj @@ -231,6 +231,10 @@ + + + + + + @@ -958,10 +970,6 @@ RelativePath=".\meta\ps2_psh.c" > - - @@ -1278,6 +1286,10 @@ RelativePath=".\meta\ubi_ckd.c" > + + @@ -1307,7 +1319,7 @@ > + @@ -143,6 +144,7 @@ + @@ -334,7 +336,6 @@ - @@ -400,13 +401,14 @@ + - + diff --git a/src/libvgmstream.vcxproj.filters b/src/libvgmstream.vcxproj.filters index 03b28dfb..d327ac76 100644 --- a/src/libvgmstream.vcxproj.filters +++ b/src/libvgmstream.vcxproj.filters @@ -80,6 +80,9 @@ meta\Header Files + + meta\Header Files + meta\Header Files @@ -583,9 +586,6 @@ meta\Source Files - - meta\Source Files - meta\Source Files @@ -781,6 +781,9 @@ meta\Source Files + + meta\Source Files + meta\Source Files @@ -799,7 +802,7 @@ meta\Source Files - + meta\Source Files @@ -1231,6 +1234,9 @@ meta\Source Files + + meta\Source Files + coding\Source Files diff --git a/src/meta/meta.h b/src/meta/meta.h index d22ca4b0..0499902a 100644 --- a/src/meta/meta.h +++ b/src/meta/meta.h @@ -221,8 +221,6 @@ VGMSTREAM * init_vgmstream_ps2_pcm(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_ps2_rkv(STREAMFILE * streamFile); -VGMSTREAM * init_vgmstream_ps2_psw(STREAMFILE * streamFile); - VGMSTREAM * init_vgmstream_ps2_vas(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_ps2_tec(STREAMFILE * streamFile); @@ -319,7 +317,8 @@ VGMSTREAM * init_vgmstream_spw(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_ps2_ass(STREAMFILE * streamFile); -VGMSTREAM * init_vgmstream_waa_wac_wad_wam(STREAMFILE * streamFile); +VGMSTREAM * init_vgmstream_ubi_jade(STREAMFILE * streamFile); +VGMSTREAM * init_vgmstream_ubi_jade_container(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_seg(STREAMFILE * streamFile); @@ -730,4 +729,9 @@ VGMSTREAM * init_vgmstream_pcm_sre(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_dsp_mcadpcm(STREAMFILE *streamFile); +VGMSTREAM * init_vgmstream_ubi_lyn(STREAMFILE * streamFile); +VGMSTREAM * init_vgmstream_ubi_lyn_container(STREAMFILE * streamFile); + +VGMSTREAM * init_vgmstream_msb_msh(STREAMFILE * streamFile); + #endif /*_META_H*/ diff --git a/src/meta/msb_msh.c b/src/meta/msb_msh.c new file mode 100644 index 00000000..a5abc447 --- /dev/null +++ b/src/meta/msb_msh.c @@ -0,0 +1,86 @@ +#include "meta.h" +#include "../coding/coding.h" + +/* MSB+MSH - Sony sfx container companion of MIH+MIB [namCollection - Ace Combat 2 (PS2) sfx, EyeToy Play (PS2)] */ +VGMSTREAM * init_vgmstream_msb_msh(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + STREAMFILE * streamHeader = NULL; + off_t start_offset, header_offset = 0; + size_t stream_size; + int loop_flag = 0, channel_count, sample_rate; + int total_subsongs, target_subsong = streamFile->stream_index; + + + /* checks */ + if (!check_extensions(streamFile, "msb")) + goto fail; + + streamHeader = open_streamfile_by_ext(streamFile, "msh"); + if (!streamHeader) goto fail; + + if (read_32bitLE(0x00,streamHeader) != get_streamfile_size(streamHeader)) + goto fail; + /* 0x04: unknown */ + + /* parse entries */ + { + int i; + int entries = read_32bitLE(0x08,streamHeader); + + total_subsongs = 0; + if (target_subsong == 0) target_subsong = 1; + + for (i = 0; i < entries; i++) { + if (read_32bitLE(0x0c + 0x10*i, streamHeader) == 0) /* size 0 = empty entry */ + continue; + + total_subsongs++; + if (total_subsongs == target_subsong && !header_offset) { + header_offset = 0x0c + 0x10*i; + } + } + + if (!header_offset) goto fail; + if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail; + } + + + + loop_flag = 0; + channel_count = 1; + + stream_size = read_32bitLE(header_offset+0x00, streamHeader); + if (read_32bitLE(header_offset+0x04, streamHeader) != 0) /* stereo flag? */ + goto fail; + start_offset = read_32bitLE(header_offset+0x08, streamHeader); + sample_rate = read_32bitLE(header_offset+0x0c, streamHeader); /* Ace Combat 2 seems to set wrong values but probably their bug */ + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count,loop_flag); + if (!vgmstream) goto fail; + + vgmstream->sample_rate = sample_rate; + vgmstream->num_samples = ps_bytes_to_samples(stream_size,channel_count); + + vgmstream->num_streams = total_subsongs; + vgmstream->stream_size = stream_size; + vgmstream->meta_type = meta_MSB_MSH; + + vgmstream->coding_type = coding_PSX; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x10; + + + close_streamfile(streamHeader); + + if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + goto fail; + return vgmstream; + + +fail: + close_streamfile(streamHeader); + close_vgmstream(vgmstream); + return NULL; +} diff --git a/src/meta/ogg_vorbis.c b/src/meta/ogg_vorbis.c index fc030dab..dc32db64 100644 --- a/src/meta/ogg_vorbis.c +++ b/src/meta/ogg_vorbis.c @@ -176,6 +176,29 @@ static void l2sd_ogg_decryption_callback(void *ptr, size_t size, size_t nmemb, v } } +static void rpgmvo_ogg_decryption_callback(void *ptr, size_t size, size_t nmemb, void *datasource) { + static const uint8_t header[16] = { /* OggS, packet type, granule, stream id(empty) */ + 0x4F,0x67,0x67,0x53,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 + }; + size_t bytes_read = size*nmemb; + ogg_vorbis_streamfile * const ov_streamfile = datasource; + int i; + + /* first 0x10 are xor'd with a key, but the header can be easily reconstructed + * (key is also in (game)/www/data/System.json "encryptionKey") */ + for (i = 0; i < bytes_read; i++) { + if (ov_streamfile->offset+i < 0x10) { + ((uint8_t*)ptr)[i] = header[(ov_streamfile->offset + i) % 16]; + + /* last two bytes are the stream id, get from next OggS */ + if (ov_streamfile->offset+i == 0x0e) + ((uint8_t*)ptr)[i] = read_8bit(0x58, ov_streamfile->streamfile); + if (ov_streamfile->offset+i == 0x0f) + ((uint8_t*)ptr)[i] = read_8bit(0x59, ov_streamfile->streamfile); + } + } +} + /* Ogg Vorbis, by way of libvorbisfile; may contain loop comments */ VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) { @@ -189,6 +212,7 @@ VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) { int is_sngw = 0; int is_isd = 0; int is_l2sd = 0; + int is_rpgmvo = 0; /* check extension */ @@ -206,6 +230,8 @@ VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) { is_sngw = 1; } else if (check_extensions(streamFile,"isd")) { /* .isd: Azure Striker Gunvolt (PC) */ is_isd = 1; + } else if (check_extensions(streamFile,"rpgmvo")) { /* .rpgmvo: RPG Maker MV games (PC) */ + is_rpgmvo = 1; } else { goto fail; } @@ -267,6 +293,18 @@ VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) { // 0x0c(2): PCM block size, 0x0e(2): PCM bps, 0x10: null, 0x18: samples (in PCM bytes) } + /* check RPGMKVO (RPG Maker MV), header + minor encryption */ + if (is_rpgmvo) { + if (read_32bitBE(0x00,streamFile) != 0x5250474D && /* "RPGM" */ + read_32bitBE(0x00,streamFile) != 0x56000000) { /* "V\0\0\0" */ + goto fail; + } + ovmi.decryption_callback = rpgmvo_ogg_decryption_callback; + + start_offset = 0x10; + } + + if (is_um3) { ovmi.meta_type = meta_OGG_UM3; @@ -280,6 +318,8 @@ VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) { ovmi.meta_type = meta_OGG_ISD; } else if (is_l2sd) { ovmi.meta_type = meta_OGG_L2SD; + } else if (is_rpgmvo) { + ovmi.meta_type = meta_OGG_RPGMV; } else { ovmi.meta_type = meta_OGG_VORBIS; } diff --git a/src/meta/ps2_psw.c b/src/meta/ps2_psw.c deleted file mode 100644 index 6ac4ffa2..00000000 --- a/src/meta/ps2_psw.c +++ /dev/null @@ -1,88 +0,0 @@ -#include "meta.h" -#include "../util.h" - -/* PSW (from Rayman Raving Rabbids) -...coefs are missing for the dsp type... */ -VGMSTREAM * init_vgmstream_ps2_psw(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; - off_t start_offset; - int loop_flag = 0; - int channel_count; - - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("psw",filename_extension(filename))) goto fail; - - /* check header */ - if (read_32bitBE(0x00,streamFile) != 0x52494646 && /* "RIFF" */ - read_32bitBE(0x08,streamFile) != 0x57415645 && /* "WAVE" */ - read_32bitBE(0x26,streamFile) != 0x64617461) /* "data" */ - goto fail; - - loop_flag = 0; - channel_count = read_16bitLE(0x16,streamFile); - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); - if (!vgmstream) goto fail; - - /* fill in the vital statistics */ - switch ((uint16_t)read_16bitBE(0x14,streamFile)) { - case 0xFFFF: - start_offset = 0x2E; - vgmstream->channels = channel_count; - vgmstream->sample_rate = read_16bitLE(0x1C,streamFile); - vgmstream->coding_type = coding_PSX; - vgmstream->num_samples = read_32bitLE(0x2A,streamFile)*28/16/channel_count; - if (loop_flag) { - vgmstream->loop_start_sample = loop_flag; - vgmstream->loop_end_sample = read_32bitLE(0x2A,streamFile)*28/16/channel_count; - } - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = 0x6400; - vgmstream->meta_type = meta_PS2_PSW; - - break; - case 0xFEFF: - start_offset = 0x2E; - vgmstream->channels = channel_count; - vgmstream->sample_rate = read_16bitLE(0x1C,streamFile); - vgmstream->coding_type = coding_NGC_DSP; - vgmstream->num_samples = read_32bitLE(0x2A,streamFile)*28/16/channel_count; - if (loop_flag) { - vgmstream->loop_start_sample = loop_flag; - vgmstream->loop_end_sample = read_32bitLE(0x2A,streamFile)*28/16/channel_count; - } - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = 0x12C00; - vgmstream->meta_type = meta_PS2_PSW; - - break; -default: - goto fail; -} - - /* open the file for reading */ - { - int i; - STREAMFILE * file; - file = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); - if (!file) goto fail; - for (i=0;ich[i].streamfile = file; - - vgmstream->ch[i].channel_start_offset= - vgmstream->ch[i].offset=start_offset+ - vgmstream->interleave_block_size*i; - - } - } - - return vgmstream; - - /* clean up anything we may have opened */ -fail: - if (vgmstream) close_vgmstream(vgmstream); - return NULL; -} diff --git a/src/meta/ps2_sl3.c b/src/meta/ps2_sl3.c index b145cc50..69fc81d3 100644 --- a/src/meta/ps2_sl3.c +++ b/src/meta/ps2_sl3.c @@ -1,65 +1,45 @@ #include "meta.h" -#include "../util.h" +#include "../coding/coding.h" -/* SL3 (from Test Drive Unlimited, Transformers) */ +/* SL3 - Atari Melbourne House games [ Test Drive Unlimited (PS2), Transformers (PS2)] */ VGMSTREAM * init_vgmstream_sl3(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; off_t start_offset; + int loop_flag = 0, channel_count; - int loop_flag = 0; - int channel_count; - - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("sl3",filename_extension(filename))) goto fail; - - /* check header */ + /* checks */ + /* .ms: actual extension, sl3: header id */ + if (!check_extensions(streamFile, "ms,sl3")) + goto fail; if (read_32bitBE(0x00,streamFile) != 0x534C3300) /* "SL3\0" */ goto fail; loop_flag = 0; channel_count = read_32bitLE(0x14,streamFile); - + start_offset = 0x8000; /* also at 0x24? */ + + /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; - /* fill in the vital statistics */ - start_offset = 0x8000; - vgmstream->channels = channel_count; vgmstream->sample_rate = read_32bitLE(0x18,streamFile); - vgmstream->coding_type = coding_PSX; - vgmstream->num_samples = (get_streamfile_size(streamFile)-0x8000)*28/16/channel_count; + vgmstream->num_samples = ps_bytes_to_samples(get_streamfile_size(streamFile)-start_offset,channel_count); if (loop_flag) { vgmstream->loop_start_sample = 0; vgmstream->loop_end_sample = read_32bitLE(0x1C,streamFile); } + vgmstream->coding_type = coding_PSX; vgmstream->layout_type = layout_interleave; vgmstream->interleave_block_size = read_32bitLE(0x20,streamFile); vgmstream->meta_type = meta_SL3; - /* open the file for reading */ - { - int i; - STREAMFILE * file; - file = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); - if (!file) goto fail; - for (i=0;ich[i].streamfile = file; - - vgmstream->ch[i].channel_start_offset= - vgmstream->ch[i].offset=start_offset+ - vgmstream->interleave_block_size*i; - - } - } - + if (!vgmstream_open_stream(vgmstream,streamFile,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/src/meta/ps2_tk5.c b/src/meta/ps2_tk5.c index bf7efc3a..f0df29e1 100644 --- a/src/meta/ps2_tk5.c +++ b/src/meta/ps2_tk5.c @@ -1,6 +1,5 @@ - #include "meta.h" -#include "../util.h" +#include "../coding/coding.h" /* TK5 (Tekken 5 Streams) */ VGMSTREAM * init_vgmstream_ps2_tk5(STREAMFILE *streamFile) { @@ -65,65 +64,46 @@ fail: return NULL; } -/* TK1 (Tekken 5 Streams from Tekken (NamCollection)) */ +/* OVB - Tekken 5 Streams from Tekken (NamCollection) */ VGMSTREAM * init_vgmstream_ps2_tk1(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; off_t start_offset; - int loop_flag = 0; - int channel_count; + int loop_flag = 0, channel_count; - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("tk1",filename_extension(filename))) goto fail; + /* checks */ + /* .ovb: actual extension, tk1: fake extension */ + if (!check_extensions(streamFile, "ovb,tk1")) + goto fail; - /* check header */ if (read_32bitBE(0x00,streamFile) != 0x544B3553) goto fail; loop_flag = (read_32bitLE(0x0C,streamFile)!=0); channel_count = 2; - + start_offset = 0x800; + /* NamCollection uses 44100 while Tekken 5 48000, no apparent way to tell them apart */ + /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; - /* fill in the vital statistics */ - start_offset = 0x800; - vgmstream->channels = channel_count; vgmstream->sample_rate = 44100; + vgmstream->num_samples = ps_bytes_to_samples(read_32bitLE(0x08,streamFile)*channel_count, channel_count); + if (vgmstream->loop_flag) { + vgmstream->loop_start_sample = read_32bitLE(0x08,streamFile)/16*28; + vgmstream->loop_end_sample = vgmstream->loop_start_sample + ps_bytes_to_samples(read_32bitLE(0x0c,streamFile)*channel_count, channel_count); + } + vgmstream->coding_type = coding_PSX_badflags; - vgmstream->num_samples = read_32bitLE(0x08,streamFile)/16*28; vgmstream->layout_type = layout_interleave; vgmstream->interleave_block_size = 0x10; vgmstream->meta_type = meta_PS2_TK1; - if (vgmstream->loop_flag) - { - vgmstream->loop_start_sample = read_32bitLE(0x08,streamFile)/16*28; - vgmstream->loop_end_sample = vgmstream->loop_start_sample + (read_32bitLE(0x0C,streamFile)/16*28); - } - - /* open the file for reading */ - { - int i; - STREAMFILE * file; - file = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); - if (!file) goto fail; - for (i=0;ich[i].streamfile = file; - - vgmstream->ch[i].channel_start_offset= - vgmstream->ch[i].offset=start_offset+ - vgmstream->interleave_block_size*i; - - } - } - + if (!vgmstream_open_stream(vgmstream,streamFile,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/src/meta/riff.c b/src/meta/riff.c index 9a9c9a6b..a7f489c8 100644 --- a/src/meta/riff.c +++ b/src/meta/riff.c @@ -90,7 +90,7 @@ typedef struct { int interleave; } riff_fmt_chunk; -static int read_fmt(int big_endian, STREAMFILE * streamFile, off_t current_chunk, riff_fmt_chunk * fmt, int sns, int mwv) { +static int read_fmt(int big_endian, STREAMFILE * streamFile, off_t current_chunk, riff_fmt_chunk * fmt, int mwv) { int32_t (*read_32bit)(off_t,STREAMFILE*) = big_endian ? read_32bitBE : read_32bitLE; int16_t (*read_16bit)(off_t,STREAMFILE*) = big_endian ? read_16bitBE : read_16bitLE; @@ -143,7 +143,7 @@ static int read_fmt(int big_endian, STREAMFILE * streamFile, off_t current_chunk fmt->coding_type = coding_AICA; break; - case 0x69: /* XBOX IMA ADPCM [Dynasty Warriors 5 (Xbox), Rayman Raving Rabbids 2 (PC) --maybe waa/wac/wam/wad?] */ + case 0x69: /* XBOX IMA ADPCM [Dynasty Warriors 5 (Xbox)] */ if (fmt->bps != 4) goto fail; fmt->coding_type = coding_XBOX_IMA; break; @@ -167,12 +167,6 @@ static int read_fmt(int big_endian, STREAMFILE * streamFile, off_t current_chunk fmt->interleave = 0x12; break; - case 0x5050: /* Ubisoft LyN engine's DSP (unofficial) */ - if (!sns) goto fail; - fmt->coding_type = coding_NGC_DSP; - fmt->interleave = 0x08; - break; - #ifdef VGM_USE_VORBIS case 0x6771: /* Ogg Vorbis (mode 3+) */ fmt->coding_type = coding_OGG_VORBIS; @@ -221,7 +215,7 @@ static int read_fmt(int big_endian, STREAMFILE * streamFile, off_t current_chunk #endif } - break; + goto fail; default: goto fail; @@ -253,7 +247,6 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { int mwv = 0; /* Level-5 .mwv (Dragon Quest VIII, Rogue Galaxy) */ off_t mwv_pflt_offset = -1; off_t mwv_ctrl_offset = -1; - int sns = 0; /* Ubisoft .sns LyN engine (Red Steel 2, Just Dance 3) */ int at3 = 0; /* Sony ATRAC3 / ATRAC3plus */ int at9 = 0; /* Sony ATRAC9 */ @@ -269,9 +262,6 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { else if ( check_extensions(streamFile, "mwv") ) { mwv = 1; } - else if ( check_extensions(streamFile, "sns") ) { - sns = 1; - } /* .rws: Climax games (Silent Hill Origins PSP, Oblivion PSP), .aud: EA Replay */ else if ( check_extensions(streamFile, "at3,rws,aud") ) { at3 = 1; @@ -329,7 +319,6 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { streamFile, current_chunk, &fmt, - sns, mwv)) goto fail; @@ -395,8 +384,8 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { case 0x66616374: /* "fact" */ if (chunk_size == 0x04) { /* standard, usually found with ADPCM */ fact_sample_count = read_32bitLE(current_chunk+0x08, streamFile); - } else if (sns && chunk_size == 0x10) { - fact_sample_count = read_32bitLE(current_chunk+0x08, streamFile); + } else if (chunk_size == 0x10 && read_32bitBE(current_chunk+0x08+0x04, streamFile) == 0x4C794E20) { /* "LyN " */ + goto fail; /* parsed elsewhere */ } else if ((at3 || at9) && chunk_size == 0x08) { fact_sample_count = read_32bitLE(current_chunk+0x08, streamFile); fact_sample_skip = read_32bitLE(current_chunk+0x0c, streamFile); @@ -441,6 +430,14 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { && (fmt.coding_type==coding_MSADPCM /*|| fmt.coding_type==coding_MS_IMA*/ || fmt.coding_type==coding_XBOX_IMA)) goto fail; + /* ignore Beyond Good & Evil HD PS3 evil reuse of PCM codec */ + if (fmt.coding_type == coding_PCM16LE && + read_32bitBE(start_offset+0x00, streamFile) == 0x4D534643 && /* "MSF\43" */ + read_32bitBE(start_offset+0x34, streamFile) == 0xFFFFFFFF && /* always */ + read_32bitBE(start_offset+0x38, streamFile) == 0xFFFFFFFF && + read_32bitBE(start_offset+0x3c, streamFile) == 0xFFFFFFFF) + goto fail; + #ifdef VGM_USE_VORBIS /* special case using init_vgmstream_ogg_vorbis */ @@ -515,29 +512,6 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { vgmstream->num_samples = fact_sample_count; /* some (converted?) Xbox games have bigger fact_samples */ break; - case coding_NGC_DSP: - if (!sns) goto fail; - if (fact_sample_count <= 0) goto fail; - vgmstream->num_samples = fact_sample_count; - //vgmstream->num_samples = dsp_bytes_to_samples(data_size, fmt.channel_count); - - /* coefs */ - { - int i, ch; - static const int16_t coef[16] = { /* common codebook? */ - 0x04ab,0xfced,0x0789,0xfedf,0x09a2,0xfae5,0x0c90,0xfac1, - 0x084d,0xfaa4,0x0982,0xfdf7,0x0af6,0xfafa,0x0be6,0xfbf5 - }; - - for (ch = 0; ch < fmt.channel_count; ch++) { - for (i = 0; i < 16; i++) { - vgmstream->ch[ch].adpcm_coef[i] = coef[i]; - } - } - } - - break; - #ifdef VGM_USE_FFMPEG case coding_FFmpeg: { ffmpeg_codec_data *ffmpeg_data = init_ffmpeg_offset(streamFile, 0x00, streamFile->get_size(streamFile)); @@ -649,9 +623,6 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { if (mwv) { vgmstream->meta_type = meta_RIFF_WAVE_MWV; } - if (sns) { - vgmstream->meta_type = meta_RIFF_WAVE_SNS; - } if ( !vgmstream_open_stream(vgmstream,streamFile,start_offset) ) @@ -714,7 +685,6 @@ VGMSTREAM * init_vgmstream_rifx(STREAMFILE *streamFile) { streamFile, current_chunk, &fmt, - 0, /* sns == false */ 0)) /* mwv == false */ goto fail; diff --git a/src/meta/ubi_jade.c b/src/meta/ubi_jade.c new file mode 100644 index 00000000..0feb6cc4 --- /dev/null +++ b/src/meta/ubi_jade.c @@ -0,0 +1,389 @@ +#include "meta.h" +#include "../coding/coding.h" + +static STREAMFILE* setup_jade_streamfile(STREAMFILE *streamFile, off_t subfile_offset, size_t subfile_size, const char* fake_ext); +static int get_loop_points(STREAMFILE *streamFile, int *out_loop_start, int *out_loop_end); + +/* Jade RIFF - from Ubisoft Jade engine games [Beyond Good & Evil (multi), Rayman Raving Rabbids 1/2 (multi)] */ +VGMSTREAM * init_vgmstream_ubi_jade(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + off_t start_offset, first_offset = 0xc; + off_t fmt_offset, data_offset; + size_t fmt_size, data_size; + int loop_flag, channel_count, sample_rate, codec, block_size; + int loop_start = 0, loop_end = 0; + int is_jade_v2 = 0; + + + /* checks */ + /* .waa: ambiances, .wam: music, .wac: sfx, .wad: dialogs (usually) + * .wav: Beyond Good & Evil HD (PS3), .psw: fake/badly extracted names [ex. Rayman Raving Rabbids (PS2)] */ + if (!check_extensions(streamFile,"waa,wac,wad,wam,wav,lwav,psw")) + goto fail; + + /* a slightly twisted RIFF with custom codecs */ + if (read_32bitBE(0x00,streamFile) != 0x52494646 || /* "RIFF" */ + read_32bitBE(0x08,streamFile) != 0x57415645) /* "WAVE" */ + goto fail; + + if (check_extensions(streamFile,"psw")) { /* .psw are incorrectly extracted missing 0x04 at the end */ + if (read_32bitLE(0x04,streamFile)+0x04 != get_streamfile_size(streamFile)) + goto fail; + } + else { + if (read_32bitLE(0x04,streamFile)+0x04+0x04 != get_streamfile_size(streamFile)) + goto fail; + } + + if (!find_chunk(streamFile, 0x666d7420,first_offset,0, &fmt_offset,&fmt_size, 0, 0)) /* "fmt " */ + goto fail; + if (!find_chunk(streamFile, 0x64617461,first_offset,0, &data_offset,&data_size, 0, 0)) /* "data" */ + goto fail; + + + /* parse format */ + { + if (fmt_size < 0x10) + goto fail; + codec = (uint16_t)read_16bitLE(fmt_offset+0x00,streamFile); + channel_count = read_16bitLE(fmt_offset+0x02,streamFile); + sample_rate = read_32bitLE(fmt_offset+0x04,streamFile); + block_size = (uint16_t)read_16bitLE(fmt_offset+0x0c,streamFile); + /* 0x08: average bytes, 0x0e: bps, etc */ + + /* autodetect Jade "v2", uses a different interleave [Rayman Raving Rabbids (PS2/Wii)] */ + switch(codec) { + case 0xFFFF: { /* PS2 */ + int i; + + /* half interleave check as there is no flag (ends with the PS-ADPCM stop frame) */ + for (i = 0; i < channel_count; i++) { + off_t end_frame = data_offset + (data_size / channel_count) * (i+1) - 0x10; + if (read_32bitBE(end_frame+0x00,streamFile) != 0x07007777 || + read_32bitBE(end_frame+0x04,streamFile) != 0x77777777 || + read_32bitBE(end_frame+0x08,streamFile) != 0x77777777 || + read_32bitBE(end_frame+0x0c,streamFile) != 0x77777777) { + is_jade_v2 = 1; + break; + } + } + break; + } + + case 0xFFFE: /* GC/Wii */ + is_jade_v2 = (read_16bitLE(fmt_offset+0x10,streamFile) == 0); /* extra data size (0x2e*channels) */ + break; + } + + /* hopefully catches PC Rabbids */ + if (find_chunk(streamFile, 0x63756520,first_offset,0, NULL,NULL, 0, 0)) { /* "cue " */ + is_jade_v2 = 1; + } + } + + + /* get loop points */ + if (is_jade_v2) { + loop_flag = get_loop_points(streamFile, &loop_start, &loop_end); /* loops in "LIST" */ + } + else { + /* BG&E files don't contain looping information, so the looping is done by extension. + * wam and waa contain ambient sounds and music, so often they contain looped music. + * Later, if the file is too short looping will be disabled. */ + loop_flag = check_extensions(streamFile,"waa,wam"); + } + + start_offset = data_offset; + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count,loop_flag); + if (!vgmstream) goto fail; + + vgmstream->sample_rate = sample_rate; + vgmstream->meta_type = meta_UBI_JADE; + if (is_jade_v2) { + vgmstream->loop_start_sample = loop_start; + vgmstream->loop_end_sample = loop_end; + } + + switch(codec) { + + case 0x0069: /* Xbox */ + if (block_size != 0x24*channel_count) + goto fail; + vgmstream->coding_type = coding_XBOX_IMA; + vgmstream->layout_type = layout_none; + + vgmstream->num_samples = xbox_ima_bytes_to_samples(data_size, channel_count); + if (!is_jade_v2) { + vgmstream->loop_start_sample = 0; + vgmstream->loop_end_sample = vgmstream->num_samples; + } + + break; + + case 0xFFFF: /* PS2 */ + if (block_size != 0x10) + goto fail; + vgmstream->coding_type = coding_PSX; + vgmstream->layout_type = layout_interleave; + + if (is_jade_v2) { + vgmstream->interleave_block_size = 0x6400; + if (vgmstream->interleave_block_size) + vgmstream->interleave_last_block_size = (data_size % (vgmstream->interleave_block_size*vgmstream->channels)) / vgmstream->channels; + } + else { + vgmstream->interleave_block_size = data_size / channel_count; + } + + vgmstream->num_samples = ps_bytes_to_samples(data_size, channel_count); + if (!is_jade_v2) { + vgmstream->loop_start_sample = 0; + vgmstream->loop_end_sample = vgmstream->num_samples; + } + + break; + + case 0xFFFE: /* GC/Wii */ + if (block_size != 0x08) + goto fail; + vgmstream->coding_type = coding_NGC_DSP; + vgmstream->layout_type = layout_interleave; + + vgmstream->num_samples = dsp_bytes_to_samples(data_size, channel_count); + if (!is_jade_v2) { + vgmstream->loop_start_sample = 0; + vgmstream->loop_end_sample = vgmstream->num_samples; + } + + /* coefs / interleave */ + if (is_jade_v2) { + vgmstream->interleave_block_size = 0x6400; + if (vgmstream->interleave_block_size) + vgmstream->interleave_last_block_size = ((data_size % (vgmstream->interleave_block_size*vgmstream->channels))/2+7)/8*8; + + { + static const int16_t coef[16] = { /* default Ubisoft coefs, from ELF */ + 0x04ab,0xfced,0x0789,0xfedf,0x09a2,0xfae5,0x0c90,0xfac1, + 0x084d,0xfaa4,0x0982,0xfdf7,0x0af6,0xfafa,0x0be6,0xfbf5 + }; + int i, ch; + + for (ch = 0; ch < channel_count; ch++) { + for (i = 0; i < 16; i++) { + vgmstream->ch[ch].adpcm_coef[i] = coef[i]; + } + } + } + } + else { + /* has extra 0x2e coefs before each channel, not counted in data_size */ + vgmstream->interleave_block_size = (data_size + 0x2e*channel_count) / channel_count; + + dsp_read_coefs_be(vgmstream, streamFile, start_offset+0x00, vgmstream->interleave_block_size); + dsp_read_hist_be (vgmstream, streamFile, start_offset+0x20, vgmstream->interleave_block_size); + start_offset += 0x2e; + } + break; + + case 0x0002: /* PC */ + if (block_size != 0x24*channel_count) + goto fail; + vgmstream->coding_type = coding_MSADPCM; + vgmstream->layout_type = layout_none; + vgmstream->interleave_block_size = 0x24*channel_count; + + vgmstream->num_samples = msadpcm_bytes_to_samples(data_size, vgmstream->interleave_block_size, channel_count); + if (!is_jade_v2) { + vgmstream->loop_start_sample = 0; + vgmstream->loop_end_sample = vgmstream->num_samples; + } + + break; + + case 0x0001: { /* PS3 */ + VGMSTREAM *temp_vgmstream = NULL; + STREAMFILE *temp_streamFile = NULL; + + if (block_size != 0x02*channel_count) + goto fail; + + /* a MSF (usually ATRAC3) masquerading as PCM */ + if (read_32bitBE(start_offset, streamFile) != 0x4D534643) /* "MSF\43" */ + goto fail; + + temp_streamFile = setup_jade_streamfile(streamFile, start_offset, data_size, "msf"); + if (!temp_streamFile) goto fail; + + temp_vgmstream = init_vgmstream_ps3_msf(temp_streamFile); + close_streamfile(temp_streamFile); + if (!temp_vgmstream) goto fail; + + temp_vgmstream->meta_type = vgmstream->meta_type; + close_vgmstream(vgmstream); + return temp_vgmstream; + } + + default: /* X360 uses .XMA */ + goto fail; + } + + /* V1 loops by extension, try to detect incorrectly looped jingles (too short) */ + if (!is_jade_v2) { + if(loop_flag + && vgmstream->num_samples < 15*sample_rate) { /* in seconds */ + vgmstream->loop_flag = 0; + } + } + + + if ( !vgmstream_open_stream(vgmstream,streamFile,start_offset) ) + goto fail; + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} + +/* extract loops from "cue /LIST", returns if loops (info from Droolie) */ +static int get_loop_points(STREAMFILE *streamFile, int *out_loop_start, int *out_loop_end) { + off_t cue_offset, list_offset; + size_t cue_size, list_size; + off_t offset, first_offset = 0x0c; + int i, cue_count, loop_id = 0, loop_start = 0, loop_end = 0; + + + /* unlooped files may contain LIST, but also may not */ + if (!find_chunk(streamFile, 0x63756520,first_offset,0, &cue_offset,&cue_size, 0, 0)) /* "cue " */ + goto fail; + if (!find_chunk(streamFile, 0x4C495354,first_offset,0, &list_offset,&list_size, 0, 0)) /* "LIST" */ + goto fail; + + offset = list_offset + 0x04; + while (offset < list_offset + list_size) { + uint32_t chunk_id = read_32bitBE(offset+0x00, streamFile); + uint32_t chunk_size = read_32bitLE(offset+0x04, streamFile); + offset += 0x08; + + switch(chunk_id) { + case 0x6C61626C: /* "labl" */ + if (read_32bitBE(offset+0x04, streamFile) == 0x6C6F6F70) /* "loop", actually an string tho */ + loop_id = read_32bitLE(offset+0x00, streamFile); + chunk_size += (chunk_size % 2) ? 1 : 0; /* string is even-padded after size */ + break; + case 0x6C747874: /* "ltxt" */ + if (loop_id == read_32bitLE(offset+0x00, streamFile)) + loop_end = read_32bitLE(offset+0x04, streamFile); + break; + + default: + VGM_LOG("Jade: unknown LIST chunk at %lx\n", offset); + goto fail; + } + + offset += chunk_size; + } + + if (!loop_end) + return 0; + + cue_count = read_32bitLE(cue_offset+0x00, streamFile); + for (i = 0; i < cue_count; i++) { + if (loop_id == read_32bitLE(cue_offset+0x04 + i*0x18 + 0x00, streamFile)) { + loop_start = read_32bitLE(cue_offset+0x04 + i*0x18 + 0x04, streamFile); + loop_end += loop_start; + break; + } + } + + *out_loop_start = loop_start; + *out_loop_end = loop_end; + return 1; + +fail: + return 0; +} + + +/* Jade RIFF in containers */ +VGMSTREAM * init_vgmstream_ubi_jade_container(STREAMFILE *streamFile) { + VGMSTREAM *vgmstream = NULL; + STREAMFILE *temp_streamFile = NULL; + off_t subfile_offset; + size_t subfile_size; + + /* Jade packs files in bigfiles, and once extracted the sound files have extra engine data before + * the RIFF + padding after. Most extractors don't remove the padding correctly, so here we add support. */ + + /* checks */ + /* standard Jade exts + .xma for padded XMA used in Beyond Good & Evil HD (X360) */ + if (!check_extensions(streamFile,"waa,wac,wad,wam,wav,lwav,xma")) + goto fail; + + if (read_32bitBE(0x04,streamFile) == 0x52494646 && + read_32bitLE(0x00,streamFile)+0x04 == get_streamfile_size(streamFile)) { + /* data size + RIFF + padding */ + subfile_offset = 0x04; + } + else if (read_32bitBE(0x00,streamFile) == 0x52494646 && + read_32bitLE(0x04,streamFile)+0x04+0x04 < get_streamfile_size(streamFile) && + (get_streamfile_size(streamFile) + 0x04) % 0x800 == 0) { + /* RIFF + padding with data size removed (bad extraction) */ + subfile_offset = 0x00; + } + else if (read_32bitBE(0x04,streamFile) == 0x52494646 && + read_32bitLE(0x00,streamFile) == get_streamfile_size(streamFile)) { + /* data_size + RIFF + padding - 0x04 (bad extraction) */ + subfile_offset = 0x04; + } + else { + goto fail; + } + + subfile_size = read_32bitLE(subfile_offset+0x04,streamFile) + 0x04+0x04; + + temp_streamFile = setup_jade_streamfile(streamFile, subfile_offset,subfile_size, NULL); + if (!temp_streamFile) goto fail; + + if (check_extensions(streamFile,"xma")) { + vgmstream = init_vgmstream_xma(temp_streamFile); + } else { + vgmstream = init_vgmstream_ubi_jade(temp_streamFile); + } + + close_streamfile(temp_streamFile); + return vgmstream; + +fail: + close_streamfile(temp_streamFile); + close_vgmstream(vgmstream); + return NULL; +} + +static STREAMFILE* setup_jade_streamfile(STREAMFILE *streamFile, off_t subfile_offset, size_t subfile_size, const char* fake_ext) { + STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL; + + /* setup subfile */ + new_streamFile = open_wrap_streamfile(streamFile); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + new_streamFile = open_clamp_streamfile(temp_streamFile, subfile_offset,subfile_size); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + if (fake_ext) { + new_streamFile = open_fakename_streamfile(temp_streamFile, NULL,fake_ext); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + } + + return temp_streamFile; + +fail: + close_streamfile(temp_streamFile); + return NULL; +} diff --git a/src/meta/ubi_lyn.c b/src/meta/ubi_lyn.c new file mode 100644 index 00000000..77c714a1 --- /dev/null +++ b/src/meta/ubi_lyn.c @@ -0,0 +1,293 @@ +#include "meta.h" +#include "../layout/layout.h" +#include "../coding/coding.h" +#include "ubi_lyn_ogg_streamfile.h" + +static STREAMFILE* setup_lyn_streamfile(STREAMFILE *streamFile, off_t subfile_offset, size_t subfile_size); + +/* LyN RIFF - from Ubisoft LyN engine games [Red Steel 2 (Wii), Adventures of Tintin (Multi), From Dust (Multi), Just Dance 3/4 (multi)] */ +VGMSTREAM * init_vgmstream_ubi_lyn(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + off_t start_offset, first_offset = 0xc; + off_t fmt_offset, data_offset, fact_offset; + size_t fmt_size, data_size, fact_size; + int loop_flag, channel_count, sample_rate, codec; + int num_samples; + + + /* checks */ + /* .sns: Red Steel 2, .wav: Tintin, .son: From Dust */ + if (!check_extensions(streamFile,"sns,wav,lwav,son")) + goto fail; + + /* a slightly eccentric RIFF with custom codecs */ + if (read_32bitBE(0x00,streamFile) != 0x52494646 || /* "RIFF" */ + read_32bitBE(0x08,streamFile) != 0x57415645) /* "WAVE" */ + goto fail; + if (read_32bitLE(0x04,streamFile)+0x04+0x04 != get_streamfile_size(streamFile)) + goto fail; + + if (!find_chunk(streamFile, 0x666d7420,first_offset,0, &fmt_offset,&fmt_size, 0, 0)) /* "fmt " */ + goto fail; + if (!find_chunk(streamFile, 0x64617461,first_offset,0, &data_offset,&data_size, 0, 0)) /* "data" */ + goto fail; + + /* always found, even with PCM (LyN subchunk seems to contain the engine version, ex. 0x0d/10) */ + if (!find_chunk(streamFile, 0x66616374,first_offset,0, &fact_offset,&fact_size, 0, 0)) /* "fact" */ + goto fail; + if (fact_size != 0x10 || read_32bitBE(fact_offset+0x04, streamFile) != 0x4C794E20) /* "LyN " */ + goto fail; + num_samples = read_32bitLE(fact_offset+0x00, streamFile); + /* sometimes there is a LySE chunk */ + + + /* parse format */ + { + if (fmt_size < 0x12) + goto fail; + codec = (uint16_t)read_16bitLE(fmt_offset+0x00,streamFile); + channel_count = read_16bitLE(fmt_offset+0x02,streamFile); + sample_rate = read_32bitLE(fmt_offset+0x04,streamFile); + /* 0x08: average bytes, 0x0c: block align, 0x0e: bps, etc */ + + /* fake WAVEFORMATEX, used with > 2ch */ + if (codec == 0xFFFE) { + if (fmt_size < 0x28) + goto fail; + /* fake GUID with first value doubling as codec */ + codec = read_32bitLE(fmt_offset+0x18,streamFile); + if (read_32bitBE(fmt_offset+0x1c,streamFile) != 0x00001000 && + read_32bitBE(fmt_offset+0x20,streamFile) != 0x800000AA && + read_32bitBE(fmt_offset+0x24,streamFile) != 0x00389B71) { + goto fail; + } + } + } + + /* most songs simply repeat, loop if it looks long enough */ + loop_flag = (num_samples > 20*sample_rate); /* in seconds */ + start_offset = data_offset; + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count,loop_flag); + if (!vgmstream) goto fail; + + vgmstream->sample_rate = sample_rate; + vgmstream->meta_type = meta_UBI_LYN; + vgmstream->num_samples = num_samples; + vgmstream->loop_start_sample = 0; + vgmstream->loop_end_sample = vgmstream->num_samples; + + switch(codec) { + case 0x0001: /* PCM */ + vgmstream->coding_type = coding_PCM16LE; /* LE even in X360 */ + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x02; + break; + + case 0x5050: /* DSP (Wii) */ + vgmstream->coding_type = coding_NGC_DSP; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x08; + + /* setup default Ubisoft coefs */ + { + static const int16_t coef[16] = { + 0x04ab,0xfced,0x0789,0xfedf,0x09a2,0xfae5,0x0c90,0xfac1, + 0x084d,0xfaa4,0x0982,0xfdf7,0x0af6,0xfafa,0x0be6,0xfbf5 + }; + int i, ch; + + for (ch = 0; ch < channel_count; ch++) { + for (i = 0; i < 16; i++) { + vgmstream->ch[ch].adpcm_coef[i] = coef[i]; + } + } + } + + break; + +#ifdef VGM_USE_VORBIS + case 0x3157: { /* Ogg (PC), interleaved 1ch */ + size_t interleave_size, stride_size; + layered_layout_data* data = NULL; + int i; + + if (read_32bitLE(start_offset+0x00,streamFile) != 1) /* id? */ + goto fail; + + interleave_size = read_32bitLE(start_offset+0x04,streamFile); + stride_size = interleave_size * channel_count; + /* interleave is adjusted so there is no smaller last block, it seems */ + + vgmstream->coding_type = coding_OGG_VORBIS; + vgmstream->layout_type = layout_layered; + + /* init layout */ + data = init_layout_layered(channel_count); + if (!data) goto fail; + vgmstream->layout_data = data; + + /* open each layer subfile */ + for (i = 0; i < channel_count; i++) { + STREAMFILE* temp_streamFile = NULL; + size_t total_size = read_32bitLE(start_offset+0x08 + 0x04*i,streamFile); + off_t layer_offset = start_offset+0x08 + 0x04*channel_count + interleave_size*i; + + temp_streamFile = setup_lyn_ogg_streamfile(streamFile, layer_offset, interleave_size, stride_size, total_size); + if (!temp_streamFile) goto fail; + + data->layers[i] = init_vgmstream_ogg_vorbis(temp_streamFile); + close_streamfile(temp_streamFile); + if (!data->layers[i]) goto fail; + + /* could validate between layers, meh */ + } + + /* setup layered VGMSTREAMs */ + if (!setup_layout_layered(data)) + goto fail; + + break; + } +#endif + +#ifdef VGM_USE_MPEG + case 0x5051: { /* MPEG (PS3/PC), interleaved 1ch */ + mpeg_codec_data *mpeg_data = NULL; + mpeg_custom_config cfg = {0}; + int i; + + if (read_32bitLE(start_offset+0x00,streamFile) != 2) /* id? */ + goto fail; + + cfg.interleave = read_32bitLE(start_offset+0x04,streamFile); + cfg.chunk_size = read_32bitLE(start_offset+0x08,streamFile); + /* 0x08: frame size, 0x0c: frame per interleave, 0x10: samples per frame */ + + /* skip seek tables and find actual start */ + start_offset += 0x14; + data_size -= 0x14; + for (i = 0; i < channel_count; i++) { + int entries = read_32bitLE(start_offset,streamFile); + + start_offset += 0x04 + entries*0x08; + data_size -= 0x04 + entries*0x08; + } + + cfg.data_size = data_size; + + //todo data parsing looks correct but some files decode a bit wrong at the end (ex. Tintin: Music~Boss~Allan~Victory~02) + mpeg_data = init_mpeg_custom(streamFile, start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_LYN, &cfg); + if (!mpeg_data) goto fail; + vgmstream->codec_data = mpeg_data; + vgmstream->layout_type = layout_none; + + break; + } +#endif + +#ifdef VGM_USE_FFMPEG + case 0x0166: { /* XMA (X360), standard */ + uint8_t buf[0x100]; + int bytes; + off_t chunk_offset; + size_t chunk_size, seek_size; + + if (read_32bitLE(start_offset+0x00,streamFile) != 3) /* id? */ + goto fail; + + /* skip standard XMA header + seek table */ + chunk_offset = start_offset + 0x04 + 0x04; + chunk_size = read_32bitLE(start_offset + 0x04, streamFile); + seek_size = read_32bitLE(chunk_offset+chunk_size, streamFile); + start_offset += (0x04 + 0x04 + chunk_size + 0x04 + seek_size); + data_size -= (0x04 + 0x04 + chunk_size + 0x04 + seek_size); + + bytes = ffmpeg_make_riff_xma_from_fmt_chunk(buf,0x100, chunk_offset,chunk_size, data_size, streamFile, 1); + 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; + + break; + } +#endif + + default: + goto fail; + } + + + if ( !vgmstream_open_stream(vgmstream,streamFile,start_offset) ) + goto fail; + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} + + +/* LyN RIFF in containers */ +VGMSTREAM * init_vgmstream_ubi_lyn_container(STREAMFILE *streamFile) { + VGMSTREAM *vgmstream = NULL; + STREAMFILE *temp_streamFile = NULL; + off_t subfile_offset; + size_t subfile_size; + + /* LyN packs files in bigfiles, and once extracted the sound files have extra engine + * data before the RIFF. Might as well support them in case the RIFF wasn't extracted. */ + + /* checks */ + if (!check_extensions(streamFile,"sns,wav,lwav,son")) + goto fail; + + /* find "RIFF" position */ + if (read_32bitBE(0x00,streamFile) == 0x4C795345 && /* "LySE" */ + read_32bitBE(0x14,streamFile) == 0x52494646) { /* "RIFF" */ + subfile_offset = 0x14; /* Adventures of Tintin */ + } + else if (read_32bitLE(0x00,streamFile)+0x20 == get_streamfile_size(streamFile) && + read_32bitBE(0x20,streamFile) == 0x52494646) { /* "RIFF" */ + subfile_offset = 0x20; /* Red Steel 2, From Dust */ + } + else { + goto fail; + } + + subfile_size = read_32bitLE(subfile_offset+0x04,streamFile) + 0x04+0x04; + + temp_streamFile = setup_lyn_streamfile(streamFile, subfile_offset,subfile_size); + if (!temp_streamFile) goto fail; + + vgmstream = init_vgmstream_ubi_lyn(temp_streamFile); + + close_streamfile(temp_streamFile); + return vgmstream; + +fail: + close_streamfile(temp_streamFile); + close_vgmstream(vgmstream); + return NULL; +} + +static STREAMFILE* setup_lyn_streamfile(STREAMFILE *streamFile, off_t subfile_offset, size_t subfile_size) { + STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL; + + /* setup subfile */ + new_streamFile = open_wrap_streamfile(streamFile); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + new_streamFile = open_clamp_streamfile(temp_streamFile, subfile_offset,subfile_size); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + return temp_streamFile; + +fail: + close_streamfile(temp_streamFile); + return NULL; +} diff --git a/src/meta/ubi_lyn_ogg_streamfile.h b/src/meta/ubi_lyn_ogg_streamfile.h new file mode 100644 index 00000000..aeec84f8 --- /dev/null +++ b/src/meta/ubi_lyn_ogg_streamfile.h @@ -0,0 +1,92 @@ +#ifndef _LYN_OGG_STREAMFILE_H_ +#define _LYN_OGG_STREAMFILE_H_ +#include "../streamfile.h" + + +typedef struct { + off_t start_physical_offset; /* interleaved data start, for this substream */ + size_t interleave_block_size; /* max size that can be read before encountering other substreams */ + size_t stride_size; /* step size between interleave blocks (interleave*channels) */ + size_t total_size; /* final size of the deinterleaved substream */ +} lyn_ogg_io_data; + + +/* Handles deinterleaving of complete files, skipping portions or other substreams. */ +static size_t scd_dsp_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length, lyn_ogg_io_data* data) { + size_t total_read = 0; + + while (length > 0) { + size_t to_read; + size_t length_available; + off_t block_num; + off_t intrablock_offset; + off_t physical_offset; + + block_num = offset / data->interleave_block_size; + intrablock_offset = offset % data->interleave_block_size; + physical_offset = data->start_physical_offset + block_num*data->stride_size + intrablock_offset; + length_available = data->interleave_block_size - intrablock_offset; + + if (length < length_available) { + to_read = length; + } + else { + to_read = length_available; + } + + if (to_read > 0) { + size_t bytes_read; + + bytes_read = read_streamfile(dest, physical_offset, to_read, streamfile); + total_read += bytes_read; + + if (bytes_read != to_read) { + return total_read; + } + + dest += bytes_read; + offset += bytes_read; + length -= bytes_read; + } + } + + return total_read; +} + +static size_t scd_dsp_io_size(STREAMFILE *streamfile, lyn_ogg_io_data* data) { + return data->total_size; +} + + +static STREAMFILE* setup_lyn_ogg_streamfile(STREAMFILE *streamFile, off_t start_offset, size_t interleave_block_size, size_t stride_size, size_t total_size) { + STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL; + lyn_ogg_io_data io_data = {0}; + size_t io_data_size = sizeof(lyn_ogg_io_data); + + io_data.start_physical_offset = start_offset; + io_data.interleave_block_size = interleave_block_size; + io_data.stride_size = stride_size; + io_data.total_size = total_size; + + + /* setup subfile */ + new_streamFile = open_wrap_streamfile(streamFile); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + new_streamFile = open_io_streamfile(temp_streamFile, &io_data,io_data_size, scd_dsp_io_read,scd_dsp_io_size); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + new_streamFile = open_fakename_streamfile(temp_streamFile, NULL,"ogg"); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + return temp_streamFile; + +fail: + close_streamfile(temp_streamFile); + return NULL; +} + +#endif /* _LYN_OGG_STREAMFILE_H_ */ diff --git a/src/meta/waa_wac_wad_wam.c b/src/meta/waa_wac_wad_wam.c deleted file mode 100644 index 0b7b50d4..00000000 --- a/src/meta/waa_wac_wad_wam.c +++ /dev/null @@ -1,182 +0,0 @@ -#include "meta.h" -#include "../coding/coding.h" - -/* -const short wad_coef[16][2] = -{ - {0x4002,0x2003}, - {0x2016,0xc600}, - {0xC600,0x98ab}, - {0x96bf,0x29c5}, - {0x2003,0x0081}, - {0x0e00,0x2004}, - {0x8e01,0xc500}, - {0x70bf,0x8128}, - {0x288e,0xc600}, - {0x016e,0x0e5b}, - {0xbe20,0x2003}, - {0x03c6,0xc600}, - {0x0048,0xe85a}, - {0xbe28,0x28c6}, - {0xc600,0x00F6}, - {0xbeab,0x5520} -};*/ -const short wad_coef[16] = -{ - 0x04ab, 0xfced, - 0x0789, 0xfedf, - 0x09a2, 0xfae5, - 0x0c90, 0xfac1, - 0x084d, 0xfaa4, - 0x0982, 0xfdf7, - 0x0af6, 0xfafa, - 0x0be6, 0xfbf5 -}; - - -/* WAC/WAD/WAM/WAA - from Beyond Good & Evil (PS2/Xbox/GC/Wii) */ -VGMSTREAM * init_vgmstream_waa_wac_wad_wam(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; - int i; - off_t start_offset; - int loop_flag; - int channel_count; - int coef1_start; - int coef2_start; - int second_channel_start = -1; - - // Check file extensions - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("waa",filename_extension(filename)) && - strcasecmp("wac",filename_extension(filename)) && - strcasecmp("wad",filename_extension(filename)) && - strcasecmp("wam",filename_extension(filename))) goto fail; - - /* check header */ - if (read_32bitBE(0x00,streamFile) != 0x52494646 || /* "RIFF" */ - read_32bitBE(0x08,streamFile) != 0x57415645 || /* "WAVE" */ - read_32bitBE(0x0C,streamFile) != 0x666D7420 || /* "fmt " */ - read_32bitBE(0x10,streamFile) != 0x12000000) /* "0x12000000" */ - goto fail; - - /* files don't contain looping information, - so the looping is not done depending on extension. - wam and waa contain ambient sounds and music, so often they contain - looped music. Change extension to wac or wad to make the sound non-looping. - */ - loop_flag = strcasecmp("wac",filename_extension(filename)) && - strcasecmp("wad",filename_extension(filename)); - channel_count = (uint16_t)read_16bitLE(0x16,streamFile); - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); - if (!vgmstream) goto fail; - - /* Check what encoder is needed */ - //FIXME: //PC version uses pcm, but which encoder? - - vgmstream->channels = channel_count; - vgmstream->sample_rate = read_32bitLE(0x18,streamFile); - vgmstream->meta_type = meta_WAA_WAC_WAD_WAM; - vgmstream->layout_type = layout_none; - - switch((uint16_t)read_16bitLE(0x14,streamFile)) { - case 0x0069: // XBOX IMA ADPCM - start_offset = 0x2E; - vgmstream->coding_type = coding_XBOX_IMA; - vgmstream->num_samples = xbox_ima_bytes_to_samples(read_32bitLE(0x2A,streamFile), channel_count); - if (loop_flag) { - vgmstream->loop_start_sample = 0; - vgmstream->loop_end_sample = xbox_ima_bytes_to_samples(read_32bitLE(0x2A,streamFile),channel_count); - } - break; - case 0xFFFF: // PS2 ADPCM - start_offset = 0x2E; - vgmstream->coding_type = coding_PSX; - vgmstream->num_samples = (read_32bitLE(0x2A,streamFile))/16*28/channel_count; - if (loop_flag) { - vgmstream->loop_start_sample = 0; - vgmstream->loop_end_sample = (read_32bitLE(0x2A,streamFile))/16*28/channel_count; - } - second_channel_start = (read_32bitLE(0x2A,streamFile)/2)+start_offset; - break; - case 0xFFFE: // GameCube/WII DSP - start_offset = 0x5C; - vgmstream->coding_type = coding_NGC_DSP; - vgmstream->num_samples = (read_32bitLE(0x2A,streamFile))*14/8/channel_count; - if (loop_flag) { - vgmstream->loop_start_sample = 0; - vgmstream->loop_end_sample = (read_32bitLE(0x2A,streamFile))*14/8/channel_count; - } - if(read_16bitLE(0x24,streamFile)==0x00)//is a wii file with no coeff table - { - //FIXME: WII version of WAM/WAD/WAC need some coeff table from somewhere - for (i=0;i<16;i++) - vgmstream->ch[0].adpcm_coef[i] = wad_coef[i]; - if (channel_count == 2) { - for (i=0;i<16;i++) - vgmstream->ch[1].adpcm_coef[i] = wad_coef[i]; - } - goto fail; - } - else - { - second_channel_start = (read_32bitLE(0x2A,streamFile)/2)+0x8A; - /* Retrieveing the coef tables */ - coef1_start = 0x2E; - coef2_start = (read_32bitLE(0x2A,streamFile)/2)+0x5C; - - { - int i; - for (i=0;i<16;i++) - vgmstream->ch[0].adpcm_coef[i] = read_16bitBE(coef1_start+i*2,streamFile); - if (channel_count == 2) { - for (i=0;i<16;i++) - vgmstream->ch[1].adpcm_coef[i] = read_16bitBE(coef2_start+i*2,streamFile); - } - } - } - break; - default: - goto fail; - } - - - - - /* open the file for reading */ - { - int i; - STREAMFILE * file; - file = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); - if (!file) goto fail; - for (i=0;ich[i].streamfile = file; - - if (vgmstream->coding_type == coding_XBOX_IMA) { - /* xbox interleaving is a little odd */ - vgmstream->ch[i].channel_start_offset=start_offset; - } else { - vgmstream->ch[0].channel_start_offset=start_offset; - if (channel_count == 2) { - if (second_channel_start == -1) goto fail; - vgmstream->ch[1].channel_start_offset=second_channel_start; - } - } - vgmstream->ch[i].offset = vgmstream->ch[i].channel_start_offset; - } - } - - - return vgmstream; - -fail: - /* clean up anything we may have opened */ - if (vgmstream) close_vgmstream(vgmstream); - return NULL; -} - - - - diff --git a/src/meta/wwise.c b/src/meta/wwise.c index 04e720d2..53d1e3e3 100644 --- a/src/meta/wwise.c +++ b/src/meta/wwise.c @@ -78,6 +78,18 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) { } #endif + /* ignore LyN RIFF */ + { + off_t fact_offset; + size_t fact_size; + + if (find_chunk(streamFile, 0x66616374,first_offset,0, &fact_offset,&fact_size, 0, 0)) { /* "fact" */ + if (fact_size == 0x10 && read_32bitBE(fact_offset+0x04, streamFile) == 0x4C794E20) /* "LyN " */ + goto fail; /* parsed elsewhere */ + /* Wwise doesn't use "fact", though */ + } + } + /* parse format (roughly spec-compliant but some massaging is needed) */ { diff --git a/src/vgmstream.c b/src/vgmstream.c index 3af15724..57db021d 100644 --- a/src/vgmstream.c +++ b/src/vgmstream.c @@ -119,7 +119,6 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_scd_pcm, init_vgmstream_ps2_pcm, init_vgmstream_ps2_rkv, - init_vgmstream_ps2_psw, init_vgmstream_ps2_vas, init_vgmstream_ps2_tec, init_vgmstream_ps2_enth, @@ -178,7 +177,8 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_bgw, init_vgmstream_spw, init_vgmstream_ps2_ass, - init_vgmstream_waa_wac_wad_wam, + init_vgmstream_ubi_jade, + init_vgmstream_ubi_jade_container, init_vgmstream_seg, init_vgmstream_nds_strm_ffta2, init_vgmstream_str_asr, @@ -394,6 +394,9 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_sthd, init_vgmstream_pcm_sre, init_vgmstream_dsp_mcadpcm, + init_vgmstream_ubi_lyn, + init_vgmstream_ubi_lyn_container, + init_vgmstream_msb_msh, init_vgmstream_txth, /* should go at the end (lower priority) */ #ifdef VGM_USE_FFMPEG @@ -2386,11 +2389,6 @@ static STREAMFILE * get_vgmstream_average_bitrate_channel_streamfile(VGMSTREAM * { //AAX, AIX? - if (vgmstream->layout_type==layout_layered) { - layered_layout_data *data = (layered_layout_data *) vgmstream->layout_data; - return data->layers[channel]->ch[0].streamfile; - } - if (vgmstream->coding_type==coding_NWA) { nwa_codec_data *data = (nwa_codec_data *) vgmstream->codec_data; if (data && data->nwa) @@ -2461,6 +2459,11 @@ int get_vgmstream_average_bitrate(VGMSTREAM * vgmstream) { segmented_layout_data *data = (segmented_layout_data *) vgmstream->layout_data; return get_vgmstream_average_bitrate(data->segments[0]); } + if (vgmstream->layout_type==layout_layered) { + layered_layout_data *data = (layered_layout_data *) vgmstream->layout_data; + return get_vgmstream_average_bitrate(data->layers[0]); + } + channels = get_vgmstream_average_bitrate_channel_count(vgmstream); if (!channels) return 0; diff --git a/src/vgmstream.h b/src/vgmstream.h index b73a692b..7a9b1510 100644 --- a/src/vgmstream.h +++ b/src/vgmstream.h @@ -373,7 +373,6 @@ typedef enum { meta_SCD_PCM, /* Lunar - Eternal Blue */ meta_PS2_PCM, /* Konami KCEJ East: Ephemeral Fantasia, Yu-Gi-Oh! The Duelists of the Roses, 7 Blades */ meta_PS2_RKV, /* Legacy of Kain - Blood Omen 2 (PS2) */ - meta_PS2_PSW, /* Rayman Raving Rabbids */ meta_PS2_VAS, /* Pro Baseball Spirits 5 */ meta_PS2_TEC, /* TECMO badflagged stream */ meta_PS2_ENTH, /* Enthusia */ @@ -395,13 +394,11 @@ typedef enum { meta_GSP_GSB, /* Tecmo games (Super Swing Golf 1 & 2, Quamtum Theory) */ meta_YDSP, /* WWE Day of Reckoning */ meta_FFCC_STR, /* Final Fantasy: Crystal Chronicles */ - - meta_WAA_WAC_WAD_WAM, /* Beyond Good & Evil */ + meta_UBI_JADE, /* Beyond Good & Evil, Rayman Raving Rabbids */ meta_GCA, /* Metal Slug Anthology */ meta_MSVP, /* Popcap Hits */ meta_NGC_SSM, /* Golden Gashbell Full Power */ meta_PS2_JOE, /* Wall-E / Pixar games */ - meta_NGC_YMF, /* WWE WrestleMania X8 */ meta_SADL, /* .sad */ meta_PS2_CCC, /* Tokyo Xtreme Racer DRIFT 2 */ @@ -479,7 +476,6 @@ typedef enum { meta_RIFF_WAVE_smpl, /* RIFF w/ loop data in smpl chunk */ meta_RIFF_WAVE_wsmp, /* RIFF w/ loop data in wsmp chunk */ meta_RIFF_WAVE_MWV, /* .mwv RIFF w/ loop data in ctrl chunk pflt */ - meta_RIFF_WAVE_SNS, /* .sns RIFF */ meta_RIFX_WAVE, /* RIFX, for big-endian WAVs */ meta_RIFX_WAVE_smpl, /* RIFX w/ loop data in smpl chunk */ meta_XNB, /* XNA Game Studio 4.0 */ @@ -668,6 +664,9 @@ typedef enum { meta_MP4, /* MP4/AAC */ meta_PCM_SRE, /* .PCM+SRE [Viewtiful Joe (PS2)] */ meta_DSP_MCADPCM, /* Skyrim (Switch) */ + meta_UBI_LYN, /* Ubisoft LyN engine [The Adventures of Tintin (multi)] */ + meta_MSB_MSH, /* sfx companion of MIH+MIB */ + meta_OGG_RPGMV, /* Ogg Vorbis with encryption [RPG Maker MV games (PC)] */ #ifdef VGM_USE_FFMPEG meta_FFmpeg, @@ -886,6 +885,8 @@ typedef struct { off_t block_offset; size_t block_size; + int prev_block_samples; /* count for optimization */ + } vorbis_custom_codec_data; #endif @@ -967,6 +968,7 @@ typedef struct { mpeg_custom_t type; /* mpeg subtype */ mpeg_custom_config config; /* config depending on the mode */ + size_t default_buffer_size; mpeg_custom_stream **streams; /* array of MPEG streams (ex. 2ch+2ch) */ size_t streams_size;