diff --git a/src/Makefile b/src/Makefile index 0411eabb..fcdc24ba 100644 --- a/src/Makefile +++ b/src/Makefile @@ -305,7 +305,8 @@ META_OBJS=meta/adx_header.o \ meta/btsnd.o \ meta/hca.o \ meta/ps2_svag_snk.o \ - meta/ffmpeg.o + meta/ffmpeg.o \ + meta/mp4.o EXT_LIBS = ../ext_libs/clHCA.o diff --git a/src/meta/Makefile.unix.am b/src/meta/Makefile.unix.am index 85f61a6f..b6fb0f52 100644 --- a/src/meta/Makefile.unix.am +++ b/src/meta/Makefile.unix.am @@ -246,5 +246,6 @@ libmeta_la_SOURCES += mca.c libmeta_la_SOURCES += btsnd.c libmeta_la_SOURCES += hca.c libmeta_la_SOURCES += ps2_svag_snk.c +libmeta_la_SOURCES += mp4.c EXTRA_DIST = meta.h diff --git a/src/meta/hca.c b/src/meta/hca.c index 574f8537..93232b56 100644 --- a/src/meta/hca.c +++ b/src/meta/hca.c @@ -9,9 +9,8 @@ VGMSTREAM * init_vgmstream_hca(STREAMFILE *streamFile) { } VGMSTREAM * init_vgmstream_hca_offset(STREAMFILE *streamFile, uint64_t start, uint64_t size) { - /* These I don't know about... */ - static const unsigned int ciphKey1=0x30DBE1AB; - static const unsigned int ciphKey2=0xCC554639; + unsigned int ciphKey1; + unsigned int ciphKey2; char filename[PATH_LIMIT]; @@ -51,6 +50,20 @@ VGMSTREAM * init_vgmstream_hca_offset(STREAMFILE *streamFile, uint64_t start, ui hca = (clHCA *)(hca_file + 1); + /* try to find key in external file */ + { + uint8_t keybuf[8]; + + if ( read_key_file(keybuf, 8, streamFile) ) { + ciphKey2 = get_32bitBE(keybuf+0); + ciphKey1 = get_32bitBE(keybuf+4); + } else { + /* PSO2 */ + ciphKey2=0xCC554639; + ciphKey1=0x30DBE1AB; + } + } + clHCA_clear(hca, ciphKey1, ciphKey2); if (clHCA_Decode(hca, hca_data, header_size, 0) < 0) goto fail; diff --git a/src/meta/meta.h b/src/meta/meta.h index f777440c..8a9c6767 100644 --- a/src/meta/meta.h +++ b/src/meta/meta.h @@ -125,6 +125,8 @@ void free_ffmpeg(ffmpeg_codec_data *); VGMSTREAM * init_vgmstream_ffmpeg_offset(STREAMFILE *streamFile, uint64_t start, uint64_t size); VGMSTREAM * init_vgmstream_ffmpeg(STREAMFILE *streamFile); + +VGMSTREAM * init_vgmstream_mp4_aac_ffmpeg(STREAMFILE * streamFile); #endif #if defined(VGM_USE_MP4V2) && defined(VGM_USE_FDKAAC) @@ -587,7 +589,7 @@ VGMSTREAM * init_vgmstream_nub_vag(STREAMFILE* streamFile); VGMSTREAM * init_vgmstream_ps3_past(STREAMFILE* streamFile); -VGMSTREAM * init_vgmstream_ps3_sgh_sgb(STREAMFILE* streamFile); +VGMSTREAM * init_vgmstream_ps3_sgdx(STREAMFILE* streamFile); VGMSTREAM * init_vgmstream_ngca(STREAMFILE* streamFile); @@ -621,16 +623,12 @@ VGMSTREAM * init_vgmstream_eb_sf0(STREAMFILE* streamFile); VGMSTREAM * init_vgmstream_ps3_klbs(STREAMFILE* streamFile); -VGMSTREAM * init_vgmstream_ps3_sgx(STREAMFILE* streamFile); - VGMSTREAM * init_vgmstream_ps2_mtaf(STREAMFILE* streamFile); VGMSTREAM * init_vgmstream_tun(STREAMFILE* streamFile); VGMSTREAM * init_vgmstream_wpd(STREAMFILE* streamFile); -VGMSTREAM * init_vgmstream_ps3_sgd(STREAMFILE* streamFile); - VGMSTREAM * init_vgmstream_mn_str(STREAMFILE* streamFile); VGMSTREAM * init_vgmstream_ps2_mss(STREAMFILE* streamFile); diff --git a/src/meta/mp4.c b/src/meta/mp4.c index dbee41f6..21fe89ec 100644 --- a/src/meta/mp4.c +++ b/src/meta/mp4.c @@ -161,3 +161,79 @@ fail: } #endif #endif + + +#ifdef VGM_USE_FFMPEG + +VGMSTREAM * init_vgmstream_mp4_aac_ffmpeg(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + char filename[PATH_LIMIT]; + off_t start_offset = 0; + int loop_flag = 0; + int32_t num_samples = 0, loop_start_sample = 0, loop_end_sample = 0; + + ffmpeg_codec_data *ffmpeg_data = NULL; + + + /* check extension, case insensitive */ + streamFile->get_name(streamFile,filename,sizeof(filename)); + if ( strcasecmp("mp4",filename_extension(filename)) + && strcasecmp("m4a",filename_extension(filename)) + && strcasecmp("m4v",filename_extension(filename)) + && strcasecmp("bin",filename_extension(filename)) ) /* Final Fantasy Dimensions iOS */ + goto fail; + + + /* check header for Final Fantasy Dimensions */ + if (read_32bitBE(0x00,streamFile) == 0x4646444C) { /* "FFDL" (any kind of FFD file) */ + if (read_32bitBE(0x04,streamFile) == 0x6D747873) { /* "mtxs" (bgm file) */ + num_samples = read_32bitLE(0x08,streamFile); + loop_start_sample = read_32bitLE(0x0c,streamFile); + loop_end_sample = read_32bitLE(0x10,streamFile); + loop_flag = !(loop_start_sample==0 && loop_end_sample==num_samples); + start_offset = 0x14; + } else { + start_offset = 0x4; /* some SEs */ + } + /* todo some FFDL have multi streams ("FFLD" + mtxsdata1 + mp4data1 + mtxsdata2 + mp4data2 + etc) */ + } + + + /* check header */ + if ( read_32bitBE(start_offset+0x04,streamFile) != 0x66747970) /* size 0x00 + "ftyp" 0x04 */ + goto fail; + + ffmpeg_data = init_ffmpeg_offset(streamFile, start_offset, streamFile->get_size(streamFile)); + if ( !ffmpeg_data ) goto fail; + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(ffmpeg_data->channels,loop_flag); + if (!vgmstream) goto fail; + + vgmstream->num_samples = ffmpeg_data->totalFrames; /* todo compare with FFD num_samples*/ + vgmstream->sample_rate = ffmpeg_data->sampleRate; + vgmstream->channels = ffmpeg_data->channels; + if (loop_flag) { + vgmstream->loop_start_sample = loop_start_sample; + vgmstream->loop_end_sample = loop_end_sample; + } + + vgmstream->coding_type = coding_FFmpeg; + vgmstream->layout_type = layout_none; + vgmstream->meta_type = meta_FFmpeg; + vgmstream->codec_data = ffmpeg_data; + + + return vgmstream; + +fail: + /* clean up anything we may have opened */ + if (ffmpeg_data) { + free_ffmpeg(ffmpeg_data); + if (vgmstream) vgmstream->codec_data = NULL; + } + if (vgmstream) close_vgmstream(vgmstream); + return NULL; +} + +#endif diff --git a/src/meta/ps3_msf.c b/src/meta/ps3_msf.c index 4cdacdc4..b599a037 100644 --- a/src/meta/ps3_msf.c +++ b/src/meta/ps3_msf.c @@ -144,7 +144,7 @@ VGMSTREAM * init_vgmstream_ps3_msf(STREAMFILE *streamFile) { break; #endif -#if defined(VGM_USE_FFMPEG) && !defined(VGM_USE_MPEG) +#ifdef VGM_USE_FFMPEG case 0x7: /* MPEG */ /* delegate to FFMpeg, it can parse MSF files */ ffmpeg_data = init_ffmpeg_offset(streamFile, header_offset, streamFile->get_size(streamFile) ); @@ -155,7 +155,7 @@ VGMSTREAM * init_vgmstream_ps3_msf(STREAMFILE *streamFile) { vgmstream->meta_type = meta_FFmpeg; vgmstream->codec_data = ffmpeg_data; - /* todo check CBR better (bitrate=0?) */ + /* todo check CBR better (frame_size=0?) */ /* vgmstream->num_samples = ffmpeg_data->totalFrames; */ /* duration is not set/innacurate for MP3 in FFMpeg */ vgmstream->num_samples = (int64_t)data_size * ffmpeg_data->sampleRate * 8 / ffmpeg_data->bitrate; @@ -171,9 +171,11 @@ VGMSTREAM * init_vgmstream_ps3_msf(STREAMFILE *streamFile) { break; #endif -#ifdef VGM_USE_MPEG +#if defined(VGM_USE_MPEG) && !defined(VGM_USE_FFMPEG) case 0x7: /* MPEG */ { + int frame_size = 576; /* todo incorrect looping calcs, MP3 can have other sizes */ + mpeg_codec_data *mpeg_data = NULL; struct mpg123_frameinfo mi; coding_t ct; @@ -188,12 +190,12 @@ VGMSTREAM * init_vgmstream_ps3_msf(STREAMFILE *streamFile) { vgmstream->layout_type = layout_mpeg; if (mi.vbr != MPG123_CBR) goto fail; vgmstream->num_samples = mpeg_bytes_to_samples(data_size, &mi); - vgmstream->num_samples -= vgmstream->num_samples%576; + vgmstream->num_samples -= vgmstream->num_samples % frame_size; if (loop_flag) { vgmstream->loop_start_sample = mpeg_bytes_to_samples(loop_start, &mi); - vgmstream->loop_start_sample -= vgmstream->loop_start_sample%576; + vgmstream->loop_start_sample -= vgmstream->loop_start_sample % frame_size; vgmstream->loop_end_sample = mpeg_bytes_to_samples(loop_end, &mi); - vgmstream->loop_end_sample -= vgmstream->loop_end_sample%576; + vgmstream->loop_end_sample -= vgmstream->loop_end_sample % frame_size; } vgmstream->interleave_block_size = 0; } diff --git a/src/meta/ps3_sgh_sgb.c b/src/meta/ps3_sgh_sgb.c index a99348a6..e299df3b 100644 --- a/src/meta/ps3_sgh_sgb.c +++ b/src/meta/ps3_sgh_sgb.c @@ -1,200 +1,334 @@ #include "meta.h" #include "../util.h" -/* SGH+SGB (from Folklore) */ -VGMSTREAM * init_vgmstream_ps3_sgh_sgb(STREAMFILE *streamFile) { + +/* utils to fix AT3 looping */ +typedef struct { + int32_t fact_samples; + int32_t loop_start_sample; + int32_t loop_end_sample; + int32_t skip_samples; +} at3_riff_info; +static int get_at3_riff_info(at3_riff_info* info, STREAMFILE *streamFile, int32_t offset); + + +/* Sony's SGB+SGH / SGD / SGX (variations of the same format) + * PS3: Genji (SGX only), Folklore, Afrika, Tokyo Jungle + * PSP: Brave Story, Sarugetchu Sarusaru Daisakusen, Kurohyo 1/2 + * + * Contains header + chunks, usually: + * WAVE: stream(s) header of ADPCM, AC3, ATRAC3plus, etc + * NAME: stream name(s) + * WSUR, WMRK, BUSS: unknown + * RGND, SEQD: unknown (related to SE) + * Then data, containing the original header if applicable (ex. AT3 RIFF). + * The SGDX header has priority over it (ex. some ATRAC3plus files have 48000 while the data RIFF 44100) + */ +VGMSTREAM * init_vgmstream_ps3_sgdx(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; - off_t start_offset = 0; - STREAMFILE * streamFileSGH = NULL; + STREAMFILE * streamHeader = NULL; char filename[PATH_LIMIT]; - char filenameSGH[PATH_LIMIT]; - int channel_count; - int loop_flag; + + off_t start_offset, data_offset; + + int i; + int is_sgx, is_sgd, is_sgb; + + int chunk_offset; + int total_streams; + int target_stream = 0; /* usually only SE use substreams */ + +#ifdef VGM_USE_FFMPEG + ffmpeg_codec_data *ffmpeg_data = NULL; +#endif + /* check extension, case insensitive */ streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("sgb",filename_extension(filename))) goto fail; - - strcpy(filenameSGH,filename); - strcpy(filenameSGH+strlen(filenameSGH)-3,"sgh"); - - streamFileSGH = streamFile->open(streamFile,filenameSGH,STREAMFILE_DEFAULT_BUFFER_SIZE); - if (!streamFileSGH) goto fail; - - /* check header */ - if (read_32bitBE(0x00,streamFileSGH) != 0x53475844) /* "SGXD" */ + is_sgx = strcasecmp("sgx",filename_extension(filename))==0; + is_sgd = strcasecmp("sgd",filename_extension(filename))==0; + is_sgb = strcasecmp("sgb",filename_extension(filename))==0; + if ( !(is_sgx || is_sgd || is_sgb) ) goto fail; - channel_count = read_8bit(0x29,streamFileSGH); - if (read_32bitBE(0x44,streamFileSGH)==0xFFFFFFFF) - loop_flag = 0; - else - loop_flag = 1; - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); - if (!vgmstream) goto fail; + /* SGB+SGH: use SGH as header; otherwise use the current file as header */ + if (is_sgb) { + char fileheader[PATH_LIMIT]; - /* fill in the vital statistics */ - vgmstream->channels = channel_count; - vgmstream->sample_rate = read_32bitLE(0x2C,streamFileSGH); - vgmstream->num_samples = read_32bitLE(0xC,streamFileSGH)*28/32; - vgmstream->coding_type = coding_PSX; - if(loop_flag) { - vgmstream->loop_start_sample = read_32bitLE(0x44,streamFileSGH); - vgmstream->loop_end_sample = read_32bitLE(0x48,streamFileSGH); - } + strcpy(fileheader,filename); + strcpy(fileheader+strlen(fileheader)-3,"sgh"); + + streamHeader = streamFile->open(streamFile,fileheader,STREAMFILE_DEFAULT_BUFFER_SIZE); + if (!streamHeader) goto fail; + } else { + streamHeader = streamFile; + } - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = 0x10; - vgmstream->meta_type = meta_PS3_SGH_SGB; + /* SGXD chunk (size 0x10) */ + if (read_32bitBE(0x00,streamHeader) != 0x53475844) /* "SGXD" */ + goto fail; + /* 0x04 SGX: full header_size; SGD/SGH: unknown header_size (counting from 0x0/0x8/0x10, varies) */ + /* 0x08 SGX: first chunk offset? (0x10); SGD/SGH: full header_size */ + /* 0x0c SGX/SGH: full data size with padding; SGD: full data size + 0x80000000 with padding */ + if (is_sgb) { + data_offset = 0x00; + } else if ( is_sgx ) { + data_offset = read_32bitLE(0x04,streamHeader); + } else { + data_offset = read_32bitLE(0x08,streamHeader); + } - /* open the file for reading */ + + chunk_offset = 0x10; + /* WAVE chunk (size 0x10 + files * 0x38 + optional padding) */ + /* the format reads chunks until header_size, but we only want WAVE in the first position meaning BGM */ + if (read_32bitBE(chunk_offset+0x00,streamHeader) != 0x57415645) /* "WAVE" */ + goto fail; + /* 0x04 SGX: unknown; SGD/SGH: chunk length */ + /* 0x08 null */ + + /* usually only SE containers have multiple streams but just in case... */ + total_streams = read_32bitLE(chunk_offset+0x0c,streamHeader); + if (target_stream+1 > total_streams) + goto fail; + + /* read stream (skip until target_stream) */ + chunk_offset += 0x10; { - int i; - STREAMFILE * file; - file = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); - if (!file) goto fail; - for (i=0;ich[i].streamfile = file; + int stream_loop_flag; + int stream_type; + int stream_channels; + int32_t stream_sample_rate; + int32_t stream_num_samples, stream_loop_start_sample, stream_loop_end_sample; + off_t stream_start_offset; + int32_t stream_size; - vgmstream->ch[i].channel_start_offset= - vgmstream->ch[i].offset=start_offset+ - vgmstream->interleave_block_size*i; + for (i=0; i < total_streams; i++) { + if (i != target_stream) { + chunk_offset += 0x38; /* next file */ + continue; + } + /* 0x00 ? (00/01/02) */ + /* 0x04 sometimes global offset to wave_name */ + stream_type = read_8bit(chunk_offset+0x08,streamHeader); + stream_channels = read_8bit(chunk_offset+0x09,streamHeader); + /* 0x0a null */ + stream_sample_rate = read_32bitLE(chunk_offset+0x0c,streamHeader); + + /* 0x10 info_type: meaning of the next value + * (00=null, 30/40=data size without padding (ADPCM, ATRAC3plus), 80/A0=block size (AC3) */ + /* 0x14 info_value (see above) */ + /* 0x18 unknown (ex. 0x0008/0010/3307/CC02/etc)x2 */ + /* 0x1c null */ + + stream_num_samples = read_32bitLE(chunk_offset+0x20,streamHeader); + stream_loop_start_sample = read_32bitLE(chunk_offset+0x24,streamHeader); + stream_loop_end_sample = read_32bitLE(chunk_offset+0x28,streamHeader); + stream_size = read_32bitLE(chunk_offset+0x2c,streamHeader); /* stream size (without padding) / interleave (for type3) */ + + if (is_sgx) { + stream_start_offset = 0x0; /* TODO unknown (not seen multi SGX) */ + } else{ + stream_start_offset = read_32bitLE(chunk_offset+0x30,streamHeader); + } + /* 0x34 SGX: unknown; SGD/SGH: stream size (with padding) / interleave */ + + stream_loop_flag = stream_loop_start_sample!=0xffffffff && stream_loop_end_sample!=0xffffffff; + chunk_offset += 0x38; /* next file */ + + break; + } + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(stream_channels,stream_loop_flag); + if (!vgmstream) goto fail; + + /* fill in the vital statistics */ + vgmstream->num_samples = stream_num_samples; + vgmstream->sample_rate = stream_sample_rate; + vgmstream->channels = stream_channels; + if (stream_loop_flag) { + vgmstream->loop_start_sample = stream_loop_start_sample; + vgmstream->loop_end_sample = stream_loop_end_sample; + } + + vgmstream->meta_type = meta_PS3_SGDX; + + switch (stream_type) { + case 0x03: /* PSX ADPCM */ + vgmstream->coding_type = coding_PSX; + vgmstream->layout_type = layout_interleave; + if (is_sgx) { + vgmstream->interleave_block_size = 0x10; + } else { + vgmstream->interleave_block_size = stream_size; + } + + break; + +#ifdef VGM_USE_FFMPEG + case 0x04: /* ATRAC3plus */ + { + at3_riff_info info; + + ffmpeg_data = init_ffmpeg_offset(streamFile, data_offset, streamFile->get_size(streamFile)); + if ( !ffmpeg_data ) goto fail; + + vgmstream->coding_type = coding_FFmpeg; + vgmstream->layout_type = layout_none; + /*vgmstream->meta_type = meta_FFmpeg;*/ + vgmstream->codec_data = ffmpeg_data; + + /* manually fix looping due to FFmpeg bugs */ + if (stream_loop_flag && get_at3_riff_info(&info, streamFile, data_offset)) { + if (vgmstream->num_samples == info.fact_samples) { /* use if looks normal */ + /* todo use "skip samples"; for now we just use absolute loop values */ + vgmstream->loop_start_sample = info.loop_start_sample; + vgmstream->loop_end_sample = info.loop_end_sample; + vgmstream->num_samples += info.skip_samples; /* to ensure it always reaches loop_end */ + } + } + + break; + } +#endif + + case 0x05: /* todo PCM? */ + goto fail; + + /* + vgmstream->coding_type = coding_PCM16LE; + if (vgmstream->channels > 1) { + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x1; + } else { + vgmstream->layout_type = layout_none; + } + + break; + */ + +#ifdef VGM_USE_FFMPEG + case 0x06: /* AC3 */ + ffmpeg_data = init_ffmpeg_offset(streamFile, data_offset, streamFile->get_size(streamFile)); + if ( !ffmpeg_data ) goto fail; + + vgmstream->coding_type = coding_FFmpeg; + vgmstream->layout_type = layout_none; + /*vgmstream->meta_type = meta_FFmpeg;*/ + vgmstream->codec_data = ffmpeg_data; + + break; +#endif + + default: + goto fail; + } + + + start_offset = data_offset + stream_start_offset; + /* 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; i < vgmstream->channels; i++) { + vgmstream->ch[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 (streamFileSGH) close_streamfile(streamFileSGH); - if (vgmstream) close_vgmstream(vgmstream); +#ifdef VGM_USE_FFMPEG + if (ffmpeg_data) { + free_ffmpeg(ffmpeg_data); + vgmstream->codec_data = NULL; + } +#endif + if (is_sgb && streamHeader) close_streamfile(streamHeader); + if (vgmstream) close_vgmstream(vgmstream); return NULL; } -VGMSTREAM * init_vgmstream_ps3_sgx(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("sgx",filename_extension(filename))) goto fail; +/** + * AT3 RIFF headers have a "skip samples at the beginning" value that the decoder should use, + * and absolute loop values. However the SGDX header loop values assume those samples are skipped. + * + * FFmpeg doesn't support/export this, so we have to manually get the absolute values to fix looping. + */ +static int get_at3_riff_info(at3_riff_info* info, STREAMFILE *streamFile, int32_t offset) { + off_t current_chunk, riff_size; + int data_found = 0; - /* check header */ - if (read_32bitBE(0x00,streamFile) != 0x53475844) /* "SGXD" */ + memset(info, 0, sizeof(at3_riff_info)); + + if (read_32bitBE(offset+0x0,streamFile)!=0x52494646 /* "RIFF" */ + && read_32bitBE(offset+0x8,streamFile)!=0x57415645 ) /* "WAVE" */ goto fail; - loop_flag = (read_32bitLE(0x44,streamFile) != 0xFFFFFFFF); - channel_count = read_8bit(0x29,streamFile); - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); - if (!vgmstream) goto fail; - /* fill in the vital statistics */ - start_offset = read_32bitLE(0x4,streamFile); - vgmstream->channels = channel_count; - vgmstream->sample_rate = read_32bitLE(0x2C,streamFile); - vgmstream->coding_type = coding_PSX; - vgmstream->num_samples = read_32bitLE(0xC,streamFile)/16/channel_count*28; - if (loop_flag) { - vgmstream->loop_start_sample = read_32bitLE(0x44,streamFile); - vgmstream->loop_end_sample = read_32bitLE(0x48,streamFile); - } + /* read chunks */ + riff_size = read_32bitLE(offset+0x4,streamFile); + current_chunk = offset + 0xc; - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = read_32bitLE(0x8,streamFile); - vgmstream->meta_type = meta_PS3_SGX; + while (!data_found && current_chunk < riff_size) { + uint32_t chunk_type = read_32bitBE(current_chunk,streamFile); + off_t chunk_size = read_32bitLE(current_chunk+4,streamFile); - /* 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; + switch(chunk_type) { + case 0x736D706C: /* smpl */ + if (read_32bitLE(current_chunk+0x24, streamFile)==0 + || read_32bitLE(current_chunk+0x2c+0x4, streamFile)!=0 ) + goto fail; - vgmstream->ch[i].channel_start_offset= - vgmstream->ch[i].offset=start_offset+ - vgmstream->interleave_block_size*i; + info->loop_start_sample = read_32bitLE(current_chunk+0x2c+0x8, streamFile); + info->loop_end_sample = read_32bitLE(current_chunk+0x2c+0xc,streamFile); + break; + case 0x66616374: /* fact */ + if (chunk_size == 0x8) { + info->fact_samples = read_32bitLE(current_chunk+0x8, streamFile); + info->skip_samples = read_32bitLE(current_chunk+0xc, streamFile); + } else if (chunk_size == 0xc) { + info->fact_samples = read_32bitLE(current_chunk+0x8, streamFile); + info->skip_samples = read_32bitLE(current_chunk+0x10, streamFile); + } else { + goto fail; + } + break; + + case 0x64617461: /* data */ + data_found = 1; + break; + + default: + break; } + + current_chunk += 8+chunk_size; } - return vgmstream; - - /* clean up anything we may have opened */ -fail: - if (vgmstream) close_vgmstream(vgmstream); - return NULL; -} - - -VGMSTREAM * init_vgmstream_ps3_sgd(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("sgd",filename_extension(filename))) goto fail; - - /* check header */ - if (read_32bitBE(0x00,streamFile) != 0x53475844) /* "SGXD" */ + if (!data_found) goto fail; - loop_flag = (read_32bitLE(0x44,streamFile) != 0xFFFFFFFF); - channel_count = read_8bit(0x29,streamFile); - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); - if (!vgmstream) goto fail; - /* fill in the vital statistics */ - start_offset = read_32bitLE(0x8,streamFile); - vgmstream->channels = channel_count; - vgmstream->sample_rate = read_32bitLE(0x2C,streamFile); - vgmstream->coding_type = coding_PSX; - vgmstream->num_samples = read_32bitLE(0x40,streamFile)/16/channel_count*28; - if (loop_flag) { - vgmstream->loop_start_sample = read_32bitLE(0x44,streamFile); - vgmstream->loop_end_sample = read_32bitLE(0x48,streamFile); - } + /* found */ + return 1; - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = read_8bit(0x39,streamFile); // just a guess, all of my samples seem to be 0x10 interleave - vgmstream->meta_type = meta_PS3_SGX; - - /* 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; + /* not found */ + return 0; + } diff --git a/src/meta/riff.c b/src/meta/riff.c index e7d292e0..4c448caa 100644 --- a/src/meta/riff.c +++ b/src/meta/riff.c @@ -220,6 +220,7 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { off_t file_size = -1; int sample_count = 0; int fact_sample_count = -1; + int fact_sample_skip = -1; off_t start_offset = -1; int loop_flag = 0; @@ -241,13 +242,15 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { /* Ubisoft sns */ int sns = 0; + /* Sony atrac3 / 3plus */ + int at3 = 0; + /* check extension, case insensitive */ streamFile->get_name(streamFile,filename,sizeof(filename)); if (strcasecmp("wav",filename_extension(filename)) && strcasecmp("lwav",filename_extension(filename)) -#if defined(VGM_USE_MAIATRAC3PLUS) || defined(VGM_USE_FFMPEG) - && strcasecmp("at3",filename_extension(filename)) - && strcasecmp("sgb",filename_extension(filename)) +#ifndef VGM_USE_FFMPEG + && strcasecmp("sgb",filename_extension(filename)) /* SGB has proper support with FFmpeg in ps3_sgdx */ #endif ) { @@ -255,6 +258,10 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { mwv = 1; else if (!strcasecmp("sns",filename_extension(filename))) sns = 1; +#if defined(VGM_USE_MAIATRAC3PLUS) || defined(VGM_USE_FFMPEG) + else if (!strcasecmp("at3",filename_extension(filename))) + at3 = 1; +#endif else goto fail; } @@ -348,9 +355,16 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { mwv_ctrl_offset = current_chunk; break; case 0x66616374: /* fact */ - if (chunk_size != 4 - && (!(sns && chunk_size == 0x10))) break; - fact_sample_count = read_32bitLE(current_chunk+8, streamFile); + if (sns && chunk_size == 0x10) { + fact_sample_count = read_32bitLE(current_chunk+0x8, streamFile); + } else if (at3 && chunk_size == 0x8) { + fact_sample_count = read_32bitLE(current_chunk+0x8, streamFile); + fact_sample_skip = read_32bitLE(current_chunk+0xc, streamFile); + } else if (at3 && chunk_size == 0xc) { + fact_sample_count = read_32bitLE(current_chunk+0x8, streamFile); + fact_sample_skip = read_32bitLE(current_chunk+0x10, streamFile); + } + break; default: /* ignorance is bliss */ @@ -388,18 +402,18 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { ffmpeg_data = init_ffmpeg_offset(streamFile, 0, streamFile->get_size(streamFile) ); if ( !ffmpeg_data ) goto fail; - sample_count = ffmpeg_data->totalFrames; + sample_count = ffmpeg_data->totalFrames; /* fact_sample_count */ + /* the encoder introduces some garbage (usually silent) samples to skip before the stream + * loop values include the skip samples but fact_sample_count doesn't; add them back to fix some edge loops */ + if (fact_sample_skip > 0) + sample_count += fact_sample_skip; } break; #endif #ifdef VGM_USE_MAIATRAC3PLUS case coding_AT3plus: - /* rough total samples, not totally accurate since there are some skipped samples in the first and (maybe) last frames - * channels shouldn't matter (mono and stereo encoding produces the same number of frames) - * - * to get the correct number of samples you'd need to read the fact_sample_count and skip some samples when decoding - * the first/last samples_to_skip seem related to the ints in the "fact" chunk - * those skipped samples are typically silent so there is not much difference */ + /* rough total samples, not totally accurate since there are some skipped samples in the beginning + * channels shouldn't matter (mono and stereo encoding produces the same number of frames in ATRAC3plus) */ sample_count = (data_size / fmt.block_size) * 2048; /* number_of_frames by decoded_samples_per_frame */ break; #endif @@ -573,7 +587,7 @@ fail: #ifdef VGM_USE_FFMPEG if (ffmpeg_data) { free_ffmpeg(ffmpeg_data); - vgmstream->codec_data = NULL; + if (vgmstream) vgmstream->codec_data = NULL; } #endif if (vgmstream) close_vgmstream(vgmstream); diff --git a/src/vgmstream.c b/src/vgmstream.c index 56e1d42e..0ed2ddb8 100644 --- a/src/vgmstream.c +++ b/src/vgmstream.c @@ -303,7 +303,7 @@ VGMSTREAM * (*init_vgmstream_fcns[])(STREAMFILE *streamFile) = { init_vgmstream_fsb_mpeg, init_vgmstream_nub_vag, init_vgmstream_ps3_past, - init_vgmstream_ps3_sgh_sgb, + init_vgmstream_ps3_sgdx, init_vgmstream_ngca, init_vgmstream_wii_ras, init_vgmstream_ps2_spm, @@ -320,11 +320,9 @@ VGMSTREAM * (*init_vgmstream_fcns[])(STREAMFILE *streamFile) = { init_vgmstream_eb_sfx, init_vgmstream_eb_sf0, init_vgmstream_ps3_klbs, - init_vgmstream_ps3_sgx, init_vgmstream_ps2_mtaf, init_vgmstream_tun, init_vgmstream_wpd, - init_vgmstream_ps3_sgd, init_vgmstream_mn_str, init_vgmstream_ps2_mss, init_vgmstream_ps2_hsf, @@ -341,6 +339,7 @@ VGMSTREAM * (*init_vgmstream_fcns[])(STREAMFILE *streamFile) = { init_vgmstream_hca, init_vgmstream_ps2_svag_snk, #ifdef VGM_USE_FFMPEG + init_vgmstream_mp4_aac_ffmpeg, init_vgmstream_ffmpeg, #endif }; @@ -3150,8 +3149,8 @@ void describe_vgmstream(VGMSTREAM * vgmstream, char * desc, int length) { case meta_PS3_PAST: snprintf(temp,TEMPSIZE,"SNDP header"); break; - case meta_PS3_SGH_SGB: - snprintf(temp,TEMPSIZE,"SGH+SGB SGXD header"); + case meta_PS3_SGDX: + snprintf(temp,TEMPSIZE,"SGXD header"); break; case meta_NGCA: snprintf(temp,TEMPSIZE,"NGCA header"); @@ -3204,9 +3203,6 @@ void describe_vgmstream(VGMSTREAM * vgmstream, char * desc, int length) { case meta_PS3_KLBS: snprintf(temp,TEMPSIZE,"klBS Header"); break; - case meta_PS3_SGX: - snprintf(temp,TEMPSIZE,"PS3 SGXD/WAVE header"); - break; case meta_PS2_MTAF: snprintf(temp,TEMPSIZE,"Konami MTAF header"); break; diff --git a/src/vgmstream.h b/src/vgmstream.h index 483105fd..74f33942 100644 --- a/src/vgmstream.h +++ b/src/vgmstream.h @@ -562,7 +562,7 @@ typedef enum { meta_PS3_MSF, /* MSF header */ meta_NUB_VAG, /* VAG from Nub archives */ meta_PS3_PAST, /* Bakugan Battle Brawlers (PS3) */ - meta_PS3_SGH_SGB, /* Folklore (PS3) */ + meta_PS3_SGDX, /* Folklore, Genji, Tokyo Jungle (PS3), Brave Story, Kurohyo (PSP) */ meta_NGCA, /* GoldenEye 007 (Wii) */ meta_WII_RAS, /* Donkey Kong Country Returns (Wii) */ meta_PS2_SPM, /* Lethal Skies Elite Pilot: Team SW */ @@ -580,7 +580,6 @@ typedef enum { meta_EB_SFX, // Excitebots .sfx meta_EB_SF0, // Excitebots .sf0 meta_PS3_KLBS, // L@VE ONCE (PS3) - meta_PS3_SGX, // Boku no Natsuyasumi 3 (PS3) meta_PS2_MTAF, // Metal Gear Solid 3 MTAF meta_PS2_VAG1, // Metal Gear Solid 3 VAG1 meta_PS2_VAG2, // Metal Gear Solid 3 VAG2