diff --git a/Frameworks/vgmstream/vgmstream.xcodeproj/project.pbxproj b/Frameworks/vgmstream/vgmstream.xcodeproj/project.pbxproj index d02189474..c87344a54 100644 --- a/Frameworks/vgmstream/vgmstream.xcodeproj/project.pbxproj +++ b/Frameworks/vgmstream/vgmstream.xcodeproj/project.pbxproj @@ -259,7 +259,6 @@ 836F6FC918BDC2190095E648 /* ps2_2pfs.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E8D18BDC2180095E648 /* ps2_2pfs.c */; }; 836F6FCA18BDC2190095E648 /* ps2_adm.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E8E18BDC2180095E648 /* ps2_adm.c */; }; 836F6FCB18BDC2190095E648 /* ps2_ads.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E8F18BDC2180095E648 /* ps2_ads.c */; }; - 836F6FCC18BDC2190095E648 /* ps2_adsc.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E9018BDC2180095E648 /* ps2_adsc.c */; }; 836F6FCD18BDC2190095E648 /* ps2_ass.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E9118BDC2180095E648 /* ps2_ass.c */; }; 836F6FCE18BDC2190095E648 /* ps2_ast.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E9218BDC2180095E648 /* ps2_ast.c */; }; 836F6FD018BDC2190095E648 /* ps2_b1s.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E9418BDC2180095E648 /* ps2_b1s.c */; }; @@ -844,7 +843,6 @@ 836F6E8D18BDC2180095E648 /* ps2_2pfs.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_2pfs.c; sourceTree = ""; }; 836F6E8E18BDC2180095E648 /* ps2_adm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_adm.c; sourceTree = ""; }; 836F6E8F18BDC2180095E648 /* ps2_ads.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_ads.c; sourceTree = ""; }; - 836F6E9018BDC2180095E648 /* ps2_adsc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_adsc.c; sourceTree = ""; }; 836F6E9118BDC2180095E648 /* ps2_ass.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_ass.c; sourceTree = ""; }; 836F6E9218BDC2180095E648 /* ps2_ast.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_ast.c; sourceTree = ""; }; 836F6E9418BDC2180095E648 /* ps2_b1s.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_b1s.c; sourceTree = ""; }; @@ -1469,7 +1467,6 @@ 836F6E8D18BDC2180095E648 /* ps2_2pfs.c */, 836F6E8E18BDC2180095E648 /* ps2_adm.c */, 836F6E8F18BDC2180095E648 /* ps2_ads.c */, - 836F6E9018BDC2180095E648 /* ps2_adsc.c */, 836F6E9118BDC2180095E648 /* ps2_ass.c */, 836F6E9218BDC2180095E648 /* ps2_ast.c */, 836F6E9418BDC2180095E648 /* ps2_b1s.c */, @@ -2021,7 +2018,6 @@ 836F6F9018BDC2190095E648 /* idsp.c in Sources */, 836F6F2918BDC2190095E648 /* l5_555_decoder.c in Sources */, 836F6FF318BDC2190095E648 /* ps2_rstm.c in Sources */, - 836F6FCC18BDC2190095E648 /* ps2_adsc.c in Sources */, 836F6F7918BDC2190095E648 /* dc_asd.c in Sources */, 836F6FC118BDC2190095E648 /* pc_adp.c in Sources */, 836F701A18BDC2190095E648 /* psx_fag.c in Sources */, diff --git a/Frameworks/vgmstream/vgmstream/src/formats.c b/Frameworks/vgmstream/vgmstream/src/formats.c index 4ce2bb39b..019be68ea 100644 --- a/Frameworks/vgmstream/vgmstream/src/formats.c +++ b/Frameworks/vgmstream/vgmstream/src/formats.c @@ -208,6 +208,7 @@ static const char* extension_list[] = { //"mpc", //FFmpeg, not parsed (musepack) //common "mpdsp", "mpds", + "mps", //txh/reserved [Scandal (PS2)] "ms", "msa", "msb", @@ -647,7 +648,7 @@ static const meta_info meta_info_list[] = { {meta_DSP_STD, "Standard Nintendo DSP header"}, {meta_DSP_CSTR, "Namco Cstr header"}, {meta_GCSW, "GCSW header"}, - {meta_PS2_SShd, "SShd header"}, + {meta_PS2_SShd, "Sony ADS header"}, {meta_PS2_NPSF, "Namco Production Sound File (NPSF) header"}, {meta_RWSD, "Nintendo RWSD header (single stream)"}, {meta_RWAR, "Nintendo RWAR header (single RWAV stream)"}, @@ -661,8 +662,8 @@ static const meta_info meta_info_list[] = { {meta_DSP_STM, "Nintendo STM header"}, {meta_PS2_EXST, "EXST header"}, {meta_PS2_SVAG, "Konami SVAG header"}, - {meta_PS2_MIB, "assumed MIB Interleaved file by .mib extension"}, - {meta_PS2_MIB_MIH, "assumed MIB with MIH Info Header file by .mib+.mih extension"}, + {meta_PS2_MIB, "Headerless/MIB PS-ADPCM raw header"}, + {meta_PS2_MIB_MIH, "Sony MultiStream MIH+MIB header"}, {meta_DSP_MPDSP, "Single DSP header stereo by .mpdsp extension"}, {meta_PS2_MIC, "assume KOEI MIC file by .mic extension"}, {meta_DSP_JETTERS, "Double DSP header stereo by _lr.dsp extension"}, @@ -762,7 +763,7 @@ static const meta_info meta_info_list[] = { {meta_NGC_YMF, "YMF DSP Header"}, {meta_PS2_CCC, "CCC Header"}, {meta_PSX_FAG, "FAG Header"}, - {meta_PS2_MIHB, "MIH+MIB header"}, + {meta_PS2_MIHB, "Sony MultiStream MIC header"}, {meta_DSP_WII_MUS, "mus header"}, {meta_WII_SNG, "SNG DSP Header"}, {meta_RSD2VAG, "Radical RSD2/VAG header"}, @@ -875,7 +876,6 @@ static const meta_info meta_info_list[] = { {meta_DSP_DDSP, ".DDSP header"}, {meta_P3D, "Radical P3D header"}, {meta_PS2_TK1, "Tekken TK5STRM1 Header"}, - {meta_PS2_ADSC, "ADSC Header"}, {meta_NGC_DSP_MPDS, "MPDS DSP header"}, {meta_DSP_STR_IG, "Infogrames .DSP header"}, {meta_EA_SWVR, "Electronic Arts SWVR header"}, @@ -1009,7 +1009,7 @@ static const meta_info meta_info_list[] = { {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_MSB_MSH, "Sony MultiStream MSH+MSB header"}, {meta_OGG_RPGMV, "Ogg Vorbis (RPGMV header)"}, {meta_OGG_ENO, "Ogg Vorbis (ENO header)"}, {meta_TXTP, "TXTP generic header"}, diff --git a/Frameworks/vgmstream/vgmstream/src/layout/blocked.c b/Frameworks/vgmstream/vgmstream/src/layout/blocked.c index 240e58c16..ded88c8f6 100644 --- a/Frameworks/vgmstream/vgmstream/src/layout/blocked.c +++ b/Frameworks/vgmstream/vgmstream/src/layout/blocked.c @@ -42,14 +42,14 @@ void render_vgmstream_blocked(sample * buffer, int32_t sample_count, VGMSTREAM * break; /* probable infinite loop otherwise */ } - /* samples_this_block = 0 is allowed (empty block), will do nothing then move to next block */ - samples_to_do = vgmstream_samples_to_do(samples_this_block, samples_per_frame, vgmstream); if (samples_written + samples_to_do > sample_count) samples_to_do = sample_count - samples_written; if (vgmstream->current_block_offset >= 0) { - decode_vgmstream(vgmstream, samples_written, samples_to_do, buffer); + /* samples_this_block = 0 is allowed (empty block): do nothing then move to next block */ + if (samples_to_do > 0) + decode_vgmstream(vgmstream, samples_written, samples_to_do, buffer); } else { /* block end signal (used in halpst): partially 0-set buffer */ diff --git a/Frameworks/vgmstream/vgmstream/src/layout/blocked_ea_schl.c b/Frameworks/vgmstream/vgmstream/src/layout/blocked_ea_schl.c index 169bbaec1..b4a37ab14 100644 --- a/Frameworks/vgmstream/vgmstream/src/layout/blocked_ea_schl.c +++ b/Frameworks/vgmstream/vgmstream/src/layout/blocked_ea_schl.c @@ -2,45 +2,59 @@ #include "../coding/coding.h" #include "../vgmstream.h" -/* set up for the block at the given offset */ +/* parse EA style blocks, id+size+samples+data */ void block_update_ea_schl(off_t block_offset, VGMSTREAM * vgmstream) { STREAMFILE* streamFile = vgmstream->ch[0].streamfile; int i; int new_schl = 0; - size_t block_size = 0, block_samples = 0; + size_t block_size, block_samples; int32_t (*read_32bit)(off_t,STREAMFILE*) = vgmstream->codec_endian ? read_32bitBE : read_32bitLE; - size_t file_size = get_streamfile_size(streamFile); - while (block_offset < file_size) { - uint32_t id = read_32bitBE(block_offset+0x00,streamFile); + /* EOF reads: signal we have nothing and let the layout fail */ + if (block_offset >= get_streamfile_size(streamFile)) { + vgmstream->current_block_offset = block_offset; + vgmstream->next_block_offset = block_offset; + vgmstream->current_block_samples = -1; + return; + } + + /* read a single block */ + { + uint32_t block_id = read_32bitBE(block_offset+0x00,streamFile); block_size = read_32bitLE(block_offset+0x04,streamFile); if (block_size > 0x00F00000) /* size is always LE, except in early SAT/MAC */ block_size = read_32bitBE(block_offset+0x04,streamFile); - block_samples = 0; - - if (id == 0x5343446C || id == 0x5344454E || id == 0x53444652) { /* "SCDl" "SDEN" "SDFR" audio data */ - switch(vgmstream->coding_type) { - case coding_PSX: + switch(block_id) { + case 0x5343446C: /* "SCDl" */ + case 0x5344454E: /* "SDEN" */ + case 0x53444652: /* "SDFR" */ + case 0x53444745: /* "SDGE" */ + case 0x53444954: /* "SDIT" */ + case 0x53445350: /* "SDSP" */ + case 0x53445255: /* "SDRU" */ + case 0x53444A41: /* "SDJA" */ + /* audio chunk */ + if (vgmstream->coding_type == coding_PSX) block_samples = ps_bytes_to_samples(block_size-0x10, vgmstream->channels); - break; - default: + else block_samples = read_32bit(block_offset+0x08,streamFile); - break; - } + break; + default: + /* ignore other chunks (audio "SCHl/SCCl/...", video "pIQT/MADk/...", etc) */ + block_samples = 0; /* layout ignores this */ + break; } - else { /* any other chunk, audio ("SCHl" "SCCl" "SCLl" "SCEl" etc), or video ("pQGT" "pIQT "MADk" etc) */ - /* padding between "SCEl" and next "SCHl" (when subfiles exist) */ - if (id == 0x00000000) { - block_size = 0x04; - } - if (id == 0x5343486C || id == 0x5348454E || id == 0x53484652) { /* "SCHl" "SHEN" "SHFR" end block */ - new_schl = 1; - } - } + /* "SCHl" start block (movie "SHxx" shouldn't use multi files) */ + if (block_id == 0x5343486C) + new_schl = 1; + + /* padding between "SCEl" and next "SCHl" (when subfiles exist) */ + if (block_id == 0x00000000) + block_size = 0x04; /* guard against errors (happens in bad rips/endianness, observed max is vid ~0x20000) */ if (block_size == 0x00 || block_size > 0xFFFFF || block_samples > 0xFFFF) { @@ -48,25 +62,12 @@ void block_update_ea_schl(off_t block_offset, VGMSTREAM * vgmstream) { block_samples = 0; } - - if (block_samples) /* audio found */ - break; - block_offset += block_size; - - /* "SCEl" "SEEN" "SEFR" are aligned to 0x80 usually, but causes problems if not 32b-aligned (ex. Need for Speed 2 PC) */ - if ((id == 0x5343456C || id == 0x5345454E || id == 0x53454652) && block_offset % 0x04) { - block_offset += 0x04 - (block_offset % 0x04); + /* "SCEl" end chunk should be 32b-aligned, fixes some multi-SCHl [ex. Need for Speed 2 (PC) .eam] */ + if (((block_offset + block_size) % 0x04) && block_id == 0x5343456C) { + block_size += 0x04 - ((block_offset + block_size) % 0x04); } } - /* EOF reads: pretend we have samples to please the layout (unsure if this helps) */ - if (block_offset >= file_size) { - vgmstream->current_block_offset = block_offset; - vgmstream->next_block_offset = block_offset + 0x04; - vgmstream->current_block_samples = vgmstream->num_samples; - return; - } - /* set new channel offsets and ADPCM history */ /* ADPCM hist could be considered part of the stream/decoder (some EAXA decoders call it "EAXA R1" when it has hist), and BNKs diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ea_schl.c b/Frameworks/vgmstream/vgmstream/src/meta/ea_schl.c index a2b784082..86b613417 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ea_schl.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ea_schl.c @@ -73,7 +73,7 @@ typedef struct { static int parse_variable_header(STREAMFILE* streamFile, ea_header* ea, off_t begin_offset, int max_length); static uint32_t read_patch(STREAMFILE* streamFile, off_t* offset); -static int get_ea_stream_total_samples(STREAMFILE* streamFile, off_t start_offset, const ea_header* ea); +static int get_ea_stream_total_samples(STREAMFILE* streamFile, off_t start_offset, VGMSTREAM* vgmstream); static off_t get_ea_stream_mpeg_start_offset(STREAMFILE* streamFile, off_t start_offset, const ea_header* ea); static VGMSTREAM * init_vgmstream_ea_variable_header(STREAMFILE *streamFile, ea_header *ea, off_t start_offset, int is_bnk, int total_streams); @@ -93,11 +93,16 @@ VGMSTREAM * init_vgmstream_ea_schl(STREAMFILE *streamFile) { /* check header */ if (read_32bitBE(0x00,streamFile) != 0x5343486C && /* "SCHl" */ read_32bitBE(0x00,streamFile) != 0x5348454E && /* "SHEN" */ - read_32bitBE(0x00,streamFile) != 0x53484652) /* "SHFR" */ + read_32bitBE(0x00,streamFile) != 0x53484652 && /* "SHFR" */ + read_32bitBE(0x00,streamFile) != 0x53484745 && /* "SHGE" */ + read_32bitBE(0x00,streamFile) != 0x53484954 && /* "SHIT" */ + read_32bitBE(0x00,streamFile) != 0x53485350 && /* "SHSP" */ + read_32bitBE(0x00,streamFile) != 0x53485255 && /* "SHRU" */ + read_32bitBE(0x00,streamFile) != 0x53484A41) /* "SHJA" */ goto fail; - /* stream is divided into blocks/chunks: SCHl=audio header, SCCl=count of SCDl, SCDl=data xN, SCLl=loop end, SCEl=end. - * Video uses various blocks (MVhd/MV0K/etc) and sometimes alt audio blocks (SHxx/SCxx/SDxx/SExx where XX=language, EN/FR). + /* Stream is divided into blocks/chunks: SCHl=audio header, SCCl=count of SCDl, SCDl=data xN, SCLl=loop end, SCEl=end. + * Video uses picture blocks (MVhd/MV0K/etc) and sometimes multiaudio blocks (SHxx/SCxx/SDxx/SExx where xx=language=EN/FR/GE/IT/SP/RU/JA). * The number/size is affected by: block rate setting, sample rate, channels, CPU location (SPU/main/DSP/others), etc */ header_size = read_32bitLE(0x04,streamFile); @@ -362,13 +367,6 @@ static VGMSTREAM * init_vgmstream_ea_variable_header(STREAMFILE *streamFile, ea_ if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) goto fail; - /* fix num_samples for streams with multiple SCHl */ - if (!is_bnk) { - int total_samples = get_ea_stream_total_samples(streamFile, start_offset, ea); - if (total_samples > vgmstream->num_samples) - vgmstream->num_samples = total_samples; - } - if (is_bnk) { /* setup channel offsets */ @@ -411,6 +409,11 @@ static VGMSTREAM * init_vgmstream_ea_variable_header(STREAMFILE *streamFile, ea_ } } else { + /* fix num_samples for streams with multiple SCHl */ + int total_samples = get_ea_stream_total_samples(streamFile, start_offset, vgmstream); + if (total_samples > vgmstream->num_samples) + vgmstream->num_samples = total_samples; + /* setup first block to update offsets */ block_update_ea_schl(start_offset,vgmstream); } @@ -755,60 +758,22 @@ fail: /* Get total samples by parsing block headers, needed when multiple files are stitched together. * Some EA files (.mus/eam/sng/etc) concat many small subfiles, used for interactive/mapped * music (.map/lin). Subfiles always share header, except num_samples. */ -static int get_ea_stream_total_samples(STREAMFILE* streamFile, off_t start_offset, const ea_header* ea) { +static int get_ea_stream_total_samples(STREAMFILE* streamFile, off_t start_offset, VGMSTREAM* vgmstream) { int num_samples = 0; int new_schl = 0; - off_t block_offset = start_offset; - size_t block_size, block_samples; - int32_t (*read_32bit)(off_t,STREAMFILE*) = ea->big_endian ? read_32bitBE : read_32bitLE; - size_t file_size = get_streamfile_size(streamFile); - - while (block_offset < file_size) { - uint32_t id = read_32bitBE(block_offset+0x00,streamFile); - - block_size = read_32bitLE(block_offset+0x04,streamFile); - if (block_size > 0x00F00000) /* size is always LE, except in early SAT/MAC */ - block_size = read_32bitBE(block_offset+0x04,streamFile); - - block_samples = 0; - - if (id == 0x5343446C || id == 0x5344454E || id == 0x53444652) { /* "SCDl" "SDEN" "SDFR" audio data */ - switch (ea->codec2) { - case EA_CODEC2_VAG: - block_samples = ps_bytes_to_samples(block_size-0x10, ea->channels); - break; - default: - block_samples = read_32bit(block_offset+0x08,streamFile); - break; - } - } - else { /* any other chunk, audio ("SCHl" "SCCl" "SCLl" "SCEl" etc), or video ("pQGT" "pIQT "MADk" "MPCh" etc) */ - /* padding between "SCEl" and next "SCHl" (when subfiles exist) */ - if (id == 0x00000000) { - block_size = 0x04; - } - - if (id == 0x5343486C || id == 0x5348454E || id == 0x53484652) { /* "SCHl" "SHEN" "SHFR" end block */ + /* calc num_samples as playable data size varies between files/blocks */ + { + vgmstream->next_block_offset = start_offset; + do { + uint32_t block_id = read_32bitBE(vgmstream->next_block_offset+0x00,streamFile); + if (block_id == 0x5343486C) /* "SCHl" start block (movie "SHxx" shouldn't use multi files) */ new_schl = 1; - } - } - /* guard against errors (happens in bad rips/endianness, observed max is vid ~0x20000) */ - if (block_size == 0x00 || block_size > 0xFFFFF || block_samples > 0xFFFF) { - VGM_LOG("EA SCHl: bad block size %x at %lx\n", block_size, block_offset); - block_size = 0x04; - block_samples = 0; - } - - num_samples += block_samples; - block_offset += block_size; - - /* "SCEl" "SEEN" "SEFR" are aligned to 0x80 usually, but causes problems if not 32b-aligned (ex. Need for Speed 2 PC) */ - if ((id == 0x5343456C || id == 0x5345454E || id == 0x53454652) && block_offset % 0x04) { - VGM_LOG_ONCE("EA SCHl: mis-aligned end offset found\n"); - block_offset += 0x04 - (block_offset % 0x04); + block_update_ea_schl(vgmstream->next_block_offset,vgmstream); + num_samples += vgmstream->current_block_samples; } + while (vgmstream->next_block_offset < get_streamfile_size(streamFile)); } /* only use calculated samples with multiple subfiles (rarely header samples may be less due to padding) */ @@ -828,22 +793,31 @@ static off_t get_ea_stream_mpeg_start_offset(STREAMFILE* streamFile, off_t start int32_t (*read_32bit)(off_t,STREAMFILE*) = ea->big_endian ? read_32bitBE : read_32bitLE; while (block_offset < file_size) { - uint32_t id, block_size; + uint32_t block_id, block_size; + off_t offset; - id = read_32bitBE(block_offset+0x00,streamFile); + block_id = read_32bitBE(block_offset+0x00,streamFile); block_size = read_32bitLE(block_offset+0x04,streamFile); if (block_size > 0x00F00000) /* size is always LE, except in early SAT/MAC */ block_size = read_32bitBE(block_offset+0x04,streamFile); - if (id == 0x5343446C || id == 0x5344454E || id == 0x53444652) { /* "SCDl/SDEN/SDFR" data block found */ - off_t offset = read_32bit(block_offset+0x0c,streamFile); /* first value seems ok, second is something else in EALayer3 */ - return block_offset + 0x0c + ea->channels*0x04 + offset; - } else if (id == 0x5343436C || id == 0x5343454E || id == 0x53434652) { /* "SCCl/SCEN/SCFR" data count found */ - block_offset += block_size; /* size includes header */ - continue; - } else { - goto fail; + switch(block_id) { + case 0x5343446C: /* "SCDl" */ + case 0x5344454E: /* "SDEN" */ + case 0x53444652: /* "SDFR" */ + case 0x53444745: /* "SDGE" */ + case 0x53444954: /* "SDIT" */ + case 0x53445350: /* "SDSP" */ + case 0x53445255: /* "SDRU" */ + case 0x53444A41: /* "SDJA" */ + offset = read_32bit(block_offset+0x0c,streamFile); /* first value seems ok, second is something else in EALayer3 */ + return block_offset + 0x0c + ea->channels*0x04 + offset; + case 0x00000000: + goto fail; /* just in case */ + default: + block_offset += block_size; /* size includes header */ + break; } } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ffmpeg.c b/Frameworks/vgmstream/vgmstream/src/meta/ffmpeg.c index dc0639749..c93e60713 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ffmpeg.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ffmpeg.c @@ -17,11 +17,15 @@ VGMSTREAM * init_vgmstream_ffmpeg_offset(STREAMFILE *streamFile, uint64_t start, VGMSTREAM *vgmstream = NULL; int loop_flag = 0; int32_t loop_start = 0, loop_end = 0, num_samples = 0; + int total_subsongs, target_subsong = streamFile->stream_index; /* init ffmpeg */ ffmpeg_codec_data *data = init_ffmpeg_offset(streamFile, start, size); if (!data) return NULL; + total_subsongs = data->streamCount; + if (target_subsong == 0) target_subsong = 1; + if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail; /* try to get .pos data */ { diff --git a/Frameworks/vgmstream/vgmstream/src/meta/fsb.c b/Frameworks/vgmstream/vgmstream/src/meta/fsb.c index 1e55bc7e7..306765649 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/fsb.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/fsb.c @@ -126,17 +126,17 @@ VGMSTREAM * init_vgmstream_fsb(STREAMFILE *streamFile) { /* sample header (first stream only, not sure if there are multi-FSB1) */ { - off_t s_off = fsb.base_header_size; + off_t header_offset = fsb.base_header_size; - fsb.name_offset = s_off; + fsb.name_offset = header_offset; fsb.name_size = 0x20; - fsb.num_samples = read_32bitLE(s_off+0x20,streamFile); - fsb.stream_size = read_32bitLE(s_off+0x24,streamFile); - fsb.sample_rate = read_32bitLE(s_off+0x28,streamFile); + fsb.num_samples = read_32bitLE(header_offset+0x20,streamFile); + fsb.stream_size = read_32bitLE(header_offset+0x24,streamFile); + fsb.sample_rate = read_32bitLE(header_offset+0x28,streamFile); /* 0x2c:? 0x2e:? 0x30:? 0x32:? */ - fsb.mode = read_32bitLE(s_off+0x34,streamFile); - fsb.loop_start = read_32bitLE(s_off+0x38,streamFile); - fsb.loop_end = read_32bitLE(s_off+0x3c,streamFile); + fsb.mode = read_32bitLE(header_offset+0x34,streamFile); + fsb.loop_start = read_32bitLE(header_offset+0x38,streamFile); + fsb.loop_end = read_32bitLE(header_offset+0x3c,streamFile); fsb.channels = (fsb.mode & FSOUND_STEREO) ? 2 : 1; if (fsb.loop_end > fsb.num_samples) /* this seems common... */ @@ -191,41 +191,55 @@ VGMSTREAM * init_vgmstream_fsb(STREAMFILE *streamFile) { /* sample header (N-stream) */ { int i; - off_t s_off = fsb.base_header_size; - off_t d_off = fsb.base_header_size + fsb.sample_header_size; + off_t header_offset = fsb.base_header_size; + off_t data_offset = fsb.base_header_size + fsb.sample_header_size; /* find target_stream header (variable sized) */ for (i = 0; i < fsb.total_subsongs; i++) { - size_t stream_header_size = (uint16_t)read_16bitLE(s_off+0x00,streamFile); - fsb.name_offset = s_off+0x02; - fsb.name_size = 0x20-0x02; - fsb.num_samples = read_32bitLE(s_off+0x20,streamFile); - fsb.stream_size = read_32bitLE(s_off+0x24,streamFile); - fsb.loop_start = read_32bitLE(s_off+0x28,streamFile); - fsb.loop_end = read_32bitLE(s_off+0x2c,streamFile); - fsb.mode = read_32bitLE(s_off+0x30,streamFile); - fsb.sample_rate = read_32bitLE(s_off+0x34,streamFile); - /* 0x38:defvol 0x3a:defpan 0x3c:defpri */ - fsb.channels = read_16bitLE(s_off+0x3e,streamFile); - /* FSB3.1/4: 0x40:mindistance 0x44:maxdistance 0x48:varfreq/size_32bits 0x4c:varvol 0x4e:fsb.varpan */ - /* FSB3/4: 0x50:extended_data size_32bits (not always given) */ + size_t stream_header_size; - if (i+1 == target_subsong) /* d_off found */ + if ((fsb.flags & FMOD_FSB_SOURCE_BASICHEADERS) && i > 0) { + /* miniheader, all subsongs reuse first header [rare, ex. Biker Mice from Mars (PS2)] */ + stream_header_size = 0x08; + fsb.num_samples = read_32bitLE(header_offset+0x00,streamFile); + fsb.stream_size = read_32bitLE(header_offset+0x04,streamFile); + fsb.loop_start = 0; + fsb.loop_end = 0; + } + else { + /* subsong header for normal files */ + stream_header_size = (uint16_t)read_16bitLE(header_offset+0x00,streamFile); + fsb.name_offset = header_offset+0x02; + fsb.name_size = 0x20-0x02; + fsb.num_samples = read_32bitLE(header_offset+0x20,streamFile); + fsb.stream_size = read_32bitLE(header_offset+0x24,streamFile); + fsb.loop_start = read_32bitLE(header_offset+0x28,streamFile); + fsb.loop_end = read_32bitLE(header_offset+0x2c,streamFile); + fsb.mode = read_32bitLE(header_offset+0x30,streamFile); + fsb.sample_rate = read_32bitLE(header_offset+0x34,streamFile); + /* 0x38:defvol 0x3a:defpan 0x3c:defpri */ + fsb.channels = read_16bitLE(header_offset+0x3e,streamFile); + /* FSB3.1/4: 0x40:mindistance 0x44:maxdistance 0x48:varfreq/size_32bits 0x4c:varvol 0x4e:fsb.varpan */ + /* FSB3/4: 0x50:extended_data size_32bits (not always given) */ + } + + if (i+1 == target_subsong) /* final data_offset found */ break; - s_off += stream_header_size; - d_off += fsb.stream_size; /* there is no offset so manually count */ + header_offset += stream_header_size; + data_offset += fsb.stream_size; /* there is no offset so manually count */ - /* IMAs streams have weird end padding (maybe: FSB3=no padding, FSB4=always padding) */ - if ((fsb.mode & FSOUND_IMAADPCM) && (fsb.flags & FMOD_FSB_SOURCE_MPEG_PADDED4)) { - if (d_off % 0x20) - d_off += 0x20 - (d_off % 0x20); + /* some subsongs offsets need padding (most FSOUND_IMAADPCM, few MPEG too [Hard Reset (PC) subsong 5]) + * other PADDED4 may set it (ex. XMA) but don't seem to use it and work fine */ + if (fsb.flags & FMOD_FSB_SOURCE_MPEG_PADDED4) { + if (data_offset % 0x20) + data_offset += 0x20 - (data_offset % 0x20); } } if (i > fsb.total_subsongs) goto fail; /* not found */ - start_offset = d_off; - custom_data_offset = s_off + fsb.sample_header_min; /* DSP coefs, seek tables, etc */ + start_offset = data_offset; + custom_data_offset = header_offset + fsb.sample_header_min; /* DSP coefs, seek tables, etc */ } } @@ -246,7 +260,7 @@ VGMSTREAM * init_vgmstream_fsb(STREAMFILE *streamFile) { && fsb.num_samples < 20*fsb.sample_rate) /* seconds, lame but no other way to know */ loop_flag = 0; - /* ping-pong looping = no looping? (forward > reverse > forward) */ + /* ping-pong looping = no looping? (forward > reverse > forward) [ex. Biker Mice from Mars (PS2)] */ VGM_ASSERT(fsb.mode & FSOUND_LOOP_BIDI, "FSB BIDI looping found\n"); diff --git a/Frameworks/vgmstream/vgmstream/src/meta/fsb_keys.h b/Frameworks/vgmstream/vgmstream/src/meta/fsb_keys.h index b65bea954..b871f2081 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/fsb_keys.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/fsb_keys.h @@ -2,8 +2,8 @@ #define _FSB_KEYS_H_ typedef struct { - int is_fsb5; - int is_alt; + int is_fsb5; /* FSB5 or FSB4/3*/ + int is_alt; /* alt XOR mode (seemingly not tied to FSB version or anything) */ size_t fsbkey_size; const uint8_t *fsbkey; } fsbkey_info; @@ -69,6 +69,9 @@ static const uint8_t key_sc2[] = { 0x42,0x32,0x41,0x37,0x42,0x42,0x30,0x30 }; /* Cookie Run: Ovenbreak */ //"ghfxhslrghfxhslr" static const uint8_t key_cro[] = { 0x67,0x68,0x66,0x78,0x68,0x73,0x6C,0x72,0x67,0x68,0x66,0x78,0x68,0x73,0x6C,0x72 }; +/* Monster Jam (PS2) */ //"truck/impact/carbody" +static const uint8_t key_mtj[] = { 0x74,0x72,0x75,0x63,0x6B,0x2F,0x69,0x6D,0x70,0x61,0x63,0x74,0x2F,0x63,0x61,0x72,0x62,0x6F,0x64,0x79 }; + // Unknown: // - Battle: Los Angeles // - Guitar Hero: Warriors of Rock, DJ hero FSB @@ -129,6 +132,7 @@ static const fsbkey_info fsbkey_list[] = { { 1,0, sizeof(key_sc2),key_sc2 },//untested { 1,1, sizeof(key_sc2),key_sc2 },//untested { 1,0, sizeof(key_cro),key_cro }, + { 0,1, sizeof(key_mtj),key_mtj },// FSB3 }; static const int fsbkey_list_count = sizeof(fsbkey_list) / sizeof(fsbkey_list[0]); diff --git a/Frameworks/vgmstream/vgmstream/src/meta/hca_keys.h b/Frameworks/vgmstream/vgmstream/src/meta/hca_keys.h index 67cf66cbd..c9f6362cb 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/hca_keys.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/hca_keys.h @@ -237,6 +237,9 @@ static const hcakey_info hcakey_list[] = { // Princess Connect Re:Dive (iOS/Android/PC) {3201512}, // 000000000030D9E8 + // PriPara: All Idol Perfect Stage (Takara Tomy) [Switch] + {217735759}, // 000000000CFA624F + }; #endif/*_HCA_KEYS_H_*/ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/meta.h b/Frameworks/vgmstream/vgmstream/src/meta/meta.h index 00bb84a22..cb6317490 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/meta.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/meta.h @@ -38,6 +38,7 @@ VGMSTREAM * init_vgmstream_ngc_dsp_std_int(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_ngc_dsp_csmp(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_ps2_ads(STREAMFILE *streamFile); +VGMSTREAM * init_vgmstream_ps2_ads_container(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_ps2_npsf(STREAMFILE *streamFile); @@ -483,8 +484,6 @@ VGMSTREAM * init_vgmstream_dsp_ddsp(STREAMFILE* streamFile); VGMSTREAM * init_vgmstream_p3d(STREAMFILE* streamFile); -VGMSTREAM * init_vgmstream_ps2_adsc(STREAMFILE* streamFile); - VGMSTREAM * init_vgmstream_ngc_dsp_mpds(STREAMFILE* streamFile); VGMSTREAM * init_vgmstream_dsp_str_ig(STREAMFILE* streamFile); diff --git a/Frameworks/vgmstream/vgmstream/src/meta/msb_msh.c b/Frameworks/vgmstream/vgmstream/src/meta/msb_msh.c index 0333716f4..424bb6c9f 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/msb_msh.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/msb_msh.c @@ -1,7 +1,7 @@ #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)] */ +/* MSB+MSH - SCEE MultiStream flat bank [namCollection: Ace Combat 2 (PS2) sfx, EyeToy Play (PS2)] */ VGMSTREAM * init_vgmstream_msb_msh(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; STREAMFILE * streamHeader = NULL; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ps2_ads.c b/Frameworks/vgmstream/vgmstream/src/meta/ps2_ads.c index 628aba3ec..b9e6399d3 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ps2_ads.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ps2_ads.c @@ -1,201 +1,381 @@ #include "meta.h" -#include "../util.h" +#include "../coding/coding.h" -/* Sony .ADS with SShd & SSbd Headers */ +/* .ADS - Sony's "Audio Stream" format [Edit Racing (PS2), Evergrace II (PS2), Pri-Saga! Portable (PSP)] */ VGMSTREAM * init_vgmstream_ps2_ads(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; - - int loop_flag=0; - int channel_count; off_t start_offset; - off_t check_offset; - int32_t streamSize; + int loop_flag, channel_count, sample_rate, interleave, is_loop_samples = 0; + size_t body_size, stream_size, file_size; + uint32_t codec, loop_start_sample = 0, loop_end_sample = 0, loop_start_offset = 0, loop_end_offset = 0; + coding_t coding_type; + int ignore_silent_frame_cavia = 0, ignore_silent_frame_capcom = 0; - uint8_t testBuffer[0x10]; - uint8_t isPCM = 0; - off_t readOffset = 0; - off_t loopEnd = 0; - - int i; - - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("ads",filename_extension(filename)) && - strcasecmp("ss2",filename_extension(filename))) goto fail; - - /* check SShd Header */ - if (read_32bitBE(0x00,streamFile) != 0x53536864) + /* checks */ + /* .ads: actual extension + * .ss2: demuxed videos (fake?) + * .pcm: Taisho Mononoke Ibunroku (PS2) + * .adx: Armored Core 3 (PS2) */ + if (!check_extensions(streamFile, "ads,ss2,pcm,adx")) goto fail; - /* check SSbd Header */ - if (read_32bitBE(0x20,streamFile) != 0x53536264) + if (read_32bitBE(0x00,streamFile) != 0x53536864 && /* "SShd" */ + read_32bitBE(0x20,streamFile) != 0x53536264) /* "SSbd" */ + goto fail; + if (read_32bitLE(0x04,streamFile) != 0x18 && /* standard header size */ + read_32bitLE(0x04,streamFile) != 0x20) /* True Fortune (PS2) */ goto fail; - /* check if file is not corrupt */ - /* seems the Gran Turismo 4 ADS files are considered corrupt,*/ - /* so I changed it to adapt the stream size if that's the case */ - /* instead of failing playing them at all*/ - streamSize = read_32bitLE(0x24,streamFile); - - if (get_streamfile_size(streamFile) < (size_t)(streamSize + 0x28)) + + /* base values (a bit unorderly since devs hack ADS too much and detection is messy) */ { - streamSize = get_streamfile_size(streamFile) - 0x28; + codec = read_32bitLE(0x08,streamFile); + sample_rate = read_32bitLE(0x0C,streamFile); + channel_count = read_32bitLE(0x10,streamFile); /* up to 4 [Eve of Extinction (PS2)]*/ + interleave = read_32bitLE(0x14,streamFile); /* set even when mono */ + + + switch(codec) { + case 0x01: /* official definition */ + case 0x80000001: /* [Evergrace II (PS2), but not other From Soft games] */ + coding_type = coding_PCM16LE; + + /* Angel Studios/Rockstar San Diego videos codec hijack [Red Dead Revolver (PS2), Spy Hunter 2 (PS2)] */ + if (sample_rate == 12000 && interleave == 0x200) { + sample_rate = 48000; + interleave = 0x40; + coding_type = coding_DVI_IMA_int; + /* should try to detect IMA data but it's not so easy, this works ok since + * no known games use these settings, videos normally are 48000/24000hz */ + } + break; + + case 0x10: /* official definition */ + case 0x02: /* Capcom games extension, stereo only [Megaman X7 (PS2), Breath of Fire V (PS2), Clock Tower 3 (PS2)] */ + coding_type = coding_PSX; + break; + + case 0x00: /* PCM16BE from official docs, probably never used */ + default: + VGM_LOG("ADS: unknown codec\n"); + goto fail; + } } - /* check loop */ - if ((read_32bitLE(0x1C,streamFile) == 0xFFFFFFFF) || - ((read_32bitLE(0x18,streamFile) == 0) && (read_32bitLE(0x1C,streamFile) == 0))) + + /* sizes */ { + file_size = get_streamfile_size(streamFile); + body_size = read_32bitLE(0x24,streamFile); + + /* bigger than file_size in rare cases, even if containing all data (ex. Megaman X7's SY04.ADS) */ + if (body_size + 0x28 > file_size) { + body_size = file_size - 0x28; + } + + /* True Fortune: weird stream size */ + if (body_size * 2 == file_size - 0x18) { + body_size = (body_size * 2) - 0x10; + } + + stream_size = body_size; + } + + + /* offset */ + { + start_offset = 0x28; + + /* start padding (body size is ok, may have end padding) [Evergrace II (PS2), Armored Core 3 (PS2)] */ + /* detection depends on files being properly ripped, so broken/cut files won't play ok */ + if (file_size - body_size >= 0x800) { + start_offset = 0x800; /* aligned to sector */ + + /* too much end padding, happens in Super Galdelic Hour's SEL.ADS, maybe in bad rips too */ + VGM_ASSERT(file_size - body_size > 0x8000, "ADS: big end padding %x\n", file_size - body_size); + } + + /* "ADSC" container */ + if (coding_type == coding_PSX + && read_32bitLE(0x28,streamFile) == 0x1000 /* real start */ + && read_32bitLE(0x2c,streamFile) == 0 + && read_32bitLE(0x1008,streamFile) != 0) { + int i; + int is_adsc = 1; + + /* should be empty up to data start */ + for (i = 0; i < 0xFDC/4; i++) { + if (read_32bitLE(0x2c+(i*4),streamFile) != 0) { + is_adsc = 0; + break; + } + } + + if (is_adsc) { + start_offset = 0x1000 - 0x08; /* remove "ADSC" alignment */ + /* stream_size doesn't count start offset padding */ + } + } + } + + + /* loops */ + { + uint32_t loop_start, loop_end; + + loop_start = read_32bitLE(0x18,streamFile); + loop_end = read_32bitLE(0x1C,streamFile); + loop_flag = 0; - } - else - { - loop_flag = 1; + + /* detect loops the best we can; docs say those are loop block addresses, + * but each maker does whatever (no games seem to use PS-ADPCM loop flags though) */ + + + if (loop_start != 0xFFFFFFFF && loop_end == 0xFFFFFFFF) { + + if (codec == 0x02) { /* Capcom codec */ + /* Capcom games: loop_start is address * 0x10 [Mega Man X7, Breath of Fire V, Clock Tower 3] */ + loop_flag = ((loop_start * 0x10) + 0x200 < body_size); /* near the end (+0x20~80) means no loop */ + loop_start_offset = loop_start * 0x10; + ignore_silent_frame_capcom = 1; + } + else if (read_32bitBE(0x28,streamFile) == 0x50414421) { /* "PAD!" padding until 0x800 */ + /* Super Galdelic Hour: loop_start is PCM bytes */ + loop_flag = 1; + loop_start_sample = loop_start / 2 / channel_count; + is_loop_samples = 1; + } + else if ((loop_start % 0x800 == 0) && loop_start > 0) {/* sector-aligned, min is 0x800 */ + /* cavia games: loop_start is offset [Drakengard 1/2, GITS: Stand Alone Complex] */ + loop_flag = 1; + loop_start_offset = loop_start; + ignore_silent_frame_cavia = 1; + } + else if (loop_start % 0x800 != 0 || loop_start == 0) { /* not sector aligned */ + /* Katakamuna: loop_start is address * 0x10 */ + loop_flag = 1; + loop_start_offset = loop_start * 0x10; + } + } + else if (loop_start != 0xFFFFFFFF && loop_end != 0xFFFFFFFF + && loop_end > 0) { /* ignore Kamen Rider Blade and others */ +#if 0 + //todo improve detection to avoid clashing with address*0x20 + if (loop_end == body_size / 0x10) { /* always body_size? but not all files should loop */ + /* Akane Iro ni Somaru Saka - Parallel: loops is address * 0x10 */ + loop_flag = 1; + loop_start_offset = loop_start * 0x10; + loop_end_offset = loop_end * 0x10; + } +#endif + if (loop_end <= body_size / 0x70 && coding_type == coding_PCM16LE) { /* close to body_size */ + /* Armored Core - Nexus: loops is address * 0x70 */ + loop_flag = 1; + loop_start_offset = loop_start * 0x70; + loop_end_offset = loop_end * 0x70; + } + else if (loop_end <= body_size / 0x20 && coding_type == coding_PCM16LE) { /* close to body_size */ + /* Armored Core - Nine Breaker: loops is address * 0x20 */ + loop_flag = 1; + loop_start_offset = loop_start * 0x20; + loop_end_offset = loop_end * 0x20; + } + else if (loop_end <= body_size / 0x20 && coding_type == coding_PSX) { /* close to body_size */ + /* various games: loops is address * 0x20 [Fire Pro Wrestling Returns, A.C.E. - Another Century's Episode] */ + loop_flag = 1; + loop_start_offset = loop_start * 0x20; + loop_end_offset = loop_end * 0x20; + } + else if ((loop_end > body_size / 0x20 && coding_type == coding_PSX) || + (loop_end > body_size / 0x70 && coding_type == coding_PCM16LE)) { + /* various games: loops in samples [Eve of Extinction, Culdcept, WWE Smackdown! 3] */ + loop_flag = 1; + loop_start_sample = loop_start; + loop_end_sample = loop_end; + is_loop_samples = 1; + VGM_LOG("1\n"); + } + } + + //todo Jet Ion Grand Prix seems to have some loop-like values at 0x28 + //todo Yoake mae yori Ruriiro na has loops in unknown format + } + + + /* most games have empty PS-ADPCM frames in the last interleave block that should be skipped for smooth looping */ + if (coding_type == coding_PSX) { + off_t offset, min_offset; + + offset = start_offset + stream_size; + min_offset = offset - interleave; + + do { + offset -= 0x10; + + if (read_8bit(offset+0x01,streamFile) == 0x07) { + stream_size -= 0x10*channel_count;/* ignore don't decode flag/padding frame (most common) [ex. Capcom games] */ + } + else if (read_32bitBE(offset+0x00,streamFile) == 0x00000000 && read_32bitBE(offset+0x04,streamFile) == 0x00000000 && + read_32bitBE(offset+0x08,streamFile) == 0x00000000 && read_32bitBE(offset+0x0c,streamFile) == 0x00000000) { + stream_size -= 0x10*channel_count; /* ignore null frame [ex. A.C.E. Another Century Episode 1/2/3] */ + } + else if (read_32bitBE(offset+0x00,streamFile) == 0x00007777 && read_32bitBE(offset+0x04,streamFile) == 0x77777777 && + read_32bitBE(offset+0x08,streamFile) == 0x77777777 && read_32bitBE(offset+0x0c,streamFile) == 0x77777777) { + stream_size -= 0x10*channel_count; /* ignore padding frame [ex. Akane Iro ni Somaru Saka - Parallel] */ + } + else if (read_32bitBE(offset+0x00,streamFile) == 0x0C020000 && read_32bitBE(offset+0x04,streamFile) == 0x00000000 && + read_32bitBE(offset+0x08,streamFile) == 0x00000000 && read_32bitBE(offset+0x0c,streamFile) == 0x00000000 && + ignore_silent_frame_cavia) { + stream_size -= 0x10*channel_count; /* ignore silent frame [ex. cavia games] */ + } + else if (read_32bitBE(offset+0x00,streamFile) == 0x0C010000 && read_32bitBE(offset+0x04,streamFile) == 0x00000000 && + read_32bitBE(offset+0x08,streamFile) == 0x00000000 && read_32bitBE(offset+0x0c,streamFile) == 0x00000000 && + ignore_silent_frame_capcom) { + stream_size -= 0x10*channel_count; /* ignore silent frame [ex. Capcom games] */ + } + else { + break; /* standard frame */ + } + } + while(offset > min_offset); + + /* don't bother fixing loop_end_offset since will be adjusted to num_samples later, if needed */ } - channel_count=read_32bitLE(0x10,streamFile); /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; - /* fill in the vital statistics */ - vgmstream->channels = read_32bitLE(0x10,streamFile); - vgmstream->sample_rate = read_32bitLE(0x0C,streamFile); - - /* Check for Compression Scheme */ - vgmstream->coding_type = coding_PSX; - vgmstream->num_samples = ((streamSize-0x40)/16*28)/vgmstream->channels; - - /* SS2 container with RAW Interleaved PCM */ - if (read_32bitLE(0x08,streamFile)!=0x10) - { - - vgmstream->coding_type=coding_PCM16LE; - vgmstream->num_samples = streamSize/2/vgmstream->channels; - } - - vgmstream->interleave_block_size = read_32bitLE(0x14,streamFile); + vgmstream->sample_rate = sample_rate; + vgmstream->coding_type = coding_type; + vgmstream->interleave_block_size = interleave; vgmstream->layout_type = layout_interleave; vgmstream->meta_type = meta_PS2_SShd; - /* Get loop point values */ - if(vgmstream->loop_flag) { - if((read_32bitLE(0x1C,streamFile)*0x10*vgmstream->channels+0x800)==get_streamfile_size(streamFile)) - { - // Search for Loop Value - readOffset=(off_t)get_streamfile_size(streamFile)-(4*vgmstream->interleave_block_size); + switch(coding_type) { + case coding_PCM16LE: + vgmstream->num_samples = pcm_bytes_to_samples(stream_size, channel_count, 16); + break; + case coding_PSX: + vgmstream->num_samples = ps_bytes_to_samples(stream_size, channel_count); + break; + case coding_DVI_IMA_int: + vgmstream->num_samples = ima_bytes_to_samples(stream_size, channel_count); + break; + default: + goto fail; + } - do { - readOffset+=(off_t)read_streamfile(testBuffer,readOffset,0x10,streamFile); - - // Loop End ... - if(testBuffer[0x01]==0x01) { - if(loopEnd==0) loopEnd = readOffset-0x10; + if (vgmstream->loop_flag) { + if (is_loop_samples) { + vgmstream->loop_start_sample = loop_start_sample; + vgmstream->loop_end_sample = loop_end_sample; + } + else { + switch(vgmstream->coding_type) { + case coding_PCM16LE: + vgmstream->loop_start_sample = pcm_bytes_to_samples(loop_start_offset,channel_count,16); + vgmstream->loop_end_sample = pcm_bytes_to_samples(loop_end_offset,channel_count,16); break; - } - - } while (streamFile->get_offset(streamFile)<(int32_t)get_streamfile_size(streamFile)); - - vgmstream->loop_start_sample = 0; - vgmstream->loop_end_sample = (loopEnd/(vgmstream->interleave_block_size)*vgmstream->interleave_block_size)/16*28; - vgmstream->loop_end_sample += (loopEnd%vgmstream->interleave_block_size)/16*28; - vgmstream->loop_end_sample /=vgmstream->channels; - - } else { - if(read_32bitLE(0x1C,streamFile)<=vgmstream->num_samples) { - vgmstream->loop_start_sample = read_32bitLE(0x18,streamFile); - vgmstream->loop_end_sample = read_32bitLE(0x1C,streamFile); - } else { - vgmstream->loop_start_sample = (read_32bitLE(0x18,streamFile)*0x10)/16*28/vgmstream->channels;; - vgmstream->loop_end_sample = (read_32bitLE(0x1C,streamFile)*0x10)/16*28/vgmstream->channels; + case coding_PSX: + vgmstream->loop_start_sample = ps_bytes_to_samples(loop_start_offset,channel_count); + vgmstream->loop_end_sample = ps_bytes_to_samples(loop_end_offset,channel_count); + break; + default: + goto fail; } } - } - /* don't know why, but it does happen, in ps2 too :( */ - if (vgmstream->loop_end_sample > vgmstream->num_samples) - vgmstream->loop_end_sample = vgmstream->num_samples; - { - start_offset=0x28; + /* when loop_end = 0xFFFFFFFF */ + if (vgmstream->loop_end_sample == 0) + vgmstream->loop_end_sample = vgmstream->num_samples; + + /* happens even when loops are directly samples, loops sound fine (ex. Culdcept) */ + if (vgmstream->loop_end_sample > vgmstream->num_samples) + vgmstream->loop_end_sample = vgmstream->num_samples; } - if ((streamSize * 2) == (get_streamfile_size(streamFile) - 0x18)) - { - // True Fortune PS2 - streamSize = (read_32bitLE(0x24,streamFile) * 2) - 0x10; - vgmstream->num_samples = streamSize / 16 * 28 / vgmstream->channels; + if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + goto fail; + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} + +/* ****************************************************************************** */ + +static STREAMFILE* setup_subfile_streamfile(STREAMFILE *streamFile, off_t subfile_offset, size_t subfile_size, const char* fake_ext); + +/* ADS in containers */ +VGMSTREAM * init_vgmstream_ps2_ads_container(STREAMFILE *streamFile) { + VGMSTREAM *vgmstream = NULL; + STREAMFILE *temp_streamFile = NULL; + off_t subfile_offset; + size_t subfile_size; + + /* checks */ + if (!check_extensions(streamFile, "ads")) + goto fail; + + if (read_32bitBE(0x00,streamFile) == 0x41445343 && /* "ADSC" */ + read_32bitBE(0x04,streamFile) == 0x01000000) { + /* Kenka Bancho 2, Kamen Rider Hibiki/Kabuto, Shinjuku no Okami */ + subfile_offset = 0x08; } - else if(get_streamfile_size(streamFile) - read_32bitLE(0x24,streamFile) >= 0x800) - { - // Hack for files with start_offset = 0x800 - start_offset=0x800; + else if (read_32bitBE(0x00,streamFile) == 0x63617669 && /* "cavi" */ + read_32bitBE(0x04,streamFile) == 0x61207374 && /* "a st" */ + read_32bitBE(0x08,streamFile) == 0x7265616D) { /* "ream" */ + /* cavia games: Drakengard 1/2, Dragon Quest Yangus, GITS: Stand Alone Complex */ + subfile_offset = 0x7d8; + } + else { + goto fail; } - if((vgmstream->coding_type == coding_PSX) && (start_offset==0x28)) - { - start_offset=0x800; + subfile_size = get_streamfile_size(streamFile) - subfile_offset; - for(i=0;i<0x1f6;i+=4) - { - if(read_32bitLE(0x28+(i*4),streamFile)!=0) - { - start_offset=0x28; - break; - } - } - } + temp_streamFile = setup_subfile_streamfile(streamFile, subfile_offset,subfile_size, NULL); + if (!temp_streamFile) goto fail; - // check if we got a real pcm (ex: Clock Tower 3) - if(vgmstream->coding_type==coding_PCM16LE) - { - check_offset=start_offset; - do - { - if(read_8bit(check_offset+1,streamFile)>7) - { - isPCM=1; - break; - } - else - { - check_offset+=0x10; - } - - } while (check_offsetnum_samples=(get_streamfile_size(streamFile)-start_offset)/16*28/vgmstream->channels; - vgmstream->coding_type=coding_PSX; - } - } - - /* expect pcm format allways start @ 0x800, don't know if it's true :P */ - /*if(vgmstream->coding_type == coding_PCM16LE) - start_offset=0x800;*/ - - /* open the file for reading by each channel */ - { - for (i=0;ich[i].streamfile = streamFile->open(streamFile,filename,0x8000); - - if (!vgmstream->ch[i].streamfile) goto fail; - - vgmstream->ch[i].channel_start_offset= - vgmstream->ch[i].offset= - (off_t)(start_offset+vgmstream->interleave_block_size*i); - } - } + vgmstream = init_vgmstream_ps2_ads(temp_streamFile); + close_streamfile(temp_streamFile); return vgmstream; - /* clean up anything we may have opened */ fail: - if (vgmstream) close_vgmstream(vgmstream); + close_streamfile(temp_streamFile); + close_vgmstream(vgmstream); + return NULL; +} + +static STREAMFILE* setup_subfile_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/Frameworks/vgmstream/vgmstream/src/meta/ps2_adsc.c b/Frameworks/vgmstream/vgmstream/src/meta/ps2_adsc.c deleted file mode 100644 index ad6063c9e..000000000 --- a/Frameworks/vgmstream/vgmstream/src/meta/ps2_adsc.c +++ /dev/null @@ -1,63 +0,0 @@ -#include "meta.h" -#include "../util.h" - -/* ADSC (from Kenka Bancho 2: Full Throttle) */ -VGMSTREAM * init_vgmstream_ps2_adsc(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; - off_t start_offset; - - int loop_flag; - int channel_count; - - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("ads",filename_extension(filename))) goto fail; - - /* check header */ - if (read_32bitBE(0x00,streamFile) != 0x41445343) /* ADSC */ - goto fail; - - loop_flag = 0; - channel_count = read_32bitLE(0x18,streamFile); - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); - if (!vgmstream) goto fail; - - /* fill in the vital statistics */ - start_offset = 0x1000; - vgmstream->channels = channel_count; - vgmstream->sample_rate = read_32bitLE(0x14,streamFile); - vgmstream->coding_type = coding_PSX; - if(read_32bitLE(0x18,streamFile)==0x01) - vgmstream->num_samples = read_32bitLE(0x2c,streamFile)*56/32; - else - vgmstream->num_samples = read_32bitLE(0x2c,streamFile)*28/32; - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = read_32bitLE(0x1c,streamFile); - vgmstream->meta_type = meta_PS2_ADSC; - - /* 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/Frameworks/vgmstream/vgmstream/src/meta/ps2_joe.c b/Frameworks/vgmstream/vgmstream/src/meta/ps2_joe.c index 5590cd4e8..988a10c43 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ps2_joe.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ps2_joe.c @@ -34,11 +34,9 @@ VGMSTREAM * init_vgmstream_ps2_joe(STREAMFILE *streamFile) { } else if (data_size == file_size - 0x10 && unknown1 == 0xCCCCCCCC && unknown2 == 0xCCCCCCCC) { /* The Mummy: The Animated Series */ - data_size = data_size / 2; interleave = 0x8000; } else if (data_size == file_size - 0x4020) { /* CT Special Forces (and all games beyond) */ - data_size = data_size / 2; interleave = unknown1; /* always 0? */ if (!interleave) interleave = 0x10; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ps2_mib.c b/Frameworks/vgmstream/vgmstream/src/meta/ps2_mib.c index dfb23f4f9..b3de59893 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ps2_mib.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ps2_mib.c @@ -4,7 +4,7 @@ static int check_psadpcm(STREAMFILE *streamFile); -/* MIB+MIH - from namCollection: Ace Combat 2 (PS2), Rampage - Total Destruction (PS2) */ +/* MIB+MIH - SCEE MultiStream interleaved bank (header+data) [namCollection: Ace Combat 2 (PS2), Rampage: Total Destruction (PS2)] */ /* headerless PS-ADPCM - from Katamary Damacy (PS2), Air (PS2), Aladdin: Nasira's Revenge (PS1) * (guesses interleave and channels by testing data and using the file extension, and finds * loops in PS-ADPCM flags; this is a crutch for convenience, consider using GENH/TXTH instead). */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ps2_mihb.c b/Frameworks/vgmstream/vgmstream/src/meta/ps2_mihb.c index 9f3118db7..c5d6f4f84 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ps2_mihb.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ps2_mihb.c @@ -1,7 +1,7 @@ #include "meta.h" #include "../coding/coding.h" -/* MIC/MIHB - Merged MIH+MIB [Rogue Trooper (PS2), The Sims 2 (PS2)] */ +/* MIC/MIHB - SCEE MultiStream interleaved bank (merged MIH+MIB) [Rogue Trooper (PS2), The Sims 2 (PS2)] */ VGMSTREAM * init_vgmstream_ps2_mihb(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; off_t start_offset; @@ -9,7 +9,7 @@ VGMSTREAM * init_vgmstream_ps2_mihb(STREAMFILE *streamFile) { int channel_count, loop_flag; /* check extension */ - /* .mic: Rebellion Dev. games, .mihb: assumed? */ + /* .mic: official extension, .mihb: assumed? */ if (!check_extensions(streamFile, "mic,mihb")) goto fail; if (read_32bitBE(0x00,streamFile) != 0x40000000) /* header size */ @@ -21,7 +21,7 @@ VGMSTREAM * init_vgmstream_ps2_mihb(STREAMFILE *streamFile) { /* frame_size * frame_count * channels = data_size, but last frame has less usable data */ { - /* 0x04(1): 0x20? */ + /* 0x04: padding (0x20, MIH header must be multiple of 0x40) */ frame_last = (uint16_t)read_16bitLE(0x05,streamFile); frame_size = read_32bitLE(0x10,streamFile); frame_count = read_32bitLE(0x14,streamFile); diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ubi_jade.c b/Frameworks/vgmstream/vgmstream/src/meta/ubi_jade.c index 479063989..dbeefaef0 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ubi_jade.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ubi_jade.c @@ -122,7 +122,8 @@ VGMSTREAM * init_vgmstream_ubi_jade(STREAMFILE *streamFile) { switch(codec) { case 0x0069: /* Xbox */ - if (fmt_size != 0x12) goto fail; + /* Peter Jackson's King Kong uses 0x14 (other versions don't) */ + if (fmt_size != 0x12 && fmt_size != 0x14) goto fail; if (block_size != 0x24*channel_count) goto fail; vgmstream->coding_type = coding_XBOX_IMA; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ubi_lyn.c b/Frameworks/vgmstream/vgmstream/src/meta/ubi_lyn.c index 9d4c934a6..f63116ad6 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ubi_lyn.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ubi_lyn.c @@ -64,8 +64,9 @@ VGMSTREAM * init_vgmstream_ubi_lyn(STREAMFILE *streamFile) { } } - /* most songs simply repeat, loop if it looks long enough */ - loop_flag = (num_samples > 20*sample_rate); /* in seconds */ + /* most songs simply repeat; loop if it looks long enough, + * but not too long (ex. Michael Jackson The Experience songs) */ + loop_flag = (num_samples > 20*sample_rate && num_samples < 60*3*sample_rate); /* in seconds */ start_offset = data_offset; @@ -246,6 +247,10 @@ VGMSTREAM * init_vgmstream_ubi_lyn_container(STREAMFILE *streamFile) { read_32bitBE(0x14,streamFile) == 0x52494646) { /* "RIFF" */ subfile_offset = 0x14; /* Adventures of Tintin */ } + else if (read_32bitBE(0x20,streamFile) == 0x4C795345 && /* "LySE" */ + read_32bitBE(0x34,streamFile) == 0x52494646) { /* "RIFF" */ + subfile_offset = 0x34; /* Michael Jackson The Experience (Wii) */ + } 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 */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/wwise.c b/Frameworks/vgmstream/vgmstream/src/meta/wwise.c index 4871a53f7..f7653461b 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/wwise.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/wwise.c @@ -417,7 +417,8 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) { off_t xma2_offset; size_t xma2_size; - if (ww.fmt_size != 0x20 && ww.fmt_size != 0x34 && ww.fmt_size != 0x40) goto fail; /* XMA1, XMA2old, XMA2new */ + /* endian check should be enough */ + //if (ww.fmt_size != ...) goto fail; /* XMA1 0x20, XMA2old: 0x34, XMA2new: 0x40, XMA2 Guitar Hero Live/padded: 0x64, etc */ if (!ww.big_endian) goto fail; /* must be Wwise (real XMA are LE and parsed elsewhere) */ if (find_chunk(streamFile, 0x584D4132,first_offset,0, &xma2_offset,&xma2_size, ww.big_endian, 0)) { /*"XMA2"*/ /* older Wwise */ diff --git a/Frameworks/vgmstream/vgmstream/src/vgmstream.c b/Frameworks/vgmstream/vgmstream/src/vgmstream.c index 0679eddec..1c9a6fbf2 100644 --- a/Frameworks/vgmstream/vgmstream/src/vgmstream.c +++ b/Frameworks/vgmstream/vgmstream/src/vgmstream.c @@ -263,7 +263,6 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_dsp_ddsp, init_vgmstream_p3d, init_vgmstream_ps2_tk1, - init_vgmstream_ps2_adsc, init_vgmstream_ngc_dsp_mpds, init_vgmstream_dsp_str_ig, init_vgmstream_ea_swvr, @@ -411,6 +410,7 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_dsp_switch_audio, init_vgmstream_dsp_sadf, init_vgmstream_h4m, + init_vgmstream_ps2_ads_container, init_vgmstream_txth, /* should go at the end (lower priority) */ #ifdef VGM_USE_FFMPEG diff --git a/Frameworks/vgmstream/vgmstream/src/vgmstream.h b/Frameworks/vgmstream/vgmstream/src/vgmstream.h index 02452edc1..b88373250 100644 --- a/Frameworks/vgmstream/vgmstream/src/vgmstream.h +++ b/Frameworks/vgmstream/vgmstream/src/vgmstream.h @@ -541,7 +541,6 @@ typedef enum { meta_PC_SMP, /* Ghostbusters PC .smp */ meta_P3D, /* Prototype P3D */ meta_PS2_TK1, /* Tekken (NamCollection) */ - meta_PS2_ADSC, /* Kenka Bancho 2: Full Throttle */ meta_NGC_RKV, /* Legacy of Kain - Blood Omen 2 (GC) */ meta_DSP_DDSP, /* Various (2 dsp files stuck together */ meta_NGC_DSP_MPDS, /* Big Air Freestyle, Terminator 3 */