diff --git a/Frameworks/vgmstream/vgmstream/src/coding/coding.h b/Frameworks/vgmstream/vgmstream/src/coding/coding.h index 02e1142f9..400f2eb87 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/coding.h +++ b/Frameworks/vgmstream/vgmstream/src/coding/coding.h @@ -109,8 +109,11 @@ void decode_at3plus(VGMSTREAM *vgmstream, #endif #ifdef VGM_USE_FFMPEG -void decode_ffmpeg(VGMSTREAM *stream, - sample * outbuf, int32_t samples_to_do, int channels); +void decode_ffmpeg(VGMSTREAM *stream, sample * outbuf, int32_t samples_to_do, int channels); + +void reset_ffmpeg(VGMSTREAM *vgmstream); + +void seek_ffmpeg(VGMSTREAM *vgmstream, int32_t num_sample); #endif void decode_acm(ACMStream * acm, sample * outbuf, diff --git a/Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder.c index 1a8510cfb..2e5f6c719 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder.c @@ -59,18 +59,16 @@ static void convert_audio(sample *outbuf, const uint8_t *inbuf, int sampleCount, } } -void decode_ffmpeg(VGMSTREAM *vgmstream, - sample * outbuf, int32_t samples_to_do, int channels) { +void decode_ffmpeg(VGMSTREAM *vgmstream, sample * outbuf, int32_t samples_to_do, int channels) { ffmpeg_codec_data *data = (ffmpeg_codec_data *) vgmstream->codec_data; + int bytesPerSample; + int bytesPerFrame; int frameSize; - int dataSize; int bytesToRead; int bytesRead; - int errcode; - uint8_t *targetBuf; AVFormatContext *formatCtx; @@ -78,25 +76,24 @@ void decode_ffmpeg(VGMSTREAM *vgmstream, AVPacket *lastReadPacket; AVFrame *lastDecodedFrame; - int streamIndex; - int bytesConsumedFromDecodedFrame; int readNextPacket; int endOfStream; int endOfAudio; - int toConsume; - int framesReadNow; - if (data->totalFrames && data->framesRead >= data->totalFrames) { + + /* ignore decode attempts at EOF */ + if (data->endOfStream || data->endOfAudio) { memset(outbuf, 0, samples_to_do * channels * sizeof(sample)); return; } - frameSize = data->channels * (data->bitsPerSample / 8); - dataSize = 0; + bytesPerSample = data->bitsPerSample / 8; + bytesPerFrame = channels * bytesPerSample; + frameSize = data->channels * bytesPerSample; bytesToRead = samples_to_do * frameSize; bytesRead = 0; @@ -109,8 +106,6 @@ void decode_ffmpeg(VGMSTREAM *vgmstream, lastReadPacket = data->lastReadPacket; lastDecodedFrame = data->lastDecodedFrame; - streamIndex = data->streamIndex; - bytesConsumedFromDecodedFrame = data->bytesConsumedFromDecodedFrame; readNextPacket = data->readNextPacket; @@ -120,15 +115,18 @@ void decode_ffmpeg(VGMSTREAM *vgmstream, /* keep reading and decoding packets until the requested number of samples (in bytes) */ while (bytesRead < bytesToRead) { int planeSize; - int planar = av_sample_fmt_is_planar(codecCtx->sample_fmt); - dataSize = av_samples_get_buffer_size(&planeSize, codecCtx->channels, - lastDecodedFrame->nb_samples, - codecCtx->sample_fmt, 1); - + int planar; + int dataSize; + int toConsume; + int errcode; + + + /* size of previous frame */ + dataSize = av_samples_get_buffer_size(&planeSize, codecCtx->channels, lastDecodedFrame->nb_samples, codecCtx->sample_fmt, 1); if (dataSize < 0) dataSize = 0; - /* read packet */ + /* read new frame + packets when requested */ while (readNextPacket && !endOfAudio) { if (!endOfStream) { av_packet_unref(lastReadPacket); @@ -139,10 +137,11 @@ void decode_ffmpeg(VGMSTREAM *vgmstream, if (formatCtx->pb && formatCtx->pb->error) break; } - if (lastReadPacket->stream_index != streamIndex) - continue; /* ignore non audio streams */ + if (lastReadPacket->stream_index != data->streamIndex) + continue; /* ignore non-selected streams */ } + /* send compressed packet to decoder (NULL at EOF to "drain") */ if ((errcode = avcodec_send_packet(codecCtx, endOfStream ? NULL : lastReadPacket)) < 0) { if (errcode != AVERROR(EAGAIN)) { goto end; @@ -152,13 +151,14 @@ void decode_ffmpeg(VGMSTREAM *vgmstream, readNextPacket = 0; } - /* decode packet */ + /* decode packets into frame (checking if we have bytes to consume from previous frame) */ if (dataSize <= bytesConsumedFromDecodedFrame) { if (endOfStream && endOfAudio) break; bytesConsumedFromDecodedFrame = 0; + /* receive uncompressed data from decoder */ if ((errcode = avcodec_receive_frame(codecCtx, lastDecodedFrame)) < 0) { if (errcode == AVERROR_EOF) { endOfAudio = 1; @@ -173,46 +173,45 @@ void decode_ffmpeg(VGMSTREAM *vgmstream, } } + /* size of current frame */ dataSize = av_samples_get_buffer_size(&planeSize, codecCtx->channels, lastDecodedFrame->nb_samples, codecCtx->sample_fmt, 1); - if (dataSize < 0) dataSize = 0; } toConsume = FFMIN((dataSize - bytesConsumedFromDecodedFrame), (bytesToRead - bytesRead)); - /* discard packet if needed (fully or partially) */ + /* discard decoded frame if needed (fully or partially) */ if (data->samplesToDiscard) { - int samplesToConsume; - int bytesPerFrame = ((data->bitsPerSample / 8) * channels); + int samplesDataSize = dataSize / bytesPerFrame; - /* discard all if there are more samples to do than the packet's samples */ - if (data->samplesToDiscard >= dataSize / bytesPerFrame) { - samplesToConsume = dataSize / bytesPerFrame; - } - else { - samplesToConsume = toConsume / bytesPerFrame; - } + if (data->samplesToDiscard >= samplesDataSize) { + /* discard all of the frame's samples and continue to the next */ - if (data->samplesToDiscard >= samplesToConsume) { /* full discard: skip to next */ - data->samplesToDiscard -= samplesToConsume; bytesConsumedFromDecodedFrame = dataSize; + data->samplesToDiscard -= samplesDataSize; + continue; } - else { /* partial discard: copy below */ - bytesConsumedFromDecodedFrame += data->samplesToDiscard * bytesPerFrame; - toConsume -= data->samplesToDiscard * bytesPerFrame; + else { + /* discard part of the frame and copy the rest below */ + int bytesToDiscard = data->samplesToDiscard * bytesPerFrame; + int dataSizeLeft = dataSize - bytesToDiscard; + + bytesConsumedFromDecodedFrame += bytesToDiscard; data->samplesToDiscard = 0; + if (toConsume > dataSizeLeft) + toConsume = dataSizeLeft; /* consume at most dataSize left */ } } - /* copy packet to buffer (mux channels if needed) */ + /* copy decoded frame to buffer (mux channels if needed) */ + planar = av_sample_fmt_is_planar(codecCtx->sample_fmt); if (!planar || channels == 1) { memmove(targetBuf + bytesRead, (lastDecodedFrame->data[0] + bytesConsumedFromDecodedFrame), toConsume); } else { uint8_t * out = (uint8_t *) targetBuf + bytesRead; - int bytesPerSample = data->bitsPerSample / 8; int bytesConsumedPerPlane = bytesConsumedFromDecodedFrame / channels; int toConsumePerPlane = toConsume / channels; int s, ch; @@ -231,11 +230,6 @@ void decode_ffmpeg(VGMSTREAM *vgmstream, end: framesReadNow = bytesRead / frameSize; - if (data->totalFrames && (data->framesRead + framesReadNow > data->totalFrames)) { - framesReadNow = (int)(data->totalFrames - data->framesRead); - } - - data->framesRead += framesReadNow; // Convert the audio convert_audio(outbuf, data->sampleBuffer, framesReadNow * channels, data->bitsPerSample, data->floatingPoint); @@ -252,4 +246,70 @@ end: } } + +void reset_ffmpeg(VGMSTREAM *vgmstream) { + ffmpeg_codec_data *data = (ffmpeg_codec_data *) vgmstream->codec_data; + + if (data->formatCtx) { + avformat_seek_file(data->formatCtx, data->streamIndex, 0, 0, 0, AVSEEK_FLAG_ANY); + } + if (data->codecCtx) { + avcodec_flush_buffers(data->codecCtx); + } + data->readNextPacket = 1; + data->bytesConsumedFromDecodedFrame = INT_MAX; + data->endOfStream = 0; + data->endOfAudio = 0; + data->samplesToDiscard = 0; +} + + +void seek_ffmpeg(VGMSTREAM *vgmstream, int32_t num_sample) { + ffmpeg_codec_data *data = (ffmpeg_codec_data *) vgmstream->codec_data; + int64_t ts; + +#ifndef VGM_USE_FFMPEG_ACCURATE_LOOPING + /* Seek to loop start by timestamp (closest frame) + adjust skipping some samples */ + /* FFmpeg seeks by ts by design (since not all containers can accurately skip to a frame). */ + /* TODO: this seems to be off by +-1 frames in some cases */ + ts = num_sample; + if (ts >= data->sampleRate * 2) { + data->samplesToDiscard = data->sampleRate * 2; + ts -= data->samplesToDiscard; + } + else { + data->samplesToDiscard = (int)ts; + ts = 0; + } + + /* todo fix this properly */ + if (data->totalFrames) { + ts = (int)ts * (data->formatCtx->duration) / data->totalFrames; + } else { + data->samplesToDiscard = num_sample; + ts = 0; + } + + avformat_seek_file(data->formatCtx, data->streamIndex, ts - 1000, ts, ts, AVSEEK_FLAG_ANY); + avcodec_flush_buffers(data->codecCtx); +#endif /* ifndef VGM_USE_FFMPEG_ACCURATE_LOOPING */ + +#ifdef VGM_USE_FFMPEG_ACCURATE_LOOPING + /* Start from 0 and discard samples until loop_start for accurate looping (slower but not too noticeable) */ + /* We could also seek by offset (AVSEEK_FLAG_BYTE) to the frame closest to the loop then discard + * some samples, which is fast but would need calculations per format / when frame size is not constant */ + data->samplesToDiscard = num_sample; + ts = 0; + + avformat_seek_file(data->formatCtx, data->streamIndex, ts, ts, ts, AVSEEK_FLAG_ANY); + avcodec_flush_buffers(data->codecCtx); +#endif /* ifdef VGM_USE_FFMPEG_ACCURATE_LOOPING */ + + data->readNextPacket = 1; + data->bytesConsumedFromDecodedFrame = INT_MAX; + data->endOfStream = 0; + data->endOfAudio = 0; + +} + #endif diff --git a/Frameworks/vgmstream/vgmstream/src/meta/adx_header.c b/Frameworks/vgmstream/vgmstream/src/meta/adx_header.c index 4bc59d8d6..900485aae 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/adx_header.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/adx_header.c @@ -470,9 +470,15 @@ static struct { /* Phantasy Star Online 2 * guessed with degod */ {0x07d2,0x1ec5,0x0c7f}, + /* Dragon Ball Z: Dokkan Battle * guessed with degod */ {0x0003,0x0d19,0x043b}, + + /* Kisou Ryouhei Gunhound EX (2013-01-31)(Dracue)[PSP] + * guessed with degod */ + {0x0005,0x0bcd,0x1add}, + }; static const int keys_8_count = sizeof(keys_8)/sizeof(keys_8[0]); @@ -487,6 +493,21 @@ static int find_key(STREAMFILE *file, uint8_t type, uint16_t *xor_start, uint16_ int startoff, endoff; int rc = 0; + + /* try to find key in external file first */ + { + uint8_t keybuf[6]; + + if ( read_key_file(keybuf, 6, file) ) { + *xor_start = get_16bitBE(keybuf+0); + *xor_mult = get_16bitBE(keybuf+2); + *xor_add = get_16bitBE(keybuf+4); + return 1; + } + } + + + /* guess key from the tables above */ startoff=read_16bitBE(2, file)+4; endoff=(read_32bitBE(12, file)+31)/32*18*read_8bit(7, file)+startoff; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ffmpeg.c b/Frameworks/vgmstream/vgmstream/src/meta/ffmpeg.c index 96ae82be1..e77c0c60c 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ffmpeg.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ffmpeg.c @@ -4,8 +4,18 @@ #ifdef VGM_USE_FFMPEG +/* internal sizes, can be any value */ +#define FFMPEG_DEFAULT_BLOCK_SIZE 2048 +#define FFMPEG_DEFAULT_IO_BUFFER_SIZE 128 * 1024 + +static int init_seek(ffmpeg_codec_data * data); + + static volatile int g_ffmpeg_initialized = 0; +/* + * Global FFmpeg init + */ static void g_init_ffmpeg() { if (g_ffmpeg_initialized == 1) @@ -22,24 +32,19 @@ static void g_init_ffmpeg() } } -ffmpeg_codec_data * init_ffmpeg_faux_riff(STREAMFILE *streamFile, int64_t fmt_offset, uint64_t stream_offset, uint64_t stream_size, int fmt_big_endian); -ffmpeg_codec_data * init_ffmpeg_offset(STREAMFILE *streamFile, uint64_t start, uint64_t size) { - return init_ffmpeg_faux_riff(streamFile, -1, start, size, 0); -} - -VGMSTREAM * init_vgmstream_ffmpeg_offset(STREAMFILE *streamFile, uint64_t start, uint64_t size); +/** + * Generic init FFmpeg and vgmstream for any file supported by FFmpeg. + * Always called by vgmstream when trying to identify the file type (if the player allows it). + */ VGMSTREAM * init_vgmstream_ffmpeg(STREAMFILE *streamFile) { return init_vgmstream_ffmpeg_offset( streamFile, 0, streamFile->get_size(streamFile) ); } -void free_ffmpeg(ffmpeg_codec_data *data); - VGMSTREAM * init_vgmstream_ffmpeg_offset(STREAMFILE *streamFile, uint64_t start, uint64_t size) { VGMSTREAM *vgmstream = NULL; - + ffmpeg_codec_data *data = init_ffmpeg_offset(streamFile, start, size); - if (!data) return NULL; vgmstream = allocate_vgmstream(data->channels, 0); @@ -53,23 +58,35 @@ VGMSTREAM * init_vgmstream_ffmpeg_offset(STREAMFILE *streamFile, uint64_t start, vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; vgmstream->meta_type = meta_FFmpeg; - + + /* this may happen for some streams */ + if (vgmstream->num_samples <= 0) + goto fail; + + return vgmstream; fail: free_ffmpeg(data); + if (vgmstream) { + vgmstream->codec_data = NULL; + close_vgmstream(vgmstream); + } return NULL; } + +/** + * AVIO callback: read stream, skipping external headers if needed + */ static int ffmpeg_read(void *opaque, uint8_t *buf, int buf_size) { ffmpeg_codec_data *data = (ffmpeg_codec_data *) opaque; uint64_t offset = data->offset; - int max_to_copy; + int max_to_copy = 0; int ret; if (data->header_insert_block) { - max_to_copy = 0; if (offset < data->header_size) { max_to_copy = (int)(data->header_size - offset); if (max_to_copy > buf_size) { @@ -100,11 +117,17 @@ static int ffmpeg_read(void *opaque, uint8_t *buf, int buf_size) return ret; } +/** + * AVIO callback: write stream not needed + */ static int ffmpeg_write(void *opaque, uint8_t *buf, int buf_size) { return -1; } +/** + * AVIO callback: seek stream, skipping external headers if needed + */ static int64_t ffmpeg_seek(void *opaque, int64_t offset, int whence) { ffmpeg_codec_data *data = (ffmpeg_codec_data *) opaque; @@ -112,7 +135,11 @@ static int64_t ffmpeg_seek(void *opaque, int64_t offset, int whence) return data->size + data->header_size; } whence &= ~(AVSEEK_SIZE | AVSEEK_FORCE); + /* false offsets, on reads data->start will be added */ switch (whence) { + case SEEK_SET: + break; + case SEEK_CUR: offset += data->offset; break; @@ -128,6 +155,19 @@ static int64_t ffmpeg_seek(void *opaque, int64_t offset, int whence) return data->offset = offset; } + +/** + * Manually init FFmpeg only, from an offset. + * Can be used if the stream has an extra header over data recognized by FFmpeg. + */ +ffmpeg_codec_data * init_ffmpeg_offset(STREAMFILE *streamFile, uint64_t start, uint64_t size) { + return init_ffmpeg_faux_riff(streamFile, -1, start, size, 0); +} + +/** + * Manually init FFmpeg only, from an offset / fake RIFF. + * Can insert a fake RIFF header, to trick FFmpeg into demuxing/decoding the stream. + */ ffmpeg_codec_data * init_ffmpeg_faux_riff(STREAMFILE *streamFile, int64_t fmt_offset, uint64_t start, uint64_t size, int big_endian) { char filename[PATH_LIMIT]; @@ -136,15 +176,17 @@ ffmpeg_codec_data * init_ffmpeg_faux_riff(STREAMFILE *streamFile, int64_t fmt_of int errcode, i; int streamIndex; + AVStream *stream; AVCodecParameters *codecPar; AVRational tb; - + + + /* basic setup */ g_init_ffmpeg(); data = ( ffmpeg_codec_data * ) calloc(1, sizeof(ffmpeg_codec_data)); - - if (!data) return 0; + if (!data) return NULL; streamFile->get_name( streamFile, filename, sizeof(filename) ); @@ -153,7 +195,9 @@ ffmpeg_codec_data * init_ffmpeg_faux_riff(STREAMFILE *streamFile, int64_t fmt_of data->start = start; data->size = size; - + + + /* insert fake RIFF header to trick FFmpeg into demuxing/decoding the stream */ if (fmt_offset > 0) { int max_header_size = (int)(start - fmt_offset); uint8_t *p; @@ -188,10 +232,12 @@ ffmpeg_codec_data * init_ffmpeg_faux_riff(STREAMFILE *streamFile, int64_t fmt_of put_32bitLE(p + data->header_size - 4, size); } - data->buffer = av_malloc(128 * 1024); + + /* setup IO, attempt to autodetect format and gather some info */ + data->buffer = av_malloc(FFMPEG_DEFAULT_IO_BUFFER_SIZE); if (!data->buffer) goto fail; - data->ioCtx = avio_alloc_context(data->buffer, 128 * 1024, 0, data, ffmpeg_read, ffmpeg_write, ffmpeg_seek); + data->ioCtx = avio_alloc_context(data->buffer, FFMPEG_DEFAULT_IO_BUFFER_SIZE, 0, data, ffmpeg_read, ffmpeg_write, ffmpeg_seek); if (!data->ioCtx) goto fail; data->formatCtx = avformat_alloc_context(); @@ -199,30 +245,37 @@ ffmpeg_codec_data * init_ffmpeg_faux_riff(STREAMFILE *streamFile, int64_t fmt_of data->formatCtx->pb = data->ioCtx; - if ((errcode = avformat_open_input(&data->formatCtx, "", NULL, NULL)) < 0) goto fail; + if ((errcode = avformat_open_input(&data->formatCtx, "", NULL, NULL)) < 0) goto fail; /* autodetect */ if ((errcode = avformat_find_stream_info(data->formatCtx, NULL)) < 0) goto fail; + + /* find valid audio stream inside */ streamIndex = -1; for (i = 0; i < data->formatCtx->nb_streams; ++i) { - codecPar = data->formatCtx->streams[i]->codecpar; - if (codecPar->codec_type == AVMEDIA_TYPE_AUDIO) { - streamIndex = i; - break; + stream = data->formatCtx->streams[i]; + codecPar = stream->codecpar; + if (streamIndex < 0 && codecPar->codec_type == AVMEDIA_TYPE_AUDIO) { + streamIndex = i; /* select first audio stream found */ + } else { + stream->discard = AVDISCARD_ALL; /* disable demuxing unneded streams */ } } if (streamIndex < 0) goto fail; data->streamIndex = streamIndex; + stream = data->formatCtx->streams[streamIndex]; + + /* prepare codec and frame/packet buffers */ data->codecCtx = avcodec_alloc_context3(NULL); if (!data->codecCtx) goto fail; if ((errcode = avcodec_parameters_to_context(data->codecCtx, codecPar)) < 0) goto fail; - av_codec_set_pkt_timebase(data->codecCtx, data->formatCtx->streams[streamIndex]->time_base); + av_codec_set_pkt_timebase(data->codecCtx, stream->time_base); data->codec = avcodec_find_decoder(data->codecCtx->codec_id); if (!data->codec) goto fail; @@ -232,12 +285,16 @@ ffmpeg_codec_data * init_ffmpeg_faux_riff(STREAMFILE *streamFile, int64_t fmt_of data->lastDecodedFrame = av_frame_alloc(); if (!data->lastDecodedFrame) goto fail; av_frame_unref(data->lastDecodedFrame); + data->lastReadPacket = malloc(sizeof(AVPacket)); if (!data->lastReadPacket) goto fail; av_new_packet(data->lastReadPacket, 0); + data->readNextPacket = 1; data->bytesConsumedFromDecodedFrame = INT_MAX; - + + + /* other setup */ data->sampleRate = data->codecCtx->sample_rate; data->channels = data->codecCtx->channels; data->floatingPoint = 0; @@ -274,21 +331,28 @@ ffmpeg_codec_data * init_ffmpeg_faux_riff(STREAMFILE *streamFile, int64_t fmt_of goto fail; } - tb.num = 1; tb.den = data->codecCtx->sample_rate; - - data->totalFrames = av_rescale_q(data->formatCtx->streams[streamIndex]->duration, data->formatCtx->streams[streamIndex]->time_base, tb); data->bitrate = (int)(data->codecCtx->bit_rate); - data->framesRead = 0; data->endOfStream = 0; data->endOfAudio = 0; - + + /* try to guess frames/samples (duration isn't always set) */ + tb.num = 1; tb.den = data->codecCtx->sample_rate; + data->totalFrames = av_rescale_q(stream->duration, stream->time_base, tb); if (data->totalFrames < 0) - data->totalFrames = 0; - - data->sampleBuffer = av_malloc( 2048 * (data->bitsPerSample / 8) * data->channels ); + data->totalFrames = 0; /* caller must consider this */ + + /* setup decode buffer */ + data->samplesPerBlock = FFMPEG_DEFAULT_BLOCK_SIZE; + data->sampleBuffer = av_malloc( data->samplesPerBlock * (data->bitsPerSample / 8) * data->channels ); if (!data->sampleBuffer) goto fail; + + /* setup decent seeking for faulty formats */ + errcode = init_seek(data); + if (errcode < 0) goto fail; + + return data; fail: @@ -297,6 +361,83 @@ fail: return NULL; } + +/** + * Special patching for FFmpeg's buggy seek code. + * + * To seek with avformat_seek_file/av_seek_frame, FFmpeg's demuxers can implement read_seek2 (newest API) + * or read_seek (older API), with various search modes. If none are available it will use seek_frame_generic, + * which manually reads frame by frame until the selected timestamp. However, the prev frame will be consumed + * (so after seeking to 0 next av_read_frame will actually give the second frame and so on). + * + * Fortunately seek_frame_generic can use an index to find the correct position. This function reads the + * first frame/packet and sets up index to timestamp 0. This ensures faulty demuxers will seek to 0 correctly. + * Some formats may not seek to 0 even with this, though. + */ +static int init_seek(ffmpeg_codec_data * data) { + int ret, ts_index, found_first = 0; + int64_t ts = 0; + int64_t pos; /* offset */ + int size; /* coded size */ + int distance = 0; /* always? */ + + AVStream * stream; + AVPacket * pkt; + + stream = data->formatCtx->streams[data->streamIndex]; + pkt = data->lastReadPacket; + + /* read_seek shouldn't need this index, but direct access to FFmpeg's internals is no good */ + /* if (data->formatCtx->iformat->read_seek || data->formatCtx->iformat->read_seek2) + return 0; */ + + /* some formats already have a proper index (e.g. M4A) */ + ts_index = av_index_search_timestamp(stream, ts, AVSEEK_FLAG_ANY); + if (ts_index>=0) + goto test_seek; + + + /* find the first + second packets to get pos/size */ + while (1) { + av_packet_unref(pkt); + ret = av_read_frame(data->formatCtx, pkt); + if (ret < 0) + goto fail; + if (pkt->stream_index != data->streamIndex) + continue; /* ignore non-selected streams */ + + if (!found_first) { /* first found */ + found_first = 1; + pos = pkt->pos; + continue; + } else { /* second found */ + size = pkt->pos - pos; /* coded, pkt->size is decoded size */ + break; + } + } + + /* add index 0 */ + ret = av_add_index_entry(stream, pos, ts, size, distance, AVINDEX_KEYFRAME); + if ( ret < 0 ) + return ret; + + +test_seek: + /* seek to 0 test / move back to beginning, since we just consumed packets */ + ret = avformat_seek_file(data->formatCtx, data->streamIndex, ts, ts, ts, AVSEEK_FLAG_ANY); + if ( ret < 0 ) + return ret; /* we can't even reset_vgmstream the file */ + + avcodec_flush_buffers(data->codecCtx); + + return 0; + + +fail: + return -1; +} + + void free_ffmpeg(ffmpeg_codec_data *data) { if (data->lastReadPacket) { av_packet_unref(data->lastReadPacket); diff --git a/Frameworks/vgmstream/vgmstream/src/meta/hca.c b/Frameworks/vgmstream/vgmstream/src/meta/hca.c index 574f85372..93232b56a 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/hca.c +++ b/Frameworks/vgmstream/vgmstream/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/Frameworks/vgmstream/vgmstream/src/meta/meta.h b/Frameworks/vgmstream/vgmstream/src/meta/meta.h index 5fa0338f1..eb7741cb0 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/meta.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/meta.h @@ -124,6 +124,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/Frameworks/vgmstream/vgmstream/src/meta/mp4.c b/Frameworks/vgmstream/vgmstream/src/meta/mp4.c index 20fd73c2d..ad06998ea 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/mp4.c +++ b/Frameworks/vgmstream/vgmstream/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/Frameworks/vgmstream/vgmstream/src/meta/ps3_msf.c b/Frameworks/vgmstream/vgmstream/src/meta/ps3_msf.c index 3072c3ca4..b599a037c 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ps3_msf.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ps3_msf.c @@ -6,54 +6,81 @@ VGMSTREAM * init_vgmstream_ps3_msf(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; char filename[PATH_LIMIT]; - off_t start_offset; - int32_t loop_start, loop_end; + off_t start_offset, header_offset = 0; + int32_t data_size, loop_start, loop_end; int loop_flag = 0; int channel_count; int codec_id; - size_t fileLength; + +#ifdef VGM_USE_FFMPEG + ffmpeg_codec_data *ffmpeg_data = NULL; +#endif + /* check extension, case insensitive */ streamFile->get_name(streamFile,filename,sizeof(filename)); if (strcasecmp("msf",filename_extension(filename))) goto fail; - if (read_8bit(0x0,streamFile) != 0x4D) goto fail; /* M */ - if (read_8bit(0x1,streamFile) != 0x53) goto fail; /* S */ - if (read_8bit(0x2,streamFile) != 0x46) goto fail; /* F */ + /* "WMSF" variation with a mini header over the MSFC header, same extension */ + if (read_32bitBE(0x00,streamFile) == 0x574D5346) { + header_offset = 0x10; + } + start_offset = header_offset+0x40; - fileLength = get_streamfile_size(streamFile); + /* usually 0x4D534643 "MSFC" */ + if (read_8bit(header_offset+0x0,streamFile) != 0x4D) goto fail; /* M */ + if (read_8bit(header_offset+0x1,streamFile) != 0x53) goto fail; /* S */ + if (read_8bit(header_offset+0x2,streamFile) != 0x46) goto fail; /* F */ - loop_flag = (read_32bitBE(0x18,streamFile) != 0xFFFFFFFF); - if (loop_flag) - { - loop_start = read_32bitBE(0x18,streamFile); - loop_end = read_32bitBE(0x0C,streamFile); + + + data_size = read_32bitBE(header_offset+0x0C,streamFile); /* without header*/ + if (data_size==0xFFFFFFFF) { + size_t fileLength = get_streamfile_size(streamFile); + data_size = fileLength - start_offset; } - channel_count = read_32bitBE(0x8,streamFile); - codec_id = read_32bitBE(0x4,streamFile); + /* block_align/loop_type? = read_32bitBE(header_offset+0x14,streamFile);*/ /* 00/40 when no loop, 11/50/51/71 */ + + loop_start = read_32bitBE(header_offset+0x18,streamFile); + loop_end = read_32bitBE(header_offset+0x1C,streamFile); /* loop duration */ + loop_flag = loop_start != 0xFFFFFFFF; + if (loop_flag) { + if (loop_end==0xFFFFFFFF) {/* not seen */ + loop_end = data_size; + } else { + loop_end = loop_start + loop_end; /* usually equals data_size but not always */ + if ( loop_end > data_size)/* not seen */ + loop_end = data_size; + } + } + + channel_count = read_32bitBE(header_offset+0x8,streamFile); + codec_id = read_32bitBE(header_offset+0x4,streamFile); + /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; /* fill in the vital statistics */ - vgmstream->channels = channel_count; + vgmstream->channels = channel_count; - /* Sample rate hack for strange files that don't have a specified frequency */ - if (read_32bitBE(0x10,streamFile)==0x00000000) + /* Sample rate hack for strange files that don't have a specified frequency */ + if (read_32bitBE(header_offset+0x10,streamFile)==0x00000000) vgmstream->sample_rate = 48000; else - vgmstream->sample_rate = read_32bitBE(0x10,streamFile); + vgmstream->sample_rate = read_32bitBE(header_offset+0x10,streamFile); - start_offset = 0x40; + + vgmstream->meta_type = meta_PS3_MSF; switch (codec_id) { case 0x0: /* PCM (Big Endian) */ { vgmstream->coding_type = coding_PCM16BE; - vgmstream->num_samples = read_32bitBE(0x0C,streamFile)/2/channel_count; + vgmstream->num_samples = data_size/2/channel_count; if (loop_flag){ vgmstream->loop_start_sample = loop_start/2/channel_count; @@ -74,12 +101,7 @@ VGMSTREAM * init_vgmstream_ps3_msf(STREAMFILE *streamFile) { case 0x3: /* PSx ADPCM */ { vgmstream->coding_type = coding_PSX; - vgmstream->num_samples = read_32bitBE(0x0C,streamFile)*28/16/channel_count; - - if (vgmstream->num_samples == 0xFFFFFFFF) - { - vgmstream->num_samples = (fileLength - start_offset)*28/16/channel_count; - } + vgmstream->num_samples = data_size*28/16/channel_count; if (loop_flag) { @@ -94,13 +116,66 @@ VGMSTREAM * init_vgmstream_ps3_msf(STREAMFILE *streamFile) { else if (channel_count > 1) { vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = 0x10; // read_32bitBE(0x14,streamFile); + vgmstream->interleave_block_size = 0x10; } } break; -#ifdef VGM_USE_MPEG +#ifdef VGM_USE_FFMPEG + case 0x4: /* ATRAC3 (frame size 96) */ + case 0x5: /* ATRAC3 (frame size 152) */ + case 0x6: /* ATRAC3 (frame size 192) */ + /* delegate to FFMpeg, it can parse MSF files */ + ffmpeg_data = init_ffmpeg_offset(streamFile, header_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; + + vgmstream->num_samples = ffmpeg_data->totalFrames; + if (loop_flag) { + int atrac3_frame = 1024; + int block_align = (codec_id == 0x4 ? 96 : codec_id == 0x5 ? 152 : 192) * channel_count; + /* int block_align = ffmpeg_data->codecCtx->block_align; *//* is this always set? */ + vgmstream->loop_start_sample = (loop_start / block_align) * atrac3_frame; + vgmstream->loop_end_sample = (loop_end / block_align) * atrac3_frame; + } + + break; +#endif +#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) ); + 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; + + /* 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; + if (loop_flag) { + int frame_size = ffmpeg_data->codecCtx->frame_size; + vgmstream->loop_start_sample = (int64_t)loop_start * ffmpeg_data->sampleRate * 8 / ffmpeg_data->bitrate; + vgmstream->loop_start_sample -= vgmstream->loop_start_sample==frame_size ? frame_size + : vgmstream->loop_start_sample % frame_size; + vgmstream->loop_end_sample = (int64_t)loop_end * ffmpeg_data->sampleRate * 8 / ffmpeg_data->bitrate; + vgmstream->loop_end_sample -= vgmstream->loop_end_sample==frame_size ? frame_size + : vgmstream->loop_end_sample % frame_size; + } + + break; +#endif +#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; @@ -114,13 +189,13 @@ VGMSTREAM * init_vgmstream_ps3_msf(STREAMFILE *streamFile) { vgmstream->coding_type = ct; vgmstream->layout_type = layout_mpeg; if (mi.vbr != MPG123_CBR) goto fail; - vgmstream->num_samples = mpeg_bytes_to_samples(read_32bitBE(0xC,streamFile), &mi); - vgmstream->num_samples -= vgmstream->num_samples%576; + vgmstream->num_samples = mpeg_bytes_to_samples(data_size, &mi); + 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; } @@ -130,7 +205,6 @@ VGMSTREAM * init_vgmstream_ps3_msf(STREAMFILE *streamFile) { goto fail; } - vgmstream->meta_type = meta_PS3_MSF; /* open the file for reading */ { @@ -151,6 +225,12 @@ VGMSTREAM * init_vgmstream_ps3_msf(STREAMFILE *streamFile) { /* clean up anything we may have opened */ fail: +#ifdef VGM_USE_FFMPEG + if (ffmpeg_data) { + free_ffmpeg(ffmpeg_data); + vgmstream->codec_data = NULL; + } +#endif if (vgmstream) close_vgmstream(vgmstream); return NULL; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ps3_sgh_sgb.c b/Frameworks/vgmstream/vgmstream/src/meta/ps3_sgh_sgb.c index a99348a63..e299df3b6 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ps3_sgh_sgb.c +++ b/Frameworks/vgmstream/vgmstream/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/Frameworks/vgmstream/vgmstream/src/meta/riff.c b/Frameworks/vgmstream/vgmstream/src/meta/riff.c index bd7829766..1edcd58a8 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/riff.c +++ b/Frameworks/vgmstream/vgmstream/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,13 +402,19 @@ 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: - sample_count = (data_size / fmt.block_size) * 2048 * fmt.channel_count; + /* 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 default: @@ -565,7 +585,10 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { /* clean up anything we may have opened */ fail: #ifdef VGM_USE_FFMPEG - if (ffmpeg_data) free_ffmpeg(ffmpeg_data); + if (ffmpeg_data) { + free_ffmpeg(ffmpeg_data); + if (vgmstream) vgmstream->codec_data = NULL; + } #endif if (vgmstream) close_vgmstream(vgmstream); return NULL; diff --git a/Frameworks/vgmstream/vgmstream/src/streamfile.c b/Frameworks/vgmstream/vgmstream/src/streamfile.c index e179082ae..9e36ac999 100644 --- a/Frameworks/vgmstream/vgmstream/src/streamfile.c +++ b/Frameworks/vgmstream/vgmstream/src/streamfile.c @@ -271,3 +271,82 @@ size_t get_streamfile_dos_line(int dst_length, char * dst, off_t offset, return i+extra_bytes; } + + +/** + * open file containing decryption keys and copy to buffer + * tries combinations of keynames based on the original filename + * + * returns true if found and copied + */ +int read_key_file(uint8_t * buf, size_t bufsize, STREAMFILE *streamFile) { + char keyname[PATH_LIMIT]; + char filename[PATH_LIMIT]; + const char *path, *ext; + STREAMFILE * streamFileKey = NULL; + + streamFile->get_name(streamFile,filename,sizeof(filename)); + + if (strlen(filename)+4 > sizeof(keyname)) goto fail; + + /* try to open a keyfile using variations: + * "(name.ext)key" (per song), "(.ext)key" (per folder) */ + { + ext = strrchr(filename,'.'); + if (ext!=NULL) ext = ext+1; + + path = strrchr(filename,DIR_SEPARATOR); + if (path!=NULL) path = path+1; + + /* "(name.ext)key" */ + strcpy(keyname, filename); + strcat(keyname, "key"); + streamFileKey = streamFile->open(streamFile,keyname,STREAMFILE_DEFAULT_BUFFER_SIZE); + if (streamFileKey) goto found; + + /* "(name.ext)KEY" */ + /* + strcpy(keyname+strlen(keyname)-3,"KEY"); + streamFileKey = streamFile->open(streamFile,keyname,STREAMFILE_DEFAULT_BUFFER_SIZE); + if (streamFileKey) goto found; + */ + + + /* "(.ext)key" */ + if (path) { + strcpy(keyname, filename); + keyname[path-filename] = '\0'; + strcat(keyname, "."); + } else { + strcpy(keyname, "."); + } + if (ext) strcat(keyname, ext); + strcat(keyname, "key"); + streamFileKey = streamFile->open(streamFile,keyname,STREAMFILE_DEFAULT_BUFFER_SIZE); + if (streamFileKey) goto found; + + /* "(.ext)KEY" */ + /* + strcpy(keyname+strlen(keyname)-3,"KEY"); + streamFileKey = streamFile->open(streamFile,keyname,STREAMFILE_DEFAULT_BUFFER_SIZE); + if (streamFileKey) goto found; + */ + + goto fail; + } + +found: + if (get_streamfile_size(streamFileKey) != bufsize) goto fail; + + if (read_streamfile(buf, 0, bufsize, streamFileKey)!=bufsize) goto fail; + + close_streamfile(streamFileKey); + + return 1; + +fail: + if (streamFileKey) close_streamfile(streamFileKey); + + return 0; +} + diff --git a/Frameworks/vgmstream/vgmstream/src/streamfile.h b/Frameworks/vgmstream/vgmstream/src/streamfile.h index 118fc2f16..61f32a28f 100644 --- a/Frameworks/vgmstream/vgmstream/src/streamfile.h +++ b/Frameworks/vgmstream/vgmstream/src/streamfile.h @@ -34,6 +34,14 @@ #define STREAMFILE_DEFAULT_BUFFER_SIZE 0x400 +#ifndef DIR_SEPARATOR +#if defined (_WIN32) || defined (WIN32) +#define DIR_SEPARATOR '\\' +#else +#define DIR_SEPARATOR '/' +#endif +#endif + typedef struct _STREAMFILE { size_t (*read)(struct _STREAMFILE *,uint8_t * dest, off_t offset, size_t length); size_t (*get_size)(struct _STREAMFILE *); @@ -141,4 +149,7 @@ static inline STREAMFILE * open_stdio_streamfile(const char * const filename) { size_t get_streamfile_dos_line(int dst_length, char * dst, off_t offset, STREAMFILE * infile, int *line_done_ptr); +int read_key_file(uint8_t * buf, size_t bufsize, STREAMFILE *streamFile); + + #endif diff --git a/Frameworks/vgmstream/vgmstream/src/vgmstream.c b/Frameworks/vgmstream/vgmstream/src/vgmstream.c index 53967b98f..09cbe22da 100644 --- a/Frameworks/vgmstream/vgmstream/src/vgmstream.c +++ b/Frameworks/vgmstream/vgmstream/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, @@ -340,6 +338,7 @@ VGMSTREAM * (*init_vgmstream_fcns[])(STREAMFILE *streamFile) = { init_vgmstream_g1l, init_vgmstream_hca, #ifdef VGM_USE_FFMPEG + init_vgmstream_mp4_aac_ffmpeg, init_vgmstream_ffmpeg, #endif }; @@ -361,7 +360,7 @@ VGMSTREAM * init_vgmstream_internal(STREAMFILE *streamFile, int do_dfs) { /* fail if there is nothing to play * (without this check vgmstream can generate empty files) */ - if ( vgmstream->num_samples==0 ) { + if (vgmstream->num_samples <= 0) { close_vgmstream(vgmstream); continue; } @@ -514,20 +513,7 @@ void reset_vgmstream(VGMSTREAM * vgmstream) { #ifdef VGM_USE_FFMPEG if (vgmstream->coding_type==coding_FFmpeg) { - ffmpeg_codec_data *data = (ffmpeg_codec_data *) vgmstream->codec_data; - - if (data->formatCtx) { - avformat_seek_file(data->formatCtx, -1, 0, 0, 0, AVSEEK_FLAG_ANY); - } - if (data->codecCtx) { - avcodec_flush_buffers(data->codecCtx); - } - data->readNextPacket = 1; - data->bytesConsumedFromDecodedFrame = INT_MAX; - data->framesRead = 0; - data->endOfStream = 0; - data->endOfAudio = 0; - data->samplesToDiscard = 0; + reset_ffmpeg(vgmstream); } #endif @@ -1083,9 +1069,9 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) { case coding_FFmpeg: { ffmpeg_codec_data *data = (ffmpeg_codec_data *) vgmstream->codec_data; - if (vgmstream->codec_data) { - int64_t samplesRemain = data->totalFrames - data->framesRead; - return samplesRemain > 2048 ? 2048 : samplesRemain; + if (data) { + /* must know the full block size for edge loops */ + return data->samplesPerBlock; } return 0; } @@ -1788,41 +1774,7 @@ int vgmstream_do_loop(VGMSTREAM * vgmstream) { } #ifdef VGM_USE_FFMPEG if (vgmstream->coding_type==coding_FFmpeg) { - ffmpeg_codec_data *data = (ffmpeg_codec_data *)(vgmstream->codec_data); - int64_t ts; - - /* Seek to loop start by timestamp (closest frame) + adjust skipping some samples */ - /* FFmpeg seeks by ts by design (since not all containers can accurately skip to a frame). */ - /* TODO: this seems to be off by +-1 frames in some cases */ - ts = vgmstream->loop_start_sample; - if (ts >= data->sampleRate * 2) { - data->samplesToDiscard = data->sampleRate * 2; - ts -= data->samplesToDiscard; - } - else { - data->samplesToDiscard = (int)ts; - ts = 0; - } - data->framesRead = (int)ts; - ts = data->framesRead * (data->formatCtx->duration) / data->totalFrames; - - -#ifdef VGM_USE_FFMPEG_ACCURATE_LOOPING - /* Start from 0 and discard samples until loop_start for accurate looping (slower but not too noticeable) */ - /* We could also seek by offset (AVSEEK_FLAG_BYTE) to the frame closest to the loop then discard - * some samples, which is fast but would need calculations per format / when frame size is not constant */ - data->samplesToDiscard = vgmstream->loop_start_sample; - data->framesRead = 0; - ts = 0; -#endif /* VGM_USE_FFMPEG_ACCURATE_LOOPING */ - - avformat_seek_file(data->formatCtx, -1, ts - 1000, ts, ts, AVSEEK_FLAG_ANY); - avcodec_flush_buffers(data->codecCtx); - - data->readNextPacket = 1; - data->bytesConsumedFromDecodedFrame = INT_MAX; - data->endOfStream = 0; - data->endOfAudio = 0; + seek_ffmpeg(vgmstream, vgmstream->loop_start_sample); } #endif /* VGM_USE_FFMPEG */ #if defined(VGM_USE_MP4V2) && defined(VGM_USE_FDKAAC) @@ -1909,7 +1861,7 @@ void describe_vgmstream(VGMSTREAM * vgmstream, char * desc, int length) { return; } - snprintf(temp,TEMPSIZE,"sample rate %d Hz\n" + snprintf(temp,TEMPSIZE,"sample rate: %d Hz\n" "channels: %d\n", vgmstream->sample_rate,vgmstream->channels); concatn(length,desc,temp); @@ -2125,11 +2077,14 @@ void describe_vgmstream(VGMSTREAM * vgmstream, char * desc, int length) { { ffmpeg_codec_data *data = (ffmpeg_codec_data *) vgmstream->codec_data; if (vgmstream->codec_data) { - if (data->codec) { + if (data->codec && data->codec->long_name) { snprintf(temp,TEMPSIZE,data->codec->long_name); } + else if (data->codec && data->codec->name) { + snprintf(temp,TEMPSIZE,data->codec->name); + } else { - snprintf(temp,TEMPSIZE,"FFmpeg"); + snprintf(temp,TEMPSIZE,"FFmpeg (unknown codec)"); } } else { @@ -3190,8 +3145,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"); @@ -3244,9 +3199,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/Frameworks/vgmstream/vgmstream/src/vgmstream.h b/Frameworks/vgmstream/vgmstream/src/vgmstream.h index 70f1d2209..98b09d9ab 100644 --- a/Frameworks/vgmstream/vgmstream/src/vgmstream.h +++ b/Frameworks/vgmstream/vgmstream/src/vgmstream.h @@ -13,7 +13,12 @@ enum { PATH_LIMIT = 32768 }; * removing these defines (and the references to the libraries in the * Makefile) */ #define VGM_USE_VORBIS + +/* can be disabled to decode with FFmpeg instead */ +#ifndef VGM_DISABLE_MPEG #define VGM_USE_MPEG +#endif + /* disabled by default, defined for builds that support it */ #define VGM_USE_G7221 #define VGM_USE_G719 @@ -570,7 +575,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 */ @@ -588,7 +593,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 @@ -868,6 +872,7 @@ typedef struct { // inserted header, ie. fake RIFF header uint8_t *header_insert_block; + // header/fake RIFF over the real (parseable by FFmpeg) file start uint64_t header_size; // stream info @@ -875,12 +880,13 @@ typedef struct { int bitsPerSample; int floatingPoint; int sampleRate; - int64_t totalFrames; - int64_t framesRead; + int64_t totalFrames; // sample count, or 0 if unknown int bitrate; // Intermediate buffer uint8_t *sampleBuffer; + // max samples a block can held (can be less or more than samples per decoded frame) + size_t samplesPerBlock; // FFmpeg context used for metadata AVCodec *codec; diff --git a/Plugins/vgmstream/vgmstream/VGMDecoder.m b/Plugins/vgmstream/vgmstream/VGMDecoder.m index c1909433c..9632eadfd 100644 --- a/Plugins/vgmstream/vgmstream/VGMDecoder.m +++ b/Plugins/vgmstream/vgmstream/VGMDecoder.m @@ -234,7 +234,7 @@ VGMSTREAM *init_vgmstream_from_cogfile(const char *path) { + (NSArray *)fileTypes { - return [NSArray arrayWithObjects:@"2dx9", @"aaap", @"aax", @"acm", @"adp", @"adpcm", @"ads", @"adx", @"afc", @"agsc", @"ahx",@"aifc", @"aiff", @"aix", @"amts", @"as4", @"asd", @"asf", @"asr", @"ass", @"ast", @"at3", @"aud", @"aus", @"baf", @"baka", @"bar", @"bcstm", @"bcwav", @"bfstm", @"bfwav", @"bfwavnsmbu", @"bg00", @"bgw", @"bh2pcm", @"bmdx", @"bns", @"bnsf", @"bo2", @"brstm", @"caf", @"capdsp", @"ccc", @"cfn", @"cnk", @"dcs", @"dcsw", @"ddsp", @"de2", @"dmsg", @"dsp", @"dvi", @"dxh", @"eam", @"emff", @"enth", @"fag", @"filp", @"fsb", @"fwav", @"gca", @"gcm", @"gcsw", @"gcw", @"genh", @"gms", @"gsp", @"hca", @"hgc1", @"his", @"hps", @"hwas", @"idsp", @"idvi", @"ikm", @"ild", @"int", @"isd", @"ish", @"ivaud", @"ivb", @"joe", @"kces", @"kcey", @"khv", @"kraw", @"leg", @"logg", @"lps", @"lsf", @"lwav", @"matx", @"mcg", @"mi4", @"mib", @"mic", @"mihb", @"mpdsp", @"msa", @"mss", @"msvp", @"mtaf", @"mus", @"musc", @"musx", @"mwv", @"myspd", @"ndp", @"npsf", @"nus3bank", @"nwa", @"omu", @"otm", @"p3d", @"pcm", @"pdt", @"pnb", @"pos", @"psh", @"psw", @"raw", @"rkv", @"rnd", @"rrds", @"rsd", @"rsf", @"rstm", @"rwar", @"rwav", @"rws", @"rwsd", @"rwx", @"rxw", @"s14", @"sab", @"sad", @"sap", @"sc", @"scd", @"sd9", @"sdt", @"seg", @"sfl", @"sfs", @"sgb", @"sgd", @"sl3", @"sli", @"smp", @"smpl", @"snd", @"sng", @"sns", @"spd", @"sps", @"spsd", @"spt", @"spw", @"ss2", @"ss7", @"ssm", @"sss", @"ster", @"sth", @"stm", @"stma", @"str", @"strm", @"sts", @"stx", @"svag", @"svs", @"swav", @"swd", @"tec", @"thp", @"tk5", @"tydsp", @"um3", @"vag", @"vas", @"vgs", @"vig", @"vjdsp", @"voi", @"vpk", @"vs", @"vsf", @"waa", @"wac", @"wad", @"wam", @"was", @"wavm", @"wb", @"wii", @"wp2", @"wsd", @"wsi", @"wvs", @"xa", @"xa2", @"xa30", @"xma", @"xmu", @"xss", @"xvas", @"xwav", @"xwb", @"xwma", @"ydsp", @"ymf", @"zsd", @"zwdsp", nil]; + return [NSArray arrayWithObjects:@"2dx9", @"aaap", @"aax", @"acm", @"adp", @"adpcm", @"ads", @"adx", @"afc", @"agsc", @"ahx",@"aifc", @"aiff", @"aix", @"amts", @"as4", @"asd", @"asf", @"asr", @"ass", @"ast", @"at3", @"aud", @"aus", @"baf", @"baka", @"bar", @"bcstm", @"bcwav", @"bfstm", @"bfwav", @"bfwavnsmbu", @"bg00", @"bgw", @"bh2pcm", @"bmdx", @"bns", @"bnsf", @"bo2", @"brstm", @"caf", @"capdsp", @"ccc", @"cfn", @"cnk", @"dcs", @"dcsw", @"ddsp", @"de2", @"dmsg", @"dsp", @"dvi", @"dxh", @"eam", @"emff", @"enth", @"fag", @"filp", @"fsb", @"fwav", @"gca", @"gcm", @"gcsw", @"gcw", @"genh", @"gms", @"gsp", @"hca", @"hgc1", @"his", @"hps", @"hwas", @"idsp", @"idvi", @"ikm", @"ild", @"int", @"isd", @"ish", @"ivaud", @"ivb", @"joe", @"kces", @"kcey", @"khv", @"kraw", @"leg", @"logg", @"lps", @"lsf", @"lwav", @"matx", @"mcg", @"mi4", @"mib", @"mic", @"mihb", @"mpdsp", @"msa", @"mss", @"msvp", @"mtaf", @"mus", @"musc", @"musx", @"mwv", @"myspd", @"ndp", @"npsf", @"nus3bank", @"nwa", @"omu", @"otm", @"p3d", @"pcm", @"pdt", @"pnb", @"pos", @"psh", @"psw", @"raw", @"rkv", @"rnd", @"rrds", @"rsd", @"rsf", @"rstm", @"rwar", @"rwav", @"rws", @"rwsd", @"rwx", @"rxw", @"s14", @"sab", @"sad", @"sap", @"sc", @"scd", @"sd9", @"sdt", @"seg", @"sfl", @"sfs", @"sgb", @"sgd", @"sgx", @"sl3", @"sli", @"smp", @"smpl", @"snd", @"sng", @"sns", @"spd", @"sps", @"spsd", @"spt", @"spw", @"ss2", @"ss7", @"ssm", @"sss", @"ster", @"sth", @"stm", @"stma", @"str", @"strm", @"sts", @"stx", @"svag", @"svs", @"swav", @"swd", @"tec", @"thp", @"tk5", @"tydsp", @"um3", @"vag", @"vas", @"vgs", @"vig", @"vjdsp", @"voi", @"vpk", @"vs", @"vsf", @"waa", @"wac", @"wad", @"wam", @"was", @"wavm", @"wb", @"wii", @"wp2", @"wsd", @"wsi", @"wvs", @"xa", @"xa2", @"xa30", @"xma", @"xmu", @"xss", @"xvas", @"xwav", @"xwb", @"xwma", @"ydsp", @"ymf", @"zsd", @"zwdsp", @"vgmstream", @"vgms", nil]; } + (NSArray *)mimeTypes