From 562341f999e535f7b56478841b42d065f2aa9ab1 Mon Sep 17 00:00:00 2001 From: bnnm Date: Sat, 27 Jan 2018 10:47:53 +0100 Subject: [PATCH 01/21] Fix SCHl EA-XA v1 mono [Supercross 2000 videos] --- src/layout/blocked_ea_schl.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/layout/blocked_ea_schl.c b/src/layout/blocked_ea_schl.c index 5e222339..e71844d0 100644 --- a/src/layout/blocked_ea_schl.c +++ b/src/layout/blocked_ea_schl.c @@ -95,6 +95,7 @@ void block_update_ea_schl(off_t block_offset, VGMSTREAM * vgmstream) { /* id, size, samples, hists-per-channel, stereo/interleaved data */ case coding_EA_XA: + //case coding_EA_XA_V2: /* handled in default */ case coding_EA_XA_int: for (i = 0; i < vgmstream->channels; i++) { int is_interleaved = vgmstream->coding_type == coding_EA_XA_int; @@ -106,7 +107,9 @@ void block_update_ea_schl(off_t block_offset, VGMSTREAM * vgmstream) { /* the block can have padding so find the channel size from num_samples */ interleave = is_interleaved ? (block_samples / 28 * 0x0f) : 0; - vgmstream->ch[i].offset = block_offset + 0x0c + vgmstream->channels*0x04 + i*interleave; + + /* NOT channels*0x04, as seen in Superbike 2000 (PC) EA-XA v1 mono vids */ + vgmstream->ch[i].offset = block_offset + 0x0c + 2*0x04 + i*interleave; } break; @@ -146,6 +149,7 @@ void block_update_ea_schl(off_t block_offset, VGMSTREAM * vgmstream) { for (i = 0; i < vgmstream->channels; i++) { off_t channel_start = read_32bit(block_offset + 0x0C + (0x04*i),streamFile); vgmstream->ch[i].offset = block_offset + 0x0C + (0x04*vgmstream->channels) + channel_start; + VGM_LOG("ch=%x, off=%lx\n", i, vgmstream->ch[i].offset); } /* read ADPCM history before each channel if needed (not actually read in sx.exe) */ From 9201a7384daeff2cfbd0773f1672c368371d1774 Mon Sep 17 00:00:00 2001 From: bnnm Date: Sat, 27 Jan 2018 10:57:29 +0100 Subject: [PATCH 02/21] Fix XWB PCM8 [Unreal Championship (Xbox)] --- src/meta/xwb.c | 38 ++++++++++++++++---------------------- 1 file changed, 16 insertions(+), 22 deletions(-) diff --git a/src/meta/xwb.c b/src/meta/xwb.c index 403f0d98..876940d3 100644 --- a/src/meta/xwb.c +++ b/src/meta/xwb.c @@ -9,7 +9,7 @@ /* the x.x version is just to make it clearer, MS only classifies XACT as 1/2/3 */ #define XACT1_0_MAX 1 /* Project Gotham Racing 2 (v1), Silent Hill 4 (v1) */ -#define XACT1_1_MAX 3 /* The King of Fighters 2003 (v3) */ +#define XACT1_1_MAX 3 /* Unreal Championship (v2), The King of Fighters 2003 (v3) */ #define XACT2_0_MAX 34 /* Dead or Alive 4 (v17), Kameo (v23), Table Tennis (v34) */ // v35/36/37 too? #define XACT2_1_MAX 38 /* Prey (v38) */ // v39 too? #define XACT2_2_MAX 41 /* Blue Dragon (v40) */ @@ -358,43 +358,40 @@ VGMSTREAM * init_vgmstream_xwb(STREAMFILE *streamFile) { get_xsb_name(vgmstream->stream_name,STREAM_NAME_SIZE, target_stream, &xwb, streamFile); switch(xwb.codec) { - case PCM: - vgmstream->coding_type = xwb.bits_per_sample == 0 ? coding_PCM8 : + case PCM: /* Unreal Championship (Xbox)[PCM8], KOF2003 (Xbox)[PCM16LE], Otomedius (X360)[PCM16BE] */ + vgmstream->coding_type = xwb.bits_per_sample == 0 ? coding_PCM8_U : (xwb.little_endian ? coding_PCM16LE : coding_PCM16BE); vgmstream->layout_type = xwb.channels > 1 ? layout_interleave : layout_none; vgmstream->interleave_block_size = xwb.bits_per_sample == 0 ? 0x01 : 0x02; break; - case XBOX_ADPCM: + case XBOX_ADPCM: /* Silent Hill 4 (Xbox) */ vgmstream->coding_type = coding_XBOX; vgmstream->layout_type = layout_none; break; - case MS_ADPCM: + case MS_ADPCM: /* Persona 4 Ultimax (AC) */ vgmstream->coding_type = coding_MSADPCM; vgmstream->layout_type = layout_none; vgmstream->interleave_block_size = (xwb.block_align + 22) * xwb.channels; /*22=CONVERSION_OFFSET (?)*/ break; #ifdef VGM_USE_FFMPEG - case XMA1: { - ffmpeg_codec_data *ffmpeg_data = NULL; + case XMA1: { /* Kameo (X360) */ uint8_t buf[100]; int bytes; bytes = ffmpeg_make_riff_xma1(buf, 100, vgmstream->num_samples, xwb.stream_size, vgmstream->channels, vgmstream->sample_rate, 0); if (bytes <= 0) goto fail; - ffmpeg_data = init_ffmpeg_header_offset(streamFile, buf,bytes, xwb.stream_offset,xwb.stream_size); - if ( !ffmpeg_data ) goto fail; - vgmstream->codec_data = ffmpeg_data; + vgmstream->codec_data = init_ffmpeg_header_offset(streamFile, buf,bytes, xwb.stream_offset,xwb.stream_size); + if (!vgmstream->codec_data) goto fail; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; break; } - case XMA2: { - ffmpeg_codec_data *ffmpeg_data = NULL; + case XMA2: { /* Blue Dragon (X360) */ uint8_t buf[100]; int bytes, block_size, block_count; @@ -404,15 +401,14 @@ VGMSTREAM * init_vgmstream_xwb(STREAMFILE *streamFile) { bytes = ffmpeg_make_riff_xma2(buf, 100, vgmstream->num_samples, xwb.stream_size, vgmstream->channels, vgmstream->sample_rate, block_count, block_size); if (bytes <= 0) goto fail; - ffmpeg_data = init_ffmpeg_header_offset(streamFile, buf,bytes, xwb.stream_offset,xwb.stream_size); - if ( !ffmpeg_data ) goto fail; - vgmstream->codec_data = ffmpeg_data; + vgmstream->codec_data = init_ffmpeg_header_offset(streamFile, buf,bytes, xwb.stream_offset,xwb.stream_size); + if (!vgmstream->codec_data) goto fail; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; break; } - case WMA: { /* WMAudio1 (WMA v1) */ + case WMA: { /* WMAudio1 (WMA v1): Prince of Persia 2 port (Xbox) */ ffmpeg_codec_data *ffmpeg_data = NULL; ffmpeg_data = init_ffmpeg_offset(streamFile, xwb.stream_offset,xwb.stream_size); @@ -427,8 +423,7 @@ VGMSTREAM * init_vgmstream_xwb(STREAMFILE *streamFile) { break; } - case XWMA: { /* WMAudio2 (WMA v2), WMAudio3 (WMA Pro) */ - ffmpeg_codec_data *ffmpeg_data = NULL; + case XWMA: { /* WMAudio2 (WMA v2): BlazBlue (X360), WMAudio3 (WMA Pro): ? */ uint8_t buf[100]; int bytes, bps_index, block_align, block_index, avg_bps, wma_codec; @@ -444,15 +439,14 @@ VGMSTREAM * init_vgmstream_xwb(STREAMFILE *streamFile) { bytes = ffmpeg_make_riff_xwma(buf, 100, wma_codec, xwb.stream_size, vgmstream->channels, vgmstream->sample_rate, avg_bps, block_align); if (bytes <= 0) goto fail; - ffmpeg_data = init_ffmpeg_header_offset(streamFile, buf,bytes, xwb.stream_offset,xwb.stream_size); - if ( !ffmpeg_data ) goto fail; - vgmstream->codec_data = ffmpeg_data; + vgmstream->codec_data = init_ffmpeg_header_offset(streamFile, buf,bytes, xwb.stream_offset,xwb.stream_size); + if (!vgmstream->codec_data) goto fail; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; break; } - case ATRAC3: { /* Techland PS3 extension */ + case ATRAC3: { /* Techland PS3 extension: Sniper Ghost Warrior (PS3) */ uint8_t buf[200]; int bytes; From a0e97683e42bc8f63b73f05af54feac84d3aac8e Mon Sep 17 00:00:00 2001 From: bnnm Date: Sat, 27 Jan 2018 11:00:21 +0100 Subject: [PATCH 03/21] Add Starbreeze XWC [Riddick Dark Athena, Syndicate] --- src/formats.c | 2 + src/libvgmstream.vcproj | 4 ++ src/libvgmstream.vcxproj | 1 + src/libvgmstream.vcxproj.filters | 3 + src/meta/meta.h | 1 + src/meta/xwc.c | 108 +++++++++++++++++++++++++++++++ src/vgmstream.c | 1 + src/vgmstream.h | 11 ++-- 8 files changed, 126 insertions(+), 5 deletions(-) create mode 100644 src/meta/xwc.c diff --git a/src/formats.c b/src/formats.c index f234fbf0..3981adfc 100644 --- a/src/formats.c +++ b/src/formats.c @@ -377,6 +377,7 @@ static const char* extension_list[] = { "xvas", "xwav", "xwb", + "xwc", "xwm", //FFmpeg, not parsed (XWMA) "xwma", //FFmpeg, not parsed (XWMA) "xws", @@ -947,6 +948,7 @@ static const meta_info meta_info_list[] = { {meta_OGG_SNGW, "Ogg Vorbis (Capcom)"}, {meta_OGG_ISD, "Ogg Vorbis (ISD)"}, {meta_KMA9, "Koei Tecmo KMA9 header"}, + {meta_XWC, "Starbreeze XWC header"}, #ifdef VGM_USE_MP4V2 {meta_MP4, "AAC header"}, diff --git a/src/libvgmstream.vcproj b/src/libvgmstream.vcproj index c799eff3..53fd2f38 100644 --- a/src/libvgmstream.vcproj +++ b/src/libvgmstream.vcproj @@ -1374,6 +1374,10 @@ RelativePath=".\meta\xwb.c" > + + diff --git a/src/libvgmstream.vcxproj b/src/libvgmstream.vcxproj index d1387bd1..322d932f 100644 --- a/src/libvgmstream.vcxproj +++ b/src/libvgmstream.vcxproj @@ -417,6 +417,7 @@ + diff --git a/src/libvgmstream.vcxproj.filters b/src/libvgmstream.vcxproj.filters index c309ea96..b007e054 100644 --- a/src/libvgmstream.vcxproj.filters +++ b/src/libvgmstream.vcxproj.filters @@ -835,6 +835,9 @@ meta\Source Files + + meta\Source Files + meta\Source Files diff --git a/src/meta/meta.h b/src/meta/meta.h index 5005081f..7be55948 100644 --- a/src/meta/meta.h +++ b/src/meta/meta.h @@ -697,4 +697,5 @@ VGMSTREAM * init_vgmstream_kma9(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_fsb_encrypted(STREAMFILE * streamFile); +VGMSTREAM * init_vgmstream_xwc(STREAMFILE *streamFile); #endif /*_META_H*/ diff --git a/src/meta/xwc.c b/src/meta/xwc.c new file mode 100644 index 00000000..a9f7b4c5 --- /dev/null +++ b/src/meta/xwc.c @@ -0,0 +1,108 @@ +#include "meta.h" +#include "../coding/coding.h" + +/* .XWC - Starbreeze games [Chronicles of Riddick: Assault on Dark Athena, Syndicate] */ +VGMSTREAM * init_vgmstream_xwc(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + off_t start_offset; + size_t data_size; + int loop_flag, channel_count, codec; + + /* check extensions (.xwc is the extension of the bigfile, individual files don't have one) */ + if ( !check_extensions(streamFile,"xwc")) + goto fail; + + if(read_32bitBE(0x00,streamFile) != 0x00040000 && /* version? */ + read_32bitBE(0x04,streamFile) != 0x00900000) + goto fail; + + data_size = read_32bitLE(0x08, streamFile); /* including subheader */ + channel_count = read_32bitLE(0x0c, streamFile); + /* 0x10: num_samples */ + /* 0x14: 0x8000? */ + codec = read_32bitBE(0x24, streamFile); + /* 0x28: num_samples */ + /* 0x2c: config data? (first nibble: 0x4=mono, 0x8=stereo) */ + /* 0x30+: codec dependant */ + loop_flag = 0; /* seemingly not in the file */ + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count,loop_flag); + if (!vgmstream) goto fail; + + vgmstream->num_samples = read_32bitLE(0x28, streamFile); + vgmstream->meta_type = meta_XWC; + + switch(codec) { +#ifdef VGM_USE_MPEG + case 0x4D504547: { /* "MPEG" (PS3) */ + mpeg_custom_config cfg = {0}; + + start_offset = 0x800; + vgmstream->num_samples = read_32bitLE(0x30, streamFile); /* with encoder delay */ //todo improve + cfg.data_size = read_32bitLE(0x34, streamFile); //data_size - 0x28; + + vgmstream->codec_data = init_mpeg_custom(streamFile, start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_STANDARD, &cfg); + if (!vgmstream->codec_data) goto fail; + vgmstream->layout_type = layout_none; + + vgmstream->sample_rate = ((mpeg_codec_data*)vgmstream->codec_data)->sample_rate_per_frame; + break; + } +#endif +#ifdef VGM_USE_FFMPEG + case 0x584D4100: { /* "XMA\0" (X360) */ + uint8_t buf[0x100]; + int32_t bytes, seek_size, block_size, block_count, sample_rate; + + seek_size = read_32bitLE(0x30, streamFile); + start_offset = 0x34 + seek_size + read_32bitLE(0x34+seek_size, streamFile) + 0x08; + start_offset += (start_offset % 0x800) ? 0x800 - (start_offset % 0x800) : 0; /* padded */ + + sample_rate = read_32bitBE(0x34+seek_size+0x10, streamFile); + block_size = read_32bitBE(0x34+seek_size+0x1c, streamFile); + block_count = read_32bitBE(0x34+seek_size+0x28, streamFile); + /* others: scrambled RIFF fmt BE values */ + + bytes = ffmpeg_make_riff_xma2(buf,0x100, vgmstream->num_samples, data_size, vgmstream->channels, sample_rate, block_count, block_size); + if (bytes <= 0) goto fail; + + vgmstream->codec_data = init_ffmpeg_header_offset(streamFile, buf,bytes, start_offset,data_size - start_offset - 0x28); + if (!vgmstream->codec_data) goto fail; + vgmstream->coding_type = coding_FFmpeg; + vgmstream->layout_type = layout_none; + + vgmstream->sample_rate = sample_rate; + break; + } + + case 0x564F5242: { /* "VORB" (PC) */ + start_offset = 0x30; + + vgmstream->codec_data = init_ffmpeg_offset(streamFile, start_offset, data_size - start_offset - 0x28); + if ( !vgmstream->codec_data ) goto fail; + vgmstream->coding_type = coding_FFmpeg; + vgmstream->layout_type = layout_none; + + vgmstream->sample_rate = read_32bitLE(start_offset + 0x28, streamFile); + break; + } +#endif + default: + goto fail; + } + + if (vgmstream->sample_rate != 48000) { /* get from config data instead of codecs? */ + VGM_LOG("XWC: unexpected sample rate %i\n",vgmstream->sample_rate); + goto fail; + } + + if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) ) + goto fail; + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} diff --git a/src/vgmstream.c b/src/vgmstream.c index 48cb3bcb..0fa4fd75 100644 --- a/src/vgmstream.c +++ b/src/vgmstream.c @@ -375,6 +375,7 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_mogg, init_vgmstream_kma9, init_vgmstream_fsb_encrypted, + init_vgmstream_xwc, init_vgmstream_txth, /* should go at the end (lower priority) */ #ifdef VGM_USE_FFMPEG diff --git a/src/vgmstream.h b/src/vgmstream.h index b474a4e7..e17dfb10 100644 --- a/src/vgmstream.h +++ b/src/vgmstream.h @@ -650,12 +650,13 @@ typedef enum { meta_OGG_SLI, /* Ogg Vorbis file w/ companion .sli for looping */ meta_OGG_SLI2, /* Ogg Vorbis file w/ different styled .sli for looping */ meta_OGG_SFL, /* Ogg Vorbis file w/ .sfl (RIFF SFPL) for looping */ - meta_OGG_UM3, /* Ogg Vorbis with optional first 0x800 bytes XOR 0xFF */ - meta_OGG_KOVS, /* Ogg Vorbis with extra header and 0x100 bytes XOR */ - meta_OGG_PSYCHIC, /* Ogg Vorbis with all bytes -0x23 */ - meta_OGG_SNGW, /* Ogg Vorbis with optional key XOR + nibble swap (Capcom PC games) */ - meta_OGG_ISD, /* Ogg Vorbis with key XOR (Azure Striker Gunvolt PC) */ + meta_OGG_UM3, /* Ogg Vorbis with optional encryption */ + meta_OGG_KOVS, /* Ogg Vorbis with encryption (Koei Tecmo Games) */ + meta_OGG_PSYCHIC, /* Ogg Vorbis with encryption */ + meta_OGG_SNGW, /* Ogg Vorbis with optional encryption (Capcom PC games) */ + meta_OGG_ISD, /* Ogg Vorbis with encryption (Azure Striker Gunvolt PC) */ meta_KMA9, /* Koei Tecmo [Nobunaga no Yabou - Souzou (Vita)] */ + meta_XWC, /* Starbreeze games */ #ifdef VGM_USE_MP4V2 meta_MP4, /* AAC (iOS) */ From 88999d570dd9bf4a3d8140b5cddf0c0afbcfa625 Mon Sep 17 00:00:00 2001 From: bnnm Date: Sat, 27 Jan 2018 11:07:45 +0100 Subject: [PATCH 04/21] Add STREAMFILE for fake names (used to call other meta) --- src/streamfile.c | 97 +++++++++++++++++++++++++++++++++++++++++++++++- src/streamfile.h | 6 +++ 2 files changed, 101 insertions(+), 2 deletions(-) diff --git a/src/streamfile.c b/src/streamfile.c index 61e8b3bf..c994a470 100644 --- a/src/streamfile.c +++ b/src/streamfile.c @@ -331,8 +331,18 @@ static void clamp_get_realname(CLAMP_STREAMFILE *streamfile, char *buffer, size_ streamfile->inner_sf->get_realname(streamfile->inner_sf, buffer, length); /* default */ } static STREAMFILE *clamp_open(CLAMP_STREAMFILE *streamfile, const char * const filename, size_t buffersize) { - STREAMFILE *new_inner_sf = streamfile->inner_sf->open(streamfile->inner_sf,filename,buffersize); - return open_clamp_streamfile(new_inner_sf, streamfile->start, streamfile->size); + char original_filename[PATH_LIMIT]; + STREAMFILE *new_inner_sf; + + new_inner_sf = streamfile->inner_sf->open(streamfile->inner_sf,filename,buffersize); + streamfile->inner_sf->get_name(streamfile->inner_sf, original_filename, PATH_LIMIT);//todo realname? + + /* detect re-opening the file and don't clamp */ + if (strcmp(filename, original_filename) == 0) { + return open_clamp_streamfile(new_inner_sf, streamfile->start, streamfile->size); + } else { + return new_inner_sf; + } } static void clamp_close(CLAMP_STREAMFILE *streamfile) { streamfile->inner_sf->close(streamfile->inner_sf); @@ -343,6 +353,7 @@ STREAMFILE *open_clamp_streamfile(STREAMFILE *streamfile, off_t start, size_t si CLAMP_STREAMFILE *this_sf; if (!streamfile || !size || start > size) return NULL; + if (start + size > get_streamfile_size(streamfile)) return NULL; this_sf = calloc(1,sizeof(CLAMP_STREAMFILE)); if (!this_sf) return NULL; @@ -391,6 +402,7 @@ static void io_get_realname(IO_STREAMFILE *streamfile, char *buffer, size_t leng streamfile->inner_sf->get_realname(streamfile->inner_sf, buffer, length); /* default */ } static STREAMFILE *io_open(IO_STREAMFILE *streamfile, const char * const filename, size_t buffersize) { + //todo should have some flag to decide if opening other files with IO STREAMFILE *new_inner_sf = streamfile->inner_sf->open(streamfile->inner_sf,filename,buffersize); return open_io_streamfile(new_inner_sf, streamfile->data, streamfile->data_size, streamfile->read_callback); } @@ -436,6 +448,87 @@ STREAMFILE *open_io_streamfile(STREAMFILE *streamfile, void* data, size_t data_s /* **************************************************** */ +typedef struct { + STREAMFILE sf; + + STREAMFILE *inner_sf; + char fakename[PATH_LIMIT]; +} FAKENAME_STREAMFILE; + +static size_t fakename_read(FAKENAME_STREAMFILE *streamfile, uint8_t * dest, off_t offset, size_t length) { + return streamfile->inner_sf->read(streamfile->inner_sf, dest, offset, length); /* default */ +} +static size_t fakename_get_size(FAKENAME_STREAMFILE * streamfile) { + return streamfile->inner_sf->get_size(streamfile->inner_sf); /* default */ +} +static size_t fakename_get_offset(FAKENAME_STREAMFILE * streamfile) { + return streamfile->inner_sf->get_offset(streamfile->inner_sf); /* default */ +} +static void fakename_get_name(FAKENAME_STREAMFILE *streamfile, char *buffer, size_t length) { + strncpy(buffer,streamfile->fakename,length);//todo safe ops + buffer[length-1]='\0'; +} +static void fakename_get_realname(FAKENAME_STREAMFILE *streamfile, char *buffer, size_t length) { + fakename_get_name(streamfile, buffer, length); +} +static STREAMFILE *fakename_open(FAKENAME_STREAMFILE *streamfile, const char * const filename, size_t buffersize) { + /* detect re-opening the file */ + if (strcmp(filename, streamfile->fakename) == 0) { + STREAMFILE *new_inner_sf; + char original_filename[PATH_LIMIT]; + + streamfile->inner_sf->get_name(streamfile->inner_sf, original_filename, PATH_LIMIT);//todo realname? + new_inner_sf = streamfile->inner_sf->open(streamfile->inner_sf, original_filename, buffersize); + return open_fakename_streamfile(new_inner_sf, streamfile->fakename, NULL); + } + else { + return streamfile->inner_sf->open(streamfile->inner_sf, filename, buffersize); + } +} +static void fakename_close(FAKENAME_STREAMFILE *streamfile) { + streamfile->inner_sf->close(streamfile->inner_sf); + free(streamfile); +} + +STREAMFILE *open_fakename_streamfile(STREAMFILE *streamfile, char * fakename, char* fakeext) { + FAKENAME_STREAMFILE *this_sf; + + if (!streamfile || (!fakename && !fakeext)) return NULL; + + this_sf = calloc(1,sizeof(FAKENAME_STREAMFILE)); + if (!this_sf) return NULL; + + /* set callbacks and internals */ + this_sf->sf.read = (void*)fakename_read; + this_sf->sf.get_size = (void*)fakename_get_size; + this_sf->sf.get_offset = (void*)fakename_get_offset; + this_sf->sf.get_name = (void*)fakename_get_name; + this_sf->sf.get_realname = (void*)fakename_get_realname; + this_sf->sf.open = (void*)fakename_open; + this_sf->sf.close = (void*)fakename_close; + this_sf->sf.stream_index = streamfile->stream_index; + + this_sf->inner_sf = streamfile; + + //todo use safe ops, this ain't easy + /* copy passed name or retain current, and swap extension if expected */ + if (fakename) { + strcpy(this_sf->fakename,fakename); + } else { + streamfile->get_name(streamfile, this_sf->fakename, PATH_LIMIT);//todo realname? + } + if (fakeext) { + char * ext = strrchr(this_sf->fakename,'.'); + if (ext != NULL) + ext[1] = '\0'; /* truncate past dot */ + strcat(this_sf->fakename, fakeext); + } + + return &this_sf->sf; +} + +/* **************************************************** */ + /* Read a line into dst. The source files are lines separated by CRLF (Windows) / LF (Unux) / CR (Mac). * The line will be null-terminated and CR/LF removed if found. * diff --git a/src/streamfile.h b/src/streamfile.h index fa9da54f..90aeefe7 100644 --- a/src/streamfile.h +++ b/src/streamfile.h @@ -81,6 +81,12 @@ STREAMFILE *open_clamp_streamfile(STREAMFILE *streamfile, off_t start, size_t si * Can be used with subfiles inside a bigger file, so it looks standard to a meta. */ STREAMFILE *open_io_streamfile(STREAMFILE *streamfile, void* data, size_t data_size, void* read_callback);//void* size_callback, void* seek_callback); +/* A STREAMFILE that reports a fake name, but still re-opens itself properly. + * Can be used to trick a meta's extension check (to call from another, with a modified SF). + * When fakename isn't supplied it's read from the streamfile, and the extension swapped with fakeext. + * If the fakename is an existing file, open won't work on it as it'll reopen the fake-named streamfile. */ +STREAMFILE *open_fakename_streamfile(STREAMFILE *streamfile, char * fakename, char * fakeext); + /* close a file, destroy the STREAMFILE object */ static inline void close_streamfile(STREAMFILE * streamfile) { From d84929b4994e3ced5c4ed22b00dc49b7256bd201 Mon Sep 17 00:00:00 2001 From: bnnm Date: Sat, 27 Jan 2018 11:08:00 +0100 Subject: [PATCH 05/21] Add comment --- src/meta/sxd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/meta/sxd.c b/src/meta/sxd.c index 26e9904a..c0173294 100644 --- a/src/meta/sxd.c +++ b/src/meta/sxd.c @@ -148,7 +148,7 @@ VGMSTREAM * init_vgmstream_sxd(STREAMFILE *streamFile) { break; } #endif - + //case 0x28: /* dummy codec? (found with 0 samples) */ default: VGM_LOG("SXD: unknown codec 0x%x\n", codec); goto fail; From d161050fcac106be293d80d6a9a03aa8e7f90b21 Mon Sep 17 00:00:00 2001 From: bnnm Date: Sat, 27 Jan 2018 11:08:51 +0100 Subject: [PATCH 06/21] Update STREAMFILE code a bit --- src/meta/fsb.c | 46 +++++++--- src/meta/fsb_encrypted.c | 177 ++++++++++++++++++++++----------------- src/meta/fsb_keys.h | 2 +- 3 files changed, 135 insertions(+), 90 deletions(-) diff --git a/src/meta/fsb.c b/src/meta/fsb.c index 85e09ae6..94c321e8 100644 --- a/src/meta/fsb.c +++ b/src/meta/fsb.c @@ -104,8 +104,8 @@ VGMSTREAM * init_vgmstream_fsb(STREAMFILE *streamFile) { fsb_header fsb = {0}; - /* check extensions (.wii: fsb4_wav? .bnk = Hard Corps Uprising PS3) */ - if ( !check_extensions(streamFile, "fsb,wii,bnk") ) + /* check extensions (.bnk = Hard Corps Uprising PS3) */ + if ( !check_extensions(streamFile, "fsb,bnk") ) goto fail; /* check header */ @@ -363,13 +363,15 @@ fail: } +static STREAMFILE* setup_fsb4_wav_streamfile(STREAMFILE *streamfile, off_t subfile_offset, size_t subfile_size); + /* FSB4 with "\0WAV" Header, found in Deadly Creatures (Wii). * Has a 0x10 BE header that holds the filesize (unsure if this is from a proper rip). */ VGMSTREAM * init_vgmstream_fsb4_wav(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; - STREAMFILE *custom_streamFile = NULL; - off_t custom_start = 0x10; - size_t custom_size = get_streamfile_size(streamFile) - 0x10 - 0x10; //todo + STREAMFILE *test_streamFile = NULL; + off_t subfile_start = 0x10; + size_t subfile_size = get_streamfile_size(streamFile) - 0x10 - 0x10; //todo /* check extensions */ if ( !check_extensions(streamFile, "fsb,wii") ) @@ -379,17 +381,41 @@ VGMSTREAM * init_vgmstream_fsb4_wav(STREAMFILE *streamFile) { goto fail; /* parse FSB subfile */ - custom_streamFile = open_clamp_streamfile(open_wrap_streamfile(streamFile), custom_start,custom_size); - if (!custom_streamFile) goto fail; + test_streamFile = setup_fsb4_wav_streamfile(streamFile, subfile_start,subfile_size); + if (!test_streamFile) goto fail; - vgmstream = init_vgmstream_fsb(custom_streamFile); + vgmstream = init_vgmstream_fsb(test_streamFile); if (!vgmstream) goto fail; - close_streamfile(custom_streamFile); + /* init the VGMSTREAM */ + close_streamfile(test_streamFile); return vgmstream; fail: - close_streamfile(custom_streamFile); + close_streamfile(test_streamFile); close_vgmstream(vgmstream); return NULL; } + +static STREAMFILE* setup_fsb4_wav_streamfile(STREAMFILE *streamFile, off_t subfile_offset, size_t subfile_size) { + STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL; + + /* setup subfile */ + new_streamFile = open_wrap_streamfile(streamFile); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + new_streamFile = open_clamp_streamfile(temp_streamFile, subfile_offset,subfile_size); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + new_streamFile = open_fakename_streamfile(temp_streamFile, NULL,"fsb"); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + return temp_streamFile; + +fail: + close_streamfile(temp_streamFile); + return NULL; +} diff --git a/src/meta/fsb_encrypted.c b/src/meta/fsb_encrypted.c index 20e0e352..98af7df9 100644 --- a/src/meta/fsb_encrypted.c +++ b/src/meta/fsb_encrypted.c @@ -3,58 +3,12 @@ #define FSB_KEY_MAX 128 /* probably 32 */ -typedef struct { - uint8_t fsbkey[FSB_KEY_MAX]; - size_t fsbkey_size; - int is_alt; -} fsb_decryption_data; - -static size_t fsb_decryption_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length, fsb_decryption_data* data) { - static const unsigned char reverse_bits_table[] = { /* LUT to simplify, could use some bitswap function */ - 0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0, - 0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8, - 0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4, 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4, - 0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC, 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC, - 0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2, 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2, - 0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA, 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA, - 0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6, 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6, - 0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE, 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE, - 0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1, 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1, - 0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9, 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9, - 0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5, 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5, - 0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED, 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD, - 0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3, 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3, - 0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB, 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB, - 0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7, 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7, - 0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF - }; - size_t bytes_read; - int i; - - bytes_read = streamfile->read(streamfile, dest, offset, length); - - /* decrypt data (inverted bits and xor) */ - for (i = 0; i < bytes_read; i++) { - uint8_t xor = data->fsbkey[(offset + i) % data->fsbkey_size]; - uint8_t val = dest[i]; - if (data->is_alt) { - dest[i] = reverse_bits_table[val ^ xor]; - } - else { - dest[i] = reverse_bits_table[val] ^ xor; - } - } - - return bytes_read; -} - +static STREAMFILE* setup_fsb_streamfile(STREAMFILE *streamFile, const uint8_t * key, size_t key_size, int is_alt); /* fully encrypted FSBs */ VGMSTREAM * init_vgmstream_fsb_encrypted(STREAMFILE * streamFile) { VGMSTREAM * vgmstream = NULL; - fsb_decryption_data io_data = {0}; - size_t io_data_size = sizeof(fsb_decryption_data); /* check extensions */ if ( !check_extensions(streamFile, "fsb") ) @@ -67,35 +21,29 @@ VGMSTREAM * init_vgmstream_fsb_encrypted(STREAMFILE * streamFile) { /* try fsbkey + all combinations of FSB4/5 and decryption algorithms */ { - io_data.fsbkey_size = read_key_file(io_data.fsbkey, FSB_KEY_MAX, streamFile); - if (io_data.fsbkey_size) { + STREAMFILE *temp_streamFile = NULL; + uint8_t key[FSB_KEY_MAX]; + size_t key_size = read_key_file(key, FSB_KEY_MAX, streamFile); + if (key_size) { { - STREAMFILE *custom_streamFile = NULL; + temp_streamFile = setup_fsb_streamfile(streamFile, key,key_size, 0); + if (!temp_streamFile) goto fail; - io_data.is_alt = 0; - custom_streamFile = open_io_streamfile(open_wrap_streamfile(streamFile), &io_data,io_data_size, fsb_decryption_read); - if (!custom_streamFile) goto fail; + if (!vgmstream) vgmstream = init_vgmstream_fsb(temp_streamFile); + if (!vgmstream) vgmstream = init_vgmstream_fsb5(temp_streamFile); - if (!vgmstream) vgmstream = init_vgmstream_fsb(custom_streamFile); - if (!vgmstream) vgmstream = init_vgmstream_fsb5(custom_streamFile); - - close_streamfile(custom_streamFile); + close_streamfile(temp_streamFile); } - if (!vgmstream) { - STREAMFILE *custom_streamFile = NULL; + temp_streamFile = setup_fsb_streamfile(streamFile, key,key_size, 1); + if (!temp_streamFile) goto fail; - io_data.is_alt = 1; - custom_streamFile = open_io_streamfile(open_wrap_streamfile(streamFile), &io_data,io_data_size, fsb_decryption_read); - if (!custom_streamFile) goto fail; + if (!vgmstream) vgmstream = init_vgmstream_fsb(temp_streamFile); + if (!vgmstream) vgmstream = init_vgmstream_fsb5(temp_streamFile); - vgmstream = init_vgmstream_fsb(custom_streamFile); - if (!vgmstream) - vgmstream = init_vgmstream_fsb5(custom_streamFile); - - close_streamfile(custom_streamFile); + close_streamfile(temp_streamFile); } } } @@ -104,26 +52,22 @@ VGMSTREAM * init_vgmstream_fsb_encrypted(STREAMFILE * streamFile) { /* try all keys until one works */ if (!vgmstream) { int i; - STREAMFILE *custom_streamFile = NULL; + STREAMFILE *temp_streamFile = NULL; for (i = 0; i < fsbkey_list_count; i++) { - if (!fsbkey_list[i].fsbkey_size || fsbkey_list[i].fsbkey_size > FSB_KEY_MAX) goto fail; + fsbkey_info entry = fsbkey_list[i]; + ;VGM_LOG("fsbkey: size=%i, is_fsb5=%i, is_alt=%i\n", entry.fsbkey_size,entry.is_fsb5, entry.is_alt); - memcpy(io_data.fsbkey, fsbkey_list[i].fsbkey, fsbkey_list[i].fsbkey_size); - io_data.fsbkey_size = fsbkey_list[i].fsbkey_size; - io_data.is_alt = fsbkey_list[i].is_alt; - //;VGM_LOG("fsbkey: size=%i, is_fsb5=%i, is_alt=%i\n", fsbkey_list[i].fsbkey_size,fsbkey_list[i].is_fsb5, fsbkey_list[i].is_alt); - - custom_streamFile = open_io_streamfile(open_wrap_streamfile(streamFile), &io_data,io_data_size, fsb_decryption_read); - if (!custom_streamFile) goto fail; + temp_streamFile = setup_fsb_streamfile(streamFile, entry.fsbkey, entry.fsbkey_size, entry.is_alt); + if (!temp_streamFile) goto fail; if (fsbkey_list[i].is_fsb5) { - vgmstream = init_vgmstream_fsb5(custom_streamFile); + vgmstream = init_vgmstream_fsb5(temp_streamFile); } else { - vgmstream = init_vgmstream_fsb(custom_streamFile); + vgmstream = init_vgmstream_fsb(temp_streamFile); } - close_streamfile(custom_streamFile); + close_streamfile(temp_streamFile); if (vgmstream) break; } } @@ -137,3 +81,78 @@ fail: close_vgmstream(vgmstream); return NULL; } + + +typedef struct { + uint8_t key[FSB_KEY_MAX]; + size_t key_size; + int is_alt; +} fsb_decryption_data; + +/* Encrypted FSB info from guessfsb and fsbext */ +static size_t fsb_decryption_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length, fsb_decryption_data* data) { + static const unsigned char reverse_bits_table[] = { /* LUT to simplify, could use some bitswap function */ + 0x00,0x80,0x40,0xC0,0x20,0xA0,0x60,0xE0,0x10,0x90,0x50,0xD0,0x30,0xB0,0x70,0xF0, + 0x08,0x88,0x48,0xC8,0x28,0xA8,0x68,0xE8,0x18,0x98,0x58,0xD8,0x38,0xB8,0x78,0xF8, + 0x04,0x84,0x44,0xC4,0x24,0xA4,0x64,0xE4,0x14,0x94,0x54,0xD4,0x34,0xB4,0x74,0xF4, + 0x0C,0x8C,0x4C,0xCC,0x2C,0xAC,0x6C,0xEC,0x1C,0x9C,0x5C,0xDC,0x3C,0xBC,0x7C,0xFC, + 0x02,0x82,0x42,0xC2,0x22,0xA2,0x62,0xE2,0x12,0x92,0x52,0xD2,0x32,0xB2,0x72,0xF2, + 0x0A,0x8A,0x4A,0xCA,0x2A,0xAA,0x6A,0xEA,0x1A,0x9A,0x5A,0xDA,0x3A,0xBA,0x7A,0xFA, + 0x06,0x86,0x46,0xC6,0x26,0xA6,0x66,0xE6,0x16,0x96,0x56,0xD6,0x36,0xB6,0x76,0xF6, + 0x0E,0x8E,0x4E,0xCE,0x2E,0xAE,0x6E,0xEE,0x1E,0x9E,0x5E,0xDE,0x3E,0xBE,0x7E,0xFE, + 0x01,0x81,0x41,0xC1,0x21,0xA1,0x61,0xE1,0x11,0x91,0x51,0xD1,0x31,0xB1,0x71,0xF1, + 0x09,0x89,0x49,0xC9,0x29,0xA9,0x69,0xE9,0x19,0x99,0x59,0xD9,0x39,0xB9,0x79,0xF9, + 0x05,0x85,0x45,0xC5,0x25,0xA5,0x65,0xE5,0x15,0x95,0x55,0xD5,0x35,0xB5,0x75,0xF5, + 0x0D,0x8D,0x4D,0xCD,0x2D,0xAD,0x6D,0xED,0x1D,0x9D,0x5D,0xDD,0x3D,0xBD,0x7D,0xFD, + 0x03,0x83,0x43,0xC3,0x23,0xA3,0x63,0xE3,0x13,0x93,0x53,0xD3,0x33,0xB3,0x73,0xF3, + 0x0B,0x8B,0x4B,0xCB,0x2B,0xAB,0x6B,0xEB,0x1B,0x9B,0x5B,0xDB,0x3B,0xBB,0x7B,0xFB, + 0x07,0x87,0x47,0xC7,0x27,0xA7,0x67,0xE7,0x17,0x97,0x57,0xD7,0x37,0xB7,0x77,0xF7, + 0x0F,0x8F,0x4F,0xCF,0x2F,0xAF,0x6F,0xEF,0x1F,0x9F,0x5F,0xDF,0x3F,0xBF,0x7F,0xFF + }; + size_t bytes_read; + int i; + + bytes_read = streamfile->read(streamfile, dest, offset, length); + + /* decrypt data (inverted bits and xor) */ + for (i = 0; i < bytes_read; i++) { + uint8_t xor = data->key[(offset + i) % data->key_size]; + uint8_t val = dest[i]; + if (data->is_alt) { + dest[i] = reverse_bits_table[val ^ xor]; + } + else { + dest[i] = reverse_bits_table[val] ^ xor; + } + } + + return bytes_read; +} + +static STREAMFILE* setup_fsb_streamfile(STREAMFILE *streamFile, const uint8_t * key, size_t key_size, int is_alt) { + STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL; + fsb_decryption_data io_data = {0}; + size_t io_data_size = sizeof(fsb_decryption_data); + + /* setup decryption with key (external) */ + if (!key_size || key_size > FSB_KEY_MAX) goto fail; + + memcpy(io_data.key, key, key_size); + io_data.key_size = key_size; + io_data.is_alt = is_alt; + + /* setup subfile */ + new_streamFile = open_wrap_streamfile(streamFile); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + new_streamFile = open_io_streamfile(temp_streamFile, &io_data,io_data_size, fsb_decryption_read); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + return temp_streamFile; + +fail: + close_streamfile(temp_streamFile); + return NULL; +} diff --git a/src/meta/fsb_keys.h b/src/meta/fsb_keys.h index e250ae10..1b3a6081 100644 --- a/src/meta/fsb_keys.h +++ b/src/meta/fsb_keys.h @@ -30,7 +30,7 @@ static const uint8_t key_gfs[] = { 0x25,0x6C,0x41,0x6E,0x32,0x7B,0x50,0x69,0x2A, /* RevHeadz Engine Sounds (Mobile) */ //"1^7%82#&5$~/8sz" static const uint8_t key_rev[] = { 0x31,0x5E,0x37,0x25,0x38,0x32,0x23,0x26,0x35,0x24,0x7E,0x2F,0x38,0x73,0x7A }; -/* Dark Souls 3 (PC) */ //"FDPrVuT4fAFvdHJYAgyMzRF4EcBAnKg" //todo ps4? +/* Dark Souls 3 (PC) */ //"FDPrVuT4fAFvdHJYAgyMzRF4EcBAnKg" static const uint8_t key_ds3[] = { 0x46,0x44,0x50,0x72,0x56,0x75,0x54,0x34,0x66,0x41,0x46,0x76,0x64,0x48,0x4A,0x59,0x41,0x67,0x79,0x4D,0x7A,0x52,0x46,0x34,0x45,0x63,0x42,0x41,0x6E,0x4B,0x67 }; /* Mortal Kombat X */ From 861e1ea796a99b779454aa797cc217794c25f1be Mon Sep 17 00:00:00 2001 From: bnnm Date: Sat, 27 Jan 2018 11:53:15 +0100 Subject: [PATCH 07/21] Add Koei Tecmo ATL3 subfiles [One Piece Pirate Warriors (PS3)] --- src/formats.c | 1 + src/libvgmstream.vcproj | 4 ++ src/libvgmstream.vcxproj | 1 + src/libvgmstream.vcxproj.filters | 3 ++ src/meta/atsl3.c | 76 ++++++++++++++++++++++++++++++++ src/meta/meta.h | 2 + src/streamfile.c | 2 +- src/vgmstream.c | 1 + 8 files changed, 89 insertions(+), 1 deletion(-) create mode 100644 src/meta/atsl3.c diff --git a/src/formats.c b/src/formats.c index 3981adfc..07079ef3 100644 --- a/src/formats.c +++ b/src/formats.c @@ -43,6 +43,7 @@ static const char* extension_list[] = { "ast", "at3", "at9", + "atsl3", "aud", "aus", "awc", diff --git a/src/libvgmstream.vcproj b/src/libvgmstream.vcproj index 53fd2f38..63d27174 100644 --- a/src/libvgmstream.vcproj +++ b/src/libvgmstream.vcproj @@ -276,6 +276,10 @@ RelativePath=".\meta\ast.c" > + + diff --git a/src/libvgmstream.vcxproj b/src/libvgmstream.vcxproj index 322d932f..b061f5f6 100644 --- a/src/libvgmstream.vcxproj +++ b/src/libvgmstream.vcxproj @@ -186,6 +186,7 @@ + diff --git a/src/libvgmstream.vcxproj.filters b/src/libvgmstream.vcxproj.filters index b007e054..e495ef3c 100644 --- a/src/libvgmstream.vcxproj.filters +++ b/src/libvgmstream.vcxproj.filters @@ -169,6 +169,9 @@ meta\Source Files + + meta\Source Files + meta\Source Files diff --git a/src/meta/atsl3.c b/src/meta/atsl3.c new file mode 100644 index 00000000..722c3f77 --- /dev/null +++ b/src/meta/atsl3.c @@ -0,0 +1,76 @@ +#include "meta.h" +#include "../coding/coding.h" + +static STREAMFILE* setup_atsl3_streamfile(STREAMFILE *streamFile, off_t subfile_offset, size_t subfile_size); + +/* .ATSL3 - Koei Tecmo container of multiple .AT3 [One Piece Pirate Warriors (PS3)] */ +VGMSTREAM * init_vgmstream_atsl3(STREAMFILE *streamFile) { + VGMSTREAM *vgmstream = NULL; + STREAMFILE *temp_streamFile = NULL; + int total_subsongs, target_subsong = streamFile->stream_index; + off_t subfile_offset; + size_t subfile_size, header_size, entry_size; + + /* check extensions */ + if ( !check_extensions(streamFile,"atsl3")) + goto fail; + if (read_32bitBE(0x00,streamFile) != 0x4154534C) /* "ATSL" */ + goto fail; + + /* main header (LE) */ + header_size = read_32bitLE(0x04,streamFile); + /* 0x08/0c: flags?, 0x10: some size? */ + total_subsongs = read_32bitLE(0x14,streamFile); + entry_size = read_32bitLE(0x18,streamFile); + /* 0x1c: null, 0x20: subheader size, 0x24/28: null */ + + if (target_subsong == 0) target_subsong = 1; + if (target_subsong > total_subsongs || total_subsongs <= 0) goto fail; + + /* entry header (BE) */ + /* 0x00: id */ + subfile_offset = read_32bitBE(header_size + (target_subsong-1)*entry_size + 0x04,streamFile); + subfile_size = read_32bitBE(header_size + (target_subsong-1)*entry_size + 0x08,streamFile); + /* 0x08+: sample rate/num_samples/loop_start/etc, matching subfile header */ + /* some kind of seek/switch table follows */ + + temp_streamFile = setup_atsl3_streamfile(streamFile, subfile_offset,subfile_size); + if (!temp_streamFile) goto fail; + + /* init the VGMSTREAM */ + vgmstream = init_vgmstream_riff(temp_streamFile); + if (!vgmstream) goto fail; + vgmstream->num_streams = total_subsongs; + + close_streamfile(temp_streamFile); + return vgmstream; + +fail: + close_streamfile(temp_streamFile); + close_vgmstream(vgmstream); + return NULL; +} + + +static STREAMFILE* setup_atsl3_streamfile(STREAMFILE *streamFile, off_t subfile_offset, size_t subfile_size) { + STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL; + + /* setup subfile */ + new_streamFile = open_wrap_streamfile(streamFile); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + new_streamFile = open_clamp_streamfile(temp_streamFile, subfile_offset,subfile_size); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + new_streamFile = open_fakename_streamfile(temp_streamFile, NULL,"at3"); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + return temp_streamFile; + +fail: + close_streamfile(temp_streamFile); + return NULL; +} diff --git a/src/meta/meta.h b/src/meta/meta.h index 7be55948..0199b76b 100644 --- a/src/meta/meta.h +++ b/src/meta/meta.h @@ -698,4 +698,6 @@ VGMSTREAM * init_vgmstream_kma9(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_fsb_encrypted(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_xwc(STREAMFILE *streamFile); + +VGMSTREAM * init_vgmstream_atsl3(STREAMFILE *streamFile); #endif /*_META_H*/ diff --git a/src/streamfile.c b/src/streamfile.c index c994a470..5ac252c2 100644 --- a/src/streamfile.c +++ b/src/streamfile.c @@ -352,7 +352,7 @@ static void clamp_close(CLAMP_STREAMFILE *streamfile) { STREAMFILE *open_clamp_streamfile(STREAMFILE *streamfile, off_t start, size_t size) { CLAMP_STREAMFILE *this_sf; - if (!streamfile || !size || start > size) return NULL; + if (!streamfile || !size) return NULL; if (start + size > get_streamfile_size(streamfile)) return NULL; this_sf = calloc(1,sizeof(CLAMP_STREAMFILE)); diff --git a/src/vgmstream.c b/src/vgmstream.c index 0fa4fd75..a25a4e67 100644 --- a/src/vgmstream.c +++ b/src/vgmstream.c @@ -376,6 +376,7 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_kma9, init_vgmstream_fsb_encrypted, init_vgmstream_xwc, + init_vgmstream_atsl3, init_vgmstream_txth, /* should go at the end (lower priority) */ #ifdef VGM_USE_FFMPEG From 561c3fa8c17624b8ecbc31835062d4b33b80c35b Mon Sep 17 00:00:00 2001 From: bnnm Date: Sat, 27 Jan 2018 12:21:25 +0100 Subject: [PATCH 08/21] Add Nippon Ichi SPS subfiles [ClaDun (PSP)] --- src/libvgmstream.vcproj | 4 ++ src/libvgmstream.vcxproj | 1 + src/libvgmstream.vcxproj.filters | 3 ++ src/meta/meta.h | 2 + src/meta/sps_n1.c | 83 ++++++++++++++++++++++++++++++++ src/vgmstream.c | 1 + 6 files changed, 94 insertions(+) create mode 100644 src/meta/sps_n1.c diff --git a/src/libvgmstream.vcproj b/src/libvgmstream.vcproj index 63d27174..fb99c94c 100644 --- a/src/libvgmstream.vcproj +++ b/src/libvgmstream.vcproj @@ -1182,6 +1182,10 @@ RelativePath=".\meta\sli.c" > + + diff --git a/src/libvgmstream.vcxproj b/src/libvgmstream.vcxproj index b061f5f6..6340a83f 100644 --- a/src/libvgmstream.vcxproj +++ b/src/libvgmstream.vcxproj @@ -377,6 +377,7 @@ + diff --git a/src/libvgmstream.vcxproj.filters b/src/libvgmstream.vcxproj.filters index e495ef3c..1e5f45b7 100644 --- a/src/libvgmstream.vcxproj.filters +++ b/src/libvgmstream.vcxproj.filters @@ -721,6 +721,9 @@ meta\Source Files + + meta\Source Files + meta\Source Files diff --git a/src/meta/meta.h b/src/meta/meta.h index 0199b76b..621a16d5 100644 --- a/src/meta/meta.h +++ b/src/meta/meta.h @@ -700,4 +700,6 @@ VGMSTREAM * init_vgmstream_fsb_encrypted(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_xwc(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_atsl3(STREAMFILE *streamFile); + +VGMSTREAM * init_vgmstream_sps_n1(STREAMFILE *streamFile); #endif /*_META_H*/ diff --git a/src/meta/sps_n1.c b/src/meta/sps_n1.c new file mode 100644 index 00000000..eb1a2e69 --- /dev/null +++ b/src/meta/sps_n1.c @@ -0,0 +1,83 @@ +#include "meta.h" +#include "../coding/coding.h" + +static STREAMFILE* setup_sps_streamfile(STREAMFILE *streamfile, off_t subfile_offset, size_t subfile_size, char* extension); + +/* .SPS - Nippon Ichi's RIFF AT3 wrapper [ClaDun (PSP)] */ +VGMSTREAM * init_vgmstream_sps_n1(STREAMFILE *streamFile) { + VGMSTREAM *vgmstream = NULL; + STREAMFILE *temp_streamFile = NULL; + int type, sample_rate; + off_t subfile_offset; + size_t subfile_size; + + /* check extensions */ + if ( !check_extensions(streamFile,"sps")) + goto fail; + + /* mini header */ + type = read_32bitLE(0x00,streamFile); //todo channels? all known VAG are mono and AT3 stereo + subfile_size = read_32bitLE(0x04,streamFile); + sample_rate = (uint16_t)read_16bitLE(0x08,streamFile); + /* 0x0a/0b: stereo+loop flags? */ + //num_samples = read_32bitLE(0x0c,streamFile); + subfile_offset = 0x10; + + /* init the VGMSTREAM */ + switch(type) { + case 1: /* .vag */ + temp_streamFile = setup_sps_streamfile(streamFile, subfile_offset, subfile_size, "vag"); + if (!temp_streamFile) goto fail; + + vgmstream = init_vgmstream_ps2_vag(temp_streamFile); + if (!vgmstream) goto fail; + break; + + case 2: /* .at3 */ + VGM_LOG("so=%lx, s=%x\n", subfile_offset,subfile_size ); + temp_streamFile = setup_sps_streamfile(streamFile, subfile_offset, subfile_size, "at3"); + if (!temp_streamFile) goto fail; + VGM_LOG("4\n"); + vgmstream = init_vgmstream_riff(temp_streamFile); + if (!vgmstream) goto fail; + break; + default: + goto fail; + } + + //VGM_LOG(vgmstream->num_samples != num_samples, + // "SPS: sps num_samples and subfile num_samples don't match\n"); + //vgmstream->num_samples = num_samples; //todo adjusted for MAIATRAC3 + vgmstream->sample_rate = sample_rate; /* .vag header doesn't match */ + + close_streamfile(temp_streamFile); + return vgmstream; + +fail: + close_streamfile(temp_streamFile); + close_vgmstream(vgmstream); + return NULL; +} + +static STREAMFILE* setup_sps_streamfile(STREAMFILE *streamFile, off_t subfile_offset, size_t subfile_size, char* extension) { + STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL; + + /* setup subfile */ + new_streamFile = open_wrap_streamfile(streamFile); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + new_streamFile = open_clamp_streamfile(temp_streamFile, subfile_offset,subfile_size); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + new_streamFile = open_fakename_streamfile(temp_streamFile, NULL,extension); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + return temp_streamFile; + +fail: + close_streamfile(temp_streamFile); + return NULL; +} diff --git a/src/vgmstream.c b/src/vgmstream.c index a25a4e67..bc9cf01b 100644 --- a/src/vgmstream.c +++ b/src/vgmstream.c @@ -377,6 +377,7 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_fsb_encrypted, init_vgmstream_xwc, init_vgmstream_atsl3, + init_vgmstream_sps_n1, init_vgmstream_txth, /* should go at the end (lower priority) */ #ifdef VGM_USE_FFMPEG From 8b5d26bcd8ca5282ce0943ec189cff7d27324448 Mon Sep 17 00:00:00 2001 From: bnnm Date: Sat, 27 Jan 2018 12:24:35 +0100 Subject: [PATCH 09/21] Remove log --- src/meta/sps_n1.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/meta/sps_n1.c b/src/meta/sps_n1.c index eb1a2e69..5ac38d25 100644 --- a/src/meta/sps_n1.c +++ b/src/meta/sps_n1.c @@ -34,10 +34,9 @@ VGMSTREAM * init_vgmstream_sps_n1(STREAMFILE *streamFile) { break; case 2: /* .at3 */ - VGM_LOG("so=%lx, s=%x\n", subfile_offset,subfile_size ); temp_streamFile = setup_sps_streamfile(streamFile, subfile_offset, subfile_size, "at3"); if (!temp_streamFile) goto fail; - VGM_LOG("4\n"); + vgmstream = init_vgmstream_riff(temp_streamFile); if (!vgmstream) goto fail; break; From 72d0151530d52122aff6fa87166f11166e386b15 Mon Sep 17 00:00:00 2001 From: bnnm Date: Sat, 27 Jan 2018 12:50:25 +0100 Subject: [PATCH 10/21] Setup BGW decryption directly in meta using custom streamfiles --- src/meta/bgw.c | 103 +++++++++++++++++++++++++++++++++++++------------ 1 file changed, 79 insertions(+), 24 deletions(-) diff --git a/src/meta/bgw.c b/src/meta/bgw.c index f95ac994..0be6ebab 100644 --- a/src/meta/bgw.c +++ b/src/meta/bgw.c @@ -1,9 +1,14 @@ #include "meta.h" #include "../coding/coding.h" + +static STREAMFILE* setup_bgw_atrac3_streamfile(STREAMFILE *streamFile, off_t subfile_offset, size_t subfile_size, size_t frame_size, int channels); + + /* BGW - from Final Fantasy XI (PC) music files */ VGMSTREAM * init_vgmstream_bgw(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; + STREAMFILE *temp_streamFile = NULL; uint32_t codec, file_size, block_size, sample_rate, block_align; int32_t loop_start; off_t start_offset; @@ -32,18 +37,15 @@ VGMSTREAM * init_vgmstream_bgw(STREAMFILE *streamFile) { channel_count = read_8bit(0x2e,streamFile); block_align = read_8bit(0x2f,streamFile); - - /* check file size with header value */ if (file_size != get_streamfile_size(streamFile)) goto fail; loop_flag = (loop_start > 0); - /* build the VGMSTREAM */ + /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; - /* fill in the vital statistics */ vgmstream->meta_type = meta_FFXI_BGW; vgmstream->sample_rate = sample_rate; @@ -65,7 +67,7 @@ VGMSTREAM * init_vgmstream_bgw(STREAMFILE *streamFile) { case 3: { /* ATRAC3 (encrypted) */ uint8_t buf[0x100]; int bytes, joint_stereo, skip_samples; - ffmpeg_custom_config cfg; + size_t data_size = file_size - start_offset; vgmstream->num_samples = block_size; /* atrac3_bytes_to_samples gives the same value */ if (loop_flag) { @@ -77,18 +79,18 @@ VGMSTREAM * init_vgmstream_bgw(STREAMFILE *streamFile) { joint_stereo = 0; skip_samples = 0; - bytes = ffmpeg_make_riff_atrac3(buf, 0x100, vgmstream->num_samples, file_size - start_offset, vgmstream->channels, vgmstream->sample_rate, block_align, joint_stereo, skip_samples); + bytes = ffmpeg_make_riff_atrac3(buf, 0x100, vgmstream->num_samples, data_size, vgmstream->channels, vgmstream->sample_rate, block_align, joint_stereo, skip_samples); if (bytes <= 0) goto fail; - memset(&cfg, 0, sizeof(ffmpeg_custom_config)); - cfg.type = FFMPEG_BGW_ATRAC3; - cfg.channels = vgmstream->channels; + temp_streamFile = setup_bgw_atrac3_streamfile(streamFile, start_offset,data_size, 0xC0,channel_count); + if (!temp_streamFile) goto fail; - vgmstream->codec_data = init_ffmpeg_config(streamFile, buf,bytes, start_offset,file_size - start_offset, &cfg); + vgmstream->codec_data = init_ffmpeg_header_offset(temp_streamFile, buf,bytes, 0,data_size); if (!vgmstream->codec_data) goto fail; - vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; + + close_streamfile(temp_streamFile); break; } #endif @@ -98,17 +100,17 @@ VGMSTREAM * init_vgmstream_bgw(STREAMFILE *streamFile) { } - /* open the file for reading */ if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) ) goto fail; - return vgmstream; fail: + close_streamfile(temp_streamFile); close_vgmstream(vgmstream); return NULL; } + /* SPW (SEWave) - from PlayOnline viewer for Final Fantasy XI (PC) */ VGMSTREAM * init_vgmstream_spw(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; @@ -118,8 +120,8 @@ VGMSTREAM * init_vgmstream_spw(STREAMFILE *streamFile) { int channel_count, loop_flag = 0; - /* check extensions */ - if ( !check_extensions(streamFile, "spw") ) + /* check extensions */ + if ( !check_extensions(streamFile, "spw") ) goto fail; /* check header */ @@ -127,10 +129,6 @@ VGMSTREAM * init_vgmstream_spw(STREAMFILE *streamFile) { read_32bitBE(4,streamFile) != 0x76650000) /* "ve\0\0" */ goto fail; - /* check file size with header value */ - if (read_32bitLE(0x8,streamFile) != get_streamfile_size(streamFile)) - goto fail; - file_size = read_32bitLE(0x08,streamFile); codec = read_32bitLE(0x0c,streamFile); /*file_id = read_32bitLE(0x10,streamFile);*/ @@ -144,17 +142,15 @@ VGMSTREAM * init_vgmstream_spw(STREAMFILE *streamFile) { block_align = read_8bit(0x2b,streamFile); /*0x2c: unk (0x01 when PCM, 0x10 when VAG?) */ - /* check file size with header value */ if (file_size != get_streamfile_size(streamFile)) goto fail; loop_flag = (loop_start > 0); - /* build the VGMSTREAM */ + /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; - /* fill in the vital statistics */ vgmstream->meta_type = meta_FFXI_SPW; vgmstream->sample_rate = sample_rate; @@ -171,7 +167,7 @@ VGMSTREAM * init_vgmstream_spw(STREAMFILE *streamFile) { } break; - + case 1: /* PCM */ vgmstream->coding_type = coding_PCM16LE; vgmstream->layout_type = layout_interleave; @@ -182,8 +178,9 @@ VGMSTREAM * init_vgmstream_spw(STREAMFILE *streamFile) { vgmstream->loop_start_sample = (loop_start-1); vgmstream->loop_end_sample = vgmstream->num_samples; } - + break; + default: goto fail; } @@ -199,3 +196,61 @@ fail: close_vgmstream(vgmstream); return NULL; } + + +#define BGW_KEY_MAX (0xC0*2) + +typedef struct { + uint8_t key[BGW_KEY_MAX]; + size_t key_size; +} bgw_decryption_data; + +/* Encrypted ATRAC3 info from Moogle Toolbox (https://sourceforge.net/projects/mogbox/) */ +static size_t bgw_decryption_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length, bgw_decryption_data* data) { + size_t bytes_read; + int i; + + bytes_read = streamfile->read(streamfile, dest, offset, length); + + /* decrypt data (xor) */ + for (i = 0; i < bytes_read; i++) { + dest[i] ^= data->key[(offset + i) % data->key_size]; + } + + return bytes_read; +} + +static STREAMFILE* setup_bgw_atrac3_streamfile(STREAMFILE *streamFile, off_t subfile_offset, size_t subfile_size, size_t frame_size, int channels) { + STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL; + bgw_decryption_data io_data = {0}; + size_t io_data_size = sizeof(bgw_decryption_data); + int ch; + + /* setup decryption with key (first frame + modified channel header) */ + if (frame_size*channels == 0 || frame_size*channels > BGW_KEY_MAX) goto fail; + + io_data.key_size = read_streamfile(io_data.key, subfile_offset, frame_size*channels, streamFile); + for (ch = 0; ch < channels; ch++) { + uint32_t xor = get_32bitBE(io_data.key + frame_size*ch); + put_32bitBE(io_data.key + frame_size*ch, xor ^ 0xA0024E9F); + } + + /* setup subfile */ + new_streamFile = open_wrap_streamfile(streamFile); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + new_streamFile = open_clamp_streamfile(temp_streamFile, subfile_offset,subfile_size); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + new_streamFile = open_io_streamfile(temp_streamFile, &io_data,io_data_size, bgw_decryption_read); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + return temp_streamFile; + +fail: + close_streamfile(temp_streamFile); + return NULL; +} From 448d52fa4eec8d603cd9a73d5dcc20b7515308a3 Mon Sep 17 00:00:00 2001 From: bnnm Date: Sat, 27 Jan 2018 13:01:30 +0100 Subject: [PATCH 11/21] Remove BGW ATRAC3 mode in FFmpeg in favor of custom streamfiles --- src/coding/ffmpeg_decoder.c | 6 -- src/coding/ffmpeg_decoder_utils.h | 4 -- src/coding/ffmpeg_decoder_utils_bgw_atrac3.c | 58 -------------------- src/libvgmstream.vcproj | 4 -- src/libvgmstream.vcxproj | 1 - src/libvgmstream.vcxproj.filters | 3 - src/vgmstream.h | 2 - 7 files changed, 78 deletions(-) delete mode 100644 src/coding/ffmpeg_decoder_utils_bgw_atrac3.c diff --git a/src/coding/ffmpeg_decoder.c b/src/coding/ffmpeg_decoder.c index ed3c3d7d..0e365262 100644 --- a/src/coding/ffmpeg_decoder.c +++ b/src/coding/ffmpeg_decoder.c @@ -222,7 +222,6 @@ static int ffmpeg_read(void *opaque, uint8_t *buf, int buf_size) { switch(data->config.type) { case FFMPEG_EA_XMA: ret = ffmpeg_custom_read_eaxma(data, buf, buf_size); break; case FFMPEG_SWITCH_OPUS: ret = ffmpeg_custom_read_switch_opus(data, buf, buf_size); break; - case FFMPEG_BGW_ATRAC3: ret = ffmpeg_custom_read_bgw_atrac3(data, buf, buf_size); break; //case FFMPEG_EA_SCHL: ret = ffmpeg_custom_read_ea_schl(data, buf, buf_size); break; //case FFMPEG_SFH: ret = ffmpeg_custom_read_sfh(data, buf, buf_size); break; default: ret = ffmpeg_custom_read_standard(data, buf, buf_size); break; @@ -291,7 +290,6 @@ static int64_t ffmpeg_seek(void *opaque, int64_t offset, int whence) { switch(data->config.type) { case FFMPEG_EA_XMA: offset = ffmpeg_custom_seek_eaxma(data, offset); break; case FFMPEG_SWITCH_OPUS: offset = ffmpeg_custom_seek_switch_opus(data, offset); break; - case FFMPEG_BGW_ATRAC3: offset = ffmpeg_custom_seek_bgw_atrac3(data, offset); break; //case FFMPEG_EA_SCHL: offset = ffmpeg_custom_seek_ea_schl(data, offset); break; //case FFMPEG_SFH: offset = ffmpeg_custom_seek_sfh(data, offset); break; default: offset = ffmpeg_custom_seek_standard(data, offset); break; @@ -309,7 +307,6 @@ static int64_t ffmpeg_size(ffmpeg_codec_data * data) { switch(data->config.type) { case FFMPEG_EA_XMA: bytes = ffmpeg_custom_size_eaxma(data); break; case FFMPEG_SWITCH_OPUS: bytes = ffmpeg_custom_size_switch_opus(data); break; - case FFMPEG_BGW_ATRAC3: bytes = ffmpeg_custom_size_bgw_atrac3(data); break; //case FFMPEG_EA_SCHL: bytes = ffmpeg_custom_size_ea_schl(data); break; //case FFMPEG_SFH: bytes = ffmpeg_custom_size_sfh(data); break; default: bytes = ffmpeg_custom_size_standard(data); break; @@ -806,9 +803,6 @@ void free_ffmpeg(ffmpeg_codec_data *data) { close_streamfile(data->streamfile); data->streamfile = NULL; } - if (data->config.key) { - free(data->config.key); - } free(data); } diff --git a/src/coding/ffmpeg_decoder_utils.h b/src/coding/ffmpeg_decoder_utils.h index a7e12912..64461fe9 100644 --- a/src/coding/ffmpeg_decoder_utils.h +++ b/src/coding/ffmpeg_decoder_utils.h @@ -31,10 +31,6 @@ int ffmpeg_custom_read_switch_opus(ffmpeg_codec_data *data, uint8_t *buf, int bu int64_t ffmpeg_custom_seek_switch_opus(ffmpeg_codec_data *data, int64_t virtual_offset); int64_t ffmpeg_custom_size_switch_opus(ffmpeg_codec_data *data); -int ffmpeg_custom_read_bgw_atrac3(ffmpeg_codec_data *data, uint8_t *buf, int buf_size); -int64_t ffmpeg_custom_seek_bgw_atrac3(ffmpeg_codec_data *data, int64_t virtual_offset); -int64_t ffmpeg_custom_size_bgw_atrac3(ffmpeg_codec_data *data); - //int ffmpeg_custom_read_ea_schl(ffmpeg_codec_data *data, uint8_t *buf, int buf_size); //int64_t ffmpeg_custom_seek_ea_schl(ffmpeg_codec_data *data, int64_t virtual_offset); //int64_t ffmpeg_custom_size_ea_schl(ffmpeg_codec_data *data); diff --git a/src/coding/ffmpeg_decoder_utils_bgw_atrac3.c b/src/coding/ffmpeg_decoder_utils_bgw_atrac3.c deleted file mode 100644 index 5b6bd441..00000000 --- a/src/coding/ffmpeg_decoder_utils_bgw_atrac3.c +++ /dev/null @@ -1,58 +0,0 @@ -#if 1 -#include "coding.h" -#include "ffmpeg_decoder_utils.h" - -#ifdef VGM_USE_FFMPEG - -#define BGM_ATRAC3_FRAME_SIZE 0xC0 - -/** - * Encrypted ATRAC3 used in BGW (Final Fantasy XI PC). - * Info from Moogle Toolbox: https://sourceforge.net/projects/mogbox/ - */ - -int ffmpeg_custom_read_bgw_atrac3(ffmpeg_codec_data *data, uint8_t *buf, int buf_size) { - int i, ch; - size_t bytes; - size_t block_align = BGM_ATRAC3_FRAME_SIZE * data->config.channels; - - - /* init key: first frame + modified channel header */ - if (data->config.key == NULL) { - data->config.key = malloc(block_align); - if (!data->config.key) return 0; - - read_streamfile(data->config.key, data->real_start, block_align, data->streamfile); - for (ch = 0; ch < data->config.channels; ch++) { - uint32_t xor = get_32bitBE(data->config.key + ch*BGM_ATRAC3_FRAME_SIZE); - put_32bitBE(data->config.key + ch*BGM_ATRAC3_FRAME_SIZE, xor ^ 0xA0024E9F); - } - } - - - /* read normally and unXOR the data */ - bytes = read_streamfile(buf, data->real_offset, buf_size, data->streamfile); - for (i = 0; i < bytes; i++) { - int key_pos = (data->real_offset - data->real_start + i) % block_align; - buf[i] = buf[i] ^ data->config.key[key_pos]; - } - - - data->real_offset += bytes; - return bytes; -} - -int64_t ffmpeg_custom_seek_bgw_atrac3(ffmpeg_codec_data *data, int64_t virtual_offset) { - int64_t seek_virtual_offset = virtual_offset - data->header_size; - - data->real_offset = data->real_start + seek_virtual_offset; - return virtual_offset; -} - -int64_t ffmpeg_custom_size_bgw_atrac3(ffmpeg_codec_data *data) { - return data->real_size + data->header_size; -} - - -#endif -#endif diff --git a/src/libvgmstream.vcproj b/src/libvgmstream.vcproj index fb99c94c..53468d58 100644 --- a/src/libvgmstream.vcproj +++ b/src/libvgmstream.vcproj @@ -1482,10 +1482,6 @@ RelativePath=".\coding\ffmpeg_decoder_utils_ea_schl.c" > - - diff --git a/src/libvgmstream.vcxproj b/src/libvgmstream.vcxproj index 6340a83f..a1539618 100644 --- a/src/libvgmstream.vcxproj +++ b/src/libvgmstream.vcxproj @@ -112,7 +112,6 @@ - diff --git a/src/libvgmstream.vcxproj.filters b/src/libvgmstream.vcxproj.filters index 1e5f45b7..e0b589c2 100644 --- a/src/libvgmstream.vcxproj.filters +++ b/src/libvgmstream.vcxproj.filters @@ -1222,9 +1222,6 @@ coding\Source Files - - coding\Source Files - coding\Source Files diff --git a/src/vgmstream.h b/src/vgmstream.h index e17dfb10..9b611f88 100644 --- a/src/vgmstream.h +++ b/src/vgmstream.h @@ -1098,7 +1098,6 @@ typedef enum { FFMPEG_STANDARD, /* default FFmpeg */ FFMPEG_SWITCH_OPUS, /* Opus without Ogg layer */ FFMPEG_EA_XMA, /* XMA with padding removed and custom streams in SNS blocks */ - FFMPEG_BGW_ATRAC3, /* Encrypted raw ATRAC3 */ //FFMPEG_EA_SCHL, /* Normal header+data (ex. ATRAC3) in SCxx blocks */ //FFMPEG_SFH, /* ATRAC3plus header+data in SFH blocks */ //FFMPEG_AWC_XMA, /* XMA data in AWC blocks, 1 streams per channel */ @@ -1116,7 +1115,6 @@ typedef struct { /* internal sequences, when needed */ int sequence; int samples_done; - uint8_t * key; } ffmpeg_custom_config; typedef struct { From 8c7eba151a68cdc6a99eb6c1b596a9e208415eec Mon Sep 17 00:00:00 2001 From: bnnm Date: Sat, 27 Jan 2018 13:58:46 +0100 Subject: [PATCH 12/21] Add STREAMFILE for segmented files --- src/streamfile.c | 170 ++++++++++++++++++++++++++++++++++++++++++++--- src/streamfile.h | 5 ++ 2 files changed, 167 insertions(+), 8 deletions(-) diff --git a/src/streamfile.c b/src/streamfile.c index 5ac252c2..f62b7842 100644 --- a/src/streamfile.c +++ b/src/streamfile.c @@ -250,6 +250,8 @@ STREAMFILE * open_stdio_streamfile_by_file(FILE * file, const char * filename) { /* **************************************************** */ //todo stream_index: copy? pass? funtion? external? +//todo use realnames on reopen? simplify? +//todo use safe string ops, this ain't easy typedef struct { STREAMFILE sf; @@ -335,13 +337,13 @@ static STREAMFILE *clamp_open(CLAMP_STREAMFILE *streamfile, const char * const f STREAMFILE *new_inner_sf; new_inner_sf = streamfile->inner_sf->open(streamfile->inner_sf,filename,buffersize); - streamfile->inner_sf->get_name(streamfile->inner_sf, original_filename, PATH_LIMIT);//todo realname? + streamfile->inner_sf->get_name(streamfile->inner_sf, original_filename, PATH_LIMIT); - /* detect re-opening the file and don't clamp */ + /* detect re-opening the file */ if (strcmp(filename, original_filename) == 0) { - return open_clamp_streamfile(new_inner_sf, streamfile->start, streamfile->size); + return open_clamp_streamfile(new_inner_sf, streamfile->start, streamfile->size); /* clamp again */ } else { - return new_inner_sf; + return new_inner_sf; /**/ } } static void clamp_close(CLAMP_STREAMFILE *streamfile) { @@ -465,7 +467,7 @@ static size_t fakename_get_offset(FAKENAME_STREAMFILE * streamfile) { return streamfile->inner_sf->get_offset(streamfile->inner_sf); /* default */ } static void fakename_get_name(FAKENAME_STREAMFILE *streamfile, char *buffer, size_t length) { - strncpy(buffer,streamfile->fakename,length);//todo safe ops + strncpy(buffer,streamfile->fakename,length); buffer[length-1]='\0'; } static void fakename_get_realname(FAKENAME_STREAMFILE *streamfile, char *buffer, size_t length) { @@ -477,7 +479,7 @@ static STREAMFILE *fakename_open(FAKENAME_STREAMFILE *streamfile, const char * c STREAMFILE *new_inner_sf; char original_filename[PATH_LIMIT]; - streamfile->inner_sf->get_name(streamfile->inner_sf, original_filename, PATH_LIMIT);//todo realname? + streamfile->inner_sf->get_name(streamfile->inner_sf, original_filename, PATH_LIMIT); new_inner_sf = streamfile->inner_sf->open(streamfile->inner_sf, original_filename, buffersize); return open_fakename_streamfile(new_inner_sf, streamfile->fakename, NULL); } @@ -510,12 +512,11 @@ STREAMFILE *open_fakename_streamfile(STREAMFILE *streamfile, char * fakename, ch this_sf->inner_sf = streamfile; - //todo use safe ops, this ain't easy /* copy passed name or retain current, and swap extension if expected */ if (fakename) { strcpy(this_sf->fakename,fakename); } else { - streamfile->get_name(streamfile, this_sf->fakename, PATH_LIMIT);//todo realname? + streamfile->get_name(streamfile, this_sf->fakename, PATH_LIMIT); } if (fakeext) { char * ext = strrchr(this_sf->fakename,'.'); @@ -529,6 +530,159 @@ STREAMFILE *open_fakename_streamfile(STREAMFILE *streamfile, char * fakename, ch /* **************************************************** */ + +typedef struct { + STREAMFILE sf; + + STREAMFILE **inner_sfs; + size_t inner_sfs_size; + size_t *sizes; + off_t size; + off_t offset; +} MULTIFILE_STREAMFILE; + +static size_t multifile_read(MULTIFILE_STREAMFILE *streamfile, uint8_t * dest, off_t offset, size_t length) { + int i, segment = 0; + off_t segment_offset = 0; + size_t done = 0; + + if (offset > streamfile->size) { + streamfile->offset = streamfile->size; + return 0; + } + + /* map external offset to multifile offset */ + for (i = 0; i < streamfile->inner_sfs_size; i++) { + size_t segment_size = streamfile->sizes[i]; + /* check if offset falls in this segment */ + if (offset >= segment_offset && offset < segment_offset + segment_size) { + segment = i; + segment_offset = offset - segment_offset; + break; + } + + segment_offset += segment_size; + } + + /* reads can span multiple segments */ + while(done < length) { + if (segment >= streamfile->inner_sfs_size) /* over last segment, not fully done */ + break; + /* reads over segment size are ok, will return smaller value and continue next segment */ + done += streamfile->inner_sfs[segment]->read(streamfile->inner_sfs[segment], dest+done, segment_offset, length - done); + segment++; + segment_offset = 0; + } + + streamfile->offset = offset + done; + return done; +} +static size_t multifile_get_size(MULTIFILE_STREAMFILE *streamfile) { + return streamfile->size; +} +static size_t multifile_get_offset(MULTIFILE_STREAMFILE * streamfile) { + return streamfile->offset; +} +static void multifile_get_name(MULTIFILE_STREAMFILE *streamfile, char *buffer, size_t length) { + streamfile->inner_sfs[0]->get_name(streamfile->inner_sfs[0], buffer, length); +} +static void multifile_get_realname(MULTIFILE_STREAMFILE *streamfile, char *buffer, size_t length) { + multifile_get_name(streamfile, buffer, length); +} +static STREAMFILE *multifile_open(MULTIFILE_STREAMFILE *streamfile, const char * const filename, size_t buffersize) { + char original_filename[PATH_LIMIT]; + STREAMFILE *new_sf = NULL; + STREAMFILE **new_inner_sfs = NULL; + int i; + + streamfile->inner_sfs[0]->get_name(streamfile->inner_sfs[0], original_filename, PATH_LIMIT); + + /* detect re-opening the file */ + if (strcmp(filename, original_filename) == 0) { /* same multifile */ + new_inner_sfs = calloc(streamfile->inner_sfs_size, sizeof(STREAMFILE*)); + if (!new_inner_sfs) goto fail; + + for (i = 0; i < streamfile->inner_sfs_size; i++) { + streamfile->inner_sfs[i]->get_name(streamfile->inner_sfs[i], original_filename, PATH_LIMIT); + new_inner_sfs[i] = streamfile->inner_sfs[i]->open(streamfile->inner_sfs[i], original_filename, buffersize); + if (!new_inner_sfs[i]) goto fail; + } + + new_sf = open_multifile_streamfile(new_inner_sfs, streamfile->inner_sfs_size); + if (!new_sf) goto fail; + + return new_sf; + } + else { + return streamfile->inner_sfs[0]->open(streamfile->inner_sfs[0], filename, buffersize); /* regular file */ + } + +fail: + if (new_inner_sfs) { + for (i = 0; i < streamfile->inner_sfs_size; i++) + close_streamfile(new_inner_sfs[i]); + } + free(new_inner_sfs); + return NULL; +} +static void multifile_close(MULTIFILE_STREAMFILE *streamfile) { + int i; + for (i = 0; i < streamfile->inner_sfs_size; i++) { + for (i = 0; i < streamfile->inner_sfs_size; i++) + close_streamfile(streamfile->inner_sfs[i]); + } + free(streamfile->inner_sfs); + free(streamfile->sizes); + free(streamfile); +} + +STREAMFILE *open_multifile_streamfile(STREAMFILE **streamfiles, size_t streamfiles_size) { + MULTIFILE_STREAMFILE *this_sf; + int i; + + if (!streamfiles || !streamfiles_size) return NULL; + for (i = 0; i < streamfiles_size; i++) { + if (!streamfiles[i]) return NULL; + } + + this_sf = calloc(1,sizeof(MULTIFILE_STREAMFILE)); + if (!this_sf) goto fail; + + /* set callbacks and internals */ + this_sf->sf.read = (void*)multifile_read; + this_sf->sf.get_size = (void*)multifile_get_size; + this_sf->sf.get_offset = (void*)multifile_get_offset; + this_sf->sf.get_name = (void*)multifile_get_name; + this_sf->sf.get_realname = (void*)multifile_get_realname; + this_sf->sf.open = (void*)multifile_open; + this_sf->sf.close = (void*)multifile_close; + this_sf->sf.stream_index = streamfiles[0]->stream_index; + + this_sf->inner_sfs_size = streamfiles_size; + this_sf->inner_sfs = calloc(streamfiles_size, sizeof(STREAMFILE*)); + if (!this_sf->inner_sfs) goto fail; + this_sf->sizes = calloc(streamfiles_size, sizeof(size_t)); + if (!this_sf->sizes) goto fail; + + for (i = 0; i < this_sf->inner_sfs_size; i++) { + this_sf->inner_sfs[i] = streamfiles[i]; + this_sf->sizes[i] = streamfiles[i]->get_size(streamfiles[i]); + this_sf->size += this_sf->sizes[i]; + } + + return &this_sf->sf; + +fail: + if (this_sf) { + free(this_sf->inner_sfs); + free(this_sf->sizes); + } + free(this_sf); + return NULL; +} + +/* **************************************************** */ + /* Read a line into dst. The source files are lines separated by CRLF (Windows) / LF (Unux) / CR (Mac). * The line will be null-terminated and CR/LF removed if found. * diff --git a/src/streamfile.h b/src/streamfile.h index 90aeefe7..4d0aba65 100644 --- a/src/streamfile.h +++ b/src/streamfile.h @@ -87,6 +87,11 @@ STREAMFILE *open_io_streamfile(STREAMFILE *streamfile, void* data, size_t data_s * If the fakename is an existing file, open won't work on it as it'll reopen the fake-named streamfile. */ STREAMFILE *open_fakename_streamfile(STREAMFILE *streamfile, char * fakename, char * fakeext); +/* A streamfile formed from multiple streamfiles, their data joined during reads. + * Can be used when data is segmented in multiple separate files. + * The first streamfile is used to get names, stream index and so on. */ +STREAMFILE *open_multifile_streamfile(STREAMFILE **streamfiles, size_t streamfiles_size); + /* close a file, destroy the STREAMFILE object */ static inline void close_streamfile(STREAMFILE * streamfile) { From d8c81bc14a7f854effeecb1204ba54a5ffaa3f05 Mon Sep 17 00:00:00 2001 From: bnnm Date: Sat, 27 Jan 2018 14:00:31 +0100 Subject: [PATCH 13/21] Add Media.Vision ATX subfiles (decrypted) [Shining Blade (PSP)] --- src/formats.c | 1 + src/libvgmstream.vcproj | 4 ++ src/libvgmstream.vcxproj | 1 + src/libvgmstream.vcxproj.filters | 3 + src/meta/atx.c | 109 +++++++++++++++++++++++++++++++ src/meta/meta.h | 2 + src/vgmstream.c | 1 + 7 files changed, 121 insertions(+) create mode 100644 src/meta/atx.c diff --git a/src/formats.c b/src/formats.c index 07079ef3..1fd59b6a 100644 --- a/src/formats.c +++ b/src/formats.c @@ -44,6 +44,7 @@ static const char* extension_list[] = { "at3", "at9", "atsl3", + "atx", "aud", "aus", "awc", diff --git a/src/libvgmstream.vcproj b/src/libvgmstream.vcproj index 53468d58..ffd8300f 100644 --- a/src/libvgmstream.vcproj +++ b/src/libvgmstream.vcproj @@ -279,6 +279,10 @@ + + + diff --git a/src/libvgmstream.vcxproj.filters b/src/libvgmstream.vcxproj.filters index e0b589c2..8c3c5dc1 100644 --- a/src/libvgmstream.vcxproj.filters +++ b/src/libvgmstream.vcxproj.filters @@ -172,6 +172,9 @@ meta\Source Files + + meta\Source Files + meta\Source Files diff --git a/src/meta/atx.c b/src/meta/atx.c new file mode 100644 index 00000000..a58a8833 --- /dev/null +++ b/src/meta/atx.c @@ -0,0 +1,109 @@ +#include "meta.h" +#include "../coding/coding.h" + +#define ATX_MAX_SEGMENTS 2 + +static STREAMFILE* setup_atx_streamfile(STREAMFILE *streamFile); + +/* .ATX - Media.Vision's segmented RIFF AT3 wrapper [Senjo no Valkyria 3 (PSP), Shining Blade (PSP)] */ +VGMSTREAM * init_vgmstream_atx(STREAMFILE *streamFile) { + VGMSTREAM *vgmstream = NULL; + STREAMFILE *temp_streamFile = NULL; + + + /* check extensions */ + if ( !check_extensions(streamFile,"atx")) + goto fail; + if (read_32bitBE(0x00,streamFile) != 0x41504133) /* "APA3" */ + goto fail; + + /* .ATX is made of subfile segments, handled by the streamFile. + * Each segment has a header/footer, and part of the whole data + * (i.e. ATRAC3 data ends in a subfile and continues in the next) */ + temp_streamFile = setup_atx_streamfile(streamFile); + if (!temp_streamFile) goto fail; + + vgmstream = init_vgmstream_riff(temp_streamFile); + if (!vgmstream) goto fail; + + close_streamfile(temp_streamFile); + return vgmstream; + +fail: + close_streamfile(temp_streamFile); + close_vgmstream(vgmstream); + return NULL; +} + +static STREAMFILE* setup_atx_streamfile(STREAMFILE *streamFile) { + STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL; + STREAMFILE *segment_streamFiles[ATX_MAX_SEGMENTS] = {0}; + char filename[PATH_LIMIT]; + size_t filename_len; + int i, num_segments = 0; + size_t riff_size; +VGM_LOG("1\n"); + + if (read_16bitLE(0x1c,streamFile) != 0) goto fail; /* this must be first segment */ + if (read_16bitLE(0x1e,streamFile) < 1 || read_16bitLE(0x1e,streamFile) > ATX_MAX_SEGMENTS) goto fail; + num_segments = read_16bitLE(0x1e,streamFile); + + /* expected segment name: X_XXX_XXX_0n.ATX, starting from n=1 */ + get_streamfile_name(streamFile, filename,PATH_LIMIT); + filename_len = strlen(filename); + if (filename_len < 7 || filename[filename_len - 5] != '1') goto fail; + + /* setup segments (could avoid reopening first segment but meh) */ + for (i = 0; i < num_segments; i++) { + off_t subfile_offset; + size_t subfile_size; +VGM_LOG("loop\n"); + filename[filename_len - 5] = ('0'+i+1); /* ghetto digit conversion */ + new_streamFile = open_stream_name(streamFile, filename); + if (!new_streamFile) goto fail; + segment_streamFiles[i] = new_streamFile; + + if (read_32bitBE(0x00,segment_streamFiles[i]) != 0x41504133) /* "APA3" */ + goto fail; + + /* parse block/segment header (other Media.Vision's files use it too) */ + subfile_offset = read_32bitLE(0x08,segment_streamFiles[i]); /* header size */ + subfile_size = read_32bitLE(0x14,segment_streamFiles[i]); /* can be 0 in other containers */ +VGM_LOG("subfile: %lx, %x\n", subfile_offset, subfile_size); + if (read_16bitLE(0x1c,segment_streamFiles[i]) != i) + goto fail; /* segment sequence */ + /* 0x04: block size (should match subfile_size in .ATX) */ + /* 0x0c: flags? also in other files, 0x10/18: null, 0x1e: segments */ + + /* clamp to ignore header/footer during next reads */ + new_streamFile = open_clamp_streamfile(segment_streamFiles[i], subfile_offset,subfile_size); + if (!new_streamFile) goto fail; + segment_streamFiles[i] = new_streamFile; + } + + /* setup with all segments and clamp further using riff_size (last segment has padding) */ + riff_size = read_32bitLE(read_32bitLE(0x08,streamFile) + 0x04,streamFile) + 0x08; + + new_streamFile = open_multifile_streamfile(segment_streamFiles, num_segments); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + new_streamFile = open_clamp_streamfile(temp_streamFile, 0,riff_size); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + new_streamFile = open_fakename_streamfile(temp_streamFile, NULL, "at3"); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + return temp_streamFile; + +fail: + if (!temp_streamFile) { + for (i = 0; i < num_segments; i++) + close_streamfile(segment_streamFiles[i]); + } else { + close_streamfile(temp_streamFile); /* closes all segments */ + } + return NULL; +} diff --git a/src/meta/meta.h b/src/meta/meta.h index 621a16d5..4b70c35f 100644 --- a/src/meta/meta.h +++ b/src/meta/meta.h @@ -702,4 +702,6 @@ VGMSTREAM * init_vgmstream_xwc(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_atsl3(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_sps_n1(STREAMFILE *streamFile); + +VGMSTREAM * init_vgmstream_atx(STREAMFILE *streamFile); #endif /*_META_H*/ diff --git a/src/vgmstream.c b/src/vgmstream.c index bc9cf01b..c5a2bf59 100644 --- a/src/vgmstream.c +++ b/src/vgmstream.c @@ -378,6 +378,7 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_xwc, init_vgmstream_atsl3, init_vgmstream_sps_n1, + init_vgmstream_atx, init_vgmstream_txth, /* should go at the end (lower priority) */ #ifdef VGM_USE_FFMPEG From 2300a1165d6d43dbea1d5fb79ad264aa3bed042d Mon Sep 17 00:00:00 2001 From: bnnm Date: Sat, 27 Jan 2018 17:08:04 +0100 Subject: [PATCH 14/21] Adjust raw XA detection for some DC STR --- src/meta/psx_cdxa.c | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/src/meta/psx_cdxa.c b/src/meta/psx_cdxa.c index 6e612c09..1dd04d61 100644 --- a/src/meta/psx_cdxa.c +++ b/src/meta/psx_cdxa.c @@ -39,20 +39,27 @@ VGMSTREAM * init_vgmstream_cdxa(STREAMFILE *streamFile) { } } - /* test first block (except when RIFF) */ + /* test some blocks (except when RIFF) since other .XA/STR may start blank */ if (start_offset == 0) { - int i, j; + int i, j, block; + off_t test_offset = start_offset; + size_t sector_size = (is_blocked ? 0x900 : 0x800); + size_t block_size = 0x80; - /* 0x80 frames for 1 sector (max ~0x800 for ISO mode) */ - for (i = 0; i < (0x800/0x80); i++) { - off_t test_offset = start_offset + (is_blocked ? 0x18 : 0x00) + 0x80*i; + for (block = 0; block < 3; block++) { + test_offset += (is_blocked ? 0x18 : 0x00); /* header */ - /* ADPCM predictors should be 0..3 index */ - for (j = 0; j < 16; j++) { - uint8_t header = read_8bit(test_offset + i, streamFile); - if (((header >> 4) & 0xF) > 3) - goto fail; + for (i = 0; i < (sector_size/block_size); i++) { + /* first 0x10 ADPCM predictors should be 0..3 index */ + for (j = 0; j < 16; j++) { + uint8_t header = read_8bit(test_offset + i, streamFile); + if (((header >> 4) & 0xF) > 3) + goto fail; + } + test_offset += 0x80; } + + test_offset += (is_blocked ? 0x18 : 0x00); /* footer */ } } From 2ef0663d79769f080e8477a88e9fd5ef5d5966ee Mon Sep 17 00:00:00 2001 From: bnnm Date: Sat, 27 Jan 2018 22:34:48 +0100 Subject: [PATCH 15/21] Add SQEX SAB/MAB (MPEG/ATRAC9/HCA/MSADPCM) [DQ Builders, FF XV (PS4)] --- src/formats.c | 4 + src/libvgmstream.vcproj | 4 + src/libvgmstream.vcxproj | 1 + src/libvgmstream.vcxproj.filters | 3 + src/meta/meta.h | 2 + src/meta/sqex_sead.c | 271 +++++++++++++++++++++++++++++++ src/vgmstream.c | 1 + src/vgmstream.h | 2 + 8 files changed, 288 insertions(+) create mode 100644 src/meta/sqex_sead.c diff --git a/src/formats.c b/src/formats.c index 1fd59b6a..004c724e 100644 --- a/src/formats.c +++ b/src/formats.c @@ -175,6 +175,7 @@ static const char* extension_list[] = { "lstm", //fake extension, for STMs "lwav", //fake extension, for WAVs + "mab", "matx", "mc3", "mca", @@ -265,6 +266,7 @@ static const char* extension_list[] = { "sb5", "sb6", "sb7", + "sbin", "sc", "scd", "sck", @@ -951,6 +953,8 @@ static const meta_info meta_info_list[] = { {meta_OGG_ISD, "Ogg Vorbis (ISD)"}, {meta_KMA9, "Koei Tecmo KMA9 header"}, {meta_XWC, "Starbreeze XWC header"}, + {meta_SQEX_SAB, "Square-Enix SAB header"}, + {meta_SQEX_MAB, "Square-Enix MAB header"}, #ifdef VGM_USE_MP4V2 {meta_MP4, "AAC header"}, diff --git a/src/libvgmstream.vcproj b/src/libvgmstream.vcproj index ffd8300f..6257180d 100644 --- a/src/libvgmstream.vcproj +++ b/src/libvgmstream.vcproj @@ -1198,6 +1198,10 @@ RelativePath=".\meta\sqex_scd.c" > + + diff --git a/src/libvgmstream.vcxproj b/src/libvgmstream.vcxproj index 564e1937..1941b101 100644 --- a/src/libvgmstream.vcxproj +++ b/src/libvgmstream.vcxproj @@ -162,6 +162,7 @@ + diff --git a/src/libvgmstream.vcxproj.filters b/src/libvgmstream.vcxproj.filters index 8c3c5dc1..c344eb98 100644 --- a/src/libvgmstream.vcxproj.filters +++ b/src/libvgmstream.vcxproj.filters @@ -1129,6 +1129,9 @@ meta\Source Files + + meta\Source Files + meta\Source Files diff --git a/src/meta/meta.h b/src/meta/meta.h index 4b70c35f..8088dc7c 100644 --- a/src/meta/meta.h +++ b/src/meta/meta.h @@ -704,4 +704,6 @@ VGMSTREAM * init_vgmstream_atsl3(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_sps_n1(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_atx(STREAMFILE *streamFile); + +VGMSTREAM * init_vgmstream_sqex_sead(STREAMFILE * streamFile); #endif /*_META_H*/ diff --git a/src/meta/sqex_sead.c b/src/meta/sqex_sead.c new file mode 100644 index 00000000..4e324953 --- /dev/null +++ b/src/meta/sqex_sead.c @@ -0,0 +1,271 @@ +#include "meta.h" +#include "../coding/coding.h" + + +static STREAMFILE* setup_sead_hca_streamfile(STREAMFILE *streamFile, off_t subfile_offset, size_t subfile_size); + +/* SABF/MABF - Square Enix's "Sead" audio games [Dragon Quest Builders (PS3), Dissidia Opera Omnia (mobile), FF XV (PS4)] */ +VGMSTREAM * init_vgmstream_sqex_sead(STREAMFILE * streamFile) { + VGMSTREAM * vgmstream = NULL; + off_t start_offset, mtrl_offset, meta_offset, post_meta_offset; //, info_offset, name_offset = 0; + size_t stream_size, subheader_size; //, name_size = 0; + + int loop_flag = 0, channel_count, codec, sample_rate, loop_start, loop_end; + int is_sab = 0, is_mab = 0; + int total_subsongs, target_subsong = streamFile->stream_index; + + int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL; + int16_t (*read_16bit)(off_t,STREAMFILE*) = NULL; + + + /* check extensions (.sab: sound/bgm, .mab: music, .sbin: Dissidia Opera Omnia .sab) */ + if ( !check_extensions(streamFile,"sab,mab,sbin")) + goto fail; + + + /** main header **/ + if (read_32bitBE(0x00,streamFile) == 0x73616266) { /* "sabf" */ + is_sab = 1; + } else if (read_32bitBE(0x00,streamFile) == 0x6D616266) { /* "mabf" */ + is_mab = 1; + } else { + goto fail; + } + + //if (read_8bit(0x04,streamFile) != 0x02) /* version? */ + // goto fail; + /* 0x04(1): version? (usually 0x02, rarely 0x01, ex FF XV title) */ + /* 0x05(1): 0x00/01? */ + /* 0x06(2): chunk size? (usually 0x10, rarely 0x20) */ + if (read_16bitBE(0x06,streamFile) < 0x100) { /* use size as no apparent flag */ + read_32bit = read_32bitBE; + read_16bit = read_16bitBE; + } else { + read_32bit = read_32bitLE; + read_16bit = read_16bitLE; + } + /* 0x08(1): ?, 0x09(1): ?, 0x0a(2): ? */ + if (read_32bit(0x0c,streamFile) != get_streamfile_size(streamFile)) + goto fail; + /* 0x10(10): file descriptor ("BGM", "Music", "SE", etc) */ + + + /** offset tables **/ + if (is_sab) { + if (read_32bitBE(0x20,streamFile) != 0x736E6420) goto fail; /* "snd " (info) */ + if (read_32bitBE(0x30,streamFile) != 0x73657120) goto fail; /* "seq " (unknown) */ + if (read_32bitBE(0x40,streamFile) != 0x74726B20) goto fail; /* "trk " (unknown) */ + if (read_32bitBE(0x50,streamFile) != 0x6D74726C) goto fail; /* "mtrl" (headers/streams) */ + //info_offset = read_32bit(0x28,streamFile); + //seq_offset = read_32bit(0x38,streamFile); + //trk_offset = read_32bit(0x48,streamFile); + mtrl_offset = read_32bit(0x58,streamFile); + } + else if (is_mab) { + if (read_32bitBE(0x20,streamFile) != 0x6D757363) goto fail; /* "musc" (info) */ + if (read_32bitBE(0x30,streamFile) != 0x696E7374) goto fail; /* "inst" (unknown) */ + if (read_32bitBE(0x40,streamFile) != 0x6D74726C) goto fail; /* "mtrl" (headers/streams) */ + //info_offset = read_32bit(0x28,streamFile); + //inst_offset = read_32bit(0x38,streamFile); + mtrl_offset = read_32bit(0x48,streamFile); + } + else { + goto fail; + } + /* each section starts with: + * 0x00(2): 0x00/01?, 0x02: size? (0x10), 0x04(2): entries, 0x06+: padded to 0x10 + * 0x10+0x04*entry: offset from section start, also padded to 0x10 at the end */ + + /* find meta_offset in mtrl and total subsongs */ + { + int i; + int entries = read_16bit(mtrl_offset+0x04,streamFile); + off_t entries_offset = mtrl_offset + 0x10; + + if (target_subsong == 0) target_subsong = 1; + total_subsongs = 0; + meta_offset = 0; + + /* manually find subsongs as entries can be dummy (ex. sfx banks in Dissidia Opera Omnia) */ + for (i = 0; i < entries; i++) { + off_t entry_offset = mtrl_offset + read_32bit(entries_offset + i*0x04,streamFile); + + if (read_8bit(entry_offset+0x05,streamFile) == 0) + continue; /* codec 0 when dummy */ + + total_subsongs++; + if (!meta_offset && total_subsongs == target_subsong) + meta_offset = entry_offset; + } + if (meta_offset == 0) goto fail; + /* SAB can contain 0 entries too */ + } + + + /** stream header **/ + /* 0x00(2): 0x00/01? */ + /* 0x02(2): base entry size? (0x20) */ + channel_count = read_8bit(meta_offset+0x04,streamFile); + codec = read_8bit(meta_offset+0x05,streamFile); + //entry_id = read_16bit(meta_offset+0x06,streamFile); + sample_rate = read_32bit(meta_offset+0x08,streamFile); + loop_start = read_32bit(meta_offset+0x0c,streamFile); /* in samples but usually ignored */ + + loop_end = read_32bit(meta_offset+0x10,streamFile); + subheader_size = read_32bit(meta_offset+0x14,streamFile); /* including subfile header */ + stream_size = read_32bit(meta_offset+0x18,streamFile); /* not including subfile header */ + /* 0x1c: null? */ + + loop_flag = (loop_end > 0); + post_meta_offset = meta_offset + 0x20; + + + /** info section (get stream name) **/ + //if (is_sab) { //todo load name based on entry id + /* "snd ": unknown flags/sizes and name */ + /* 0x08(2): file number within descriptor */ + /* 0x1a(2): base_entry size (-0x10?) */ + //name_size = read_32bit(snd_offset+0x20,streamFile); + //name_offset = snd_offset+0x70; + /* 0x24(4): unique id? (referenced in "seq" section) */ + //} + //else if (is_mab) { + /* "musc": unknown flags sizes and names, another format */ + //} + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count,loop_flag); + if (!vgmstream) goto fail; + + vgmstream->sample_rate = sample_rate; + vgmstream->num_streams = total_subsongs; + vgmstream->meta_type = is_sab ? meta_SQEX_SAB : meta_SQEX_MAB; + + switch(codec) { + + case 0x02: { /* MSADPCM [Dragon Quest Builders (Vita) sfx] */ + start_offset = post_meta_offset + subheader_size; + + /* 0x00 (2): null?, 0x02(2): entry size? */ + vgmstream->coding_type = coding_MSADPCM; + vgmstream->layout_type = layout_none; + vgmstream->interleave_block_size = read_16bit(post_meta_offset+0x04,streamFile); + + /* much like AKBs, there are slightly different loop values here, probably more accurate + * (if no loop, loop_end doubles as num_samples) */ + vgmstream->num_samples = msadpcm_bytes_to_samples(stream_size, vgmstream->interleave_block_size, vgmstream->channels); + vgmstream->loop_start_sample = read_32bit(post_meta_offset+0x08, streamFile); //loop_start + vgmstream->loop_end_sample = read_32bit(post_meta_offset+0x0c, streamFile); //loop_end + break; + } + +#ifdef VGM_USE_ATRAC9 + case 0x04: { /* ATRAC9 [Dragon Quest Builders (Vita), Final Fantaxy XV (PS4)] */ + atrac9_config cfg = {0}; + + start_offset = post_meta_offset + subheader_size; + /* post header has various typical ATRAC9 values */ + cfg.channels = vgmstream->channels; + cfg.config_data = read_32bit(post_meta_offset+0x0c,streamFile); + cfg.encoder_delay = read_32bit(post_meta_offset+0x18,streamFile); +VGM_LOG("1\n"); + vgmstream->codec_data = init_atrac9(&cfg); + if (!vgmstream->codec_data) goto fail; + vgmstream->coding_type = coding_ATRAC9; + vgmstream->layout_type = layout_none; +VGM_LOG("2\n"); + vgmstream->sample_rate = read_32bit(post_meta_offset+0x1c,streamFile); /* SAB's sample rate can be different but it's ignored */ + vgmstream->num_samples = read_32bit(post_meta_offset+0x10,streamFile); /* loop values above are also weird and ignored */ + vgmstream->loop_start_sample = read_32bit(post_meta_offset+0x20, streamFile) - (loop_flag ? cfg.encoder_delay : 0); //loop_start + vgmstream->loop_end_sample = read_32bit(post_meta_offset+0x24, streamFile) - (loop_flag ? cfg.encoder_delay : 0); //loop_end + break; + } +#endif + +#ifdef VGM_USE_MPEG + case 0x06: { /* MSF subfile (MPEG mode) [Dragon Quest Builders (PS3)] */ + mpeg_codec_data *mpeg_data = NULL; + mpeg_custom_config cfg = {0}; + + start_offset = post_meta_offset + subheader_size; + /* post header is a proper MSF, but sample rate/loops are ignored in favor of SAB's */ + + mpeg_data = init_mpeg_custom(streamFile, start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_STANDARD, &cfg); + if (!mpeg_data) goto fail; + vgmstream->codec_data = mpeg_data; + vgmstream->layout_type = layout_none; + + vgmstream->num_samples = mpeg_bytes_to_samples(stream_size, mpeg_data); + vgmstream->loop_start_sample = loop_start; + vgmstream->loop_end_sample = loop_end; + break; + } +#endif + + case 0x07: { /* HCA subfile [Dissidia Opera Omnia (Mobile), Final Fantaxy XV (PS4)] */ + //todo there is no easy way to use the HCA decoder; try subfile hack for now + VGMSTREAM *temp_vgmstream = NULL; + STREAMFILE *temp_streamFile = NULL; + off_t subfile_offset = post_meta_offset + 0x10; + size_t subfile_size = stream_size + subheader_size - 0x10; + /* post header has 0x10 unknown + HCA header */ + + + temp_streamFile = setup_sead_hca_streamfile(streamFile, subfile_offset, subfile_size); + if (!temp_streamFile) goto fail; + + temp_vgmstream = init_vgmstream_hca(temp_streamFile); + if (temp_vgmstream) { + /* loops can be slightly different (~1000 samples) but probably HCA's are more accurate */ + temp_vgmstream->num_streams = vgmstream->num_streams; + temp_vgmstream->meta_type = vgmstream->meta_type; + + close_streamfile(temp_streamFile); + close_vgmstream(vgmstream); + return temp_vgmstream; + } + else { + close_streamfile(temp_streamFile); + goto fail; + } + } + + case 0x00: /* dummy entry */ + default: + VGM_LOG("SQEX SEAD: unknown codec %x\n", codec); + goto fail; + } + + /* open the file for reading */ + if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) ) + goto fail; + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} + +static STREAMFILE* setup_sead_hca_streamfile(STREAMFILE *streamFile, off_t subfile_offset, size_t subfile_size) { + STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL; + + /* setup subfile */ + new_streamFile = open_wrap_streamfile(streamFile); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + new_streamFile = open_clamp_streamfile(temp_streamFile, subfile_offset,subfile_size); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + new_streamFile = open_fakename_streamfile(temp_streamFile, NULL,"hca"); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + return temp_streamFile; + +fail: + close_streamfile(temp_streamFile); + return NULL; +} diff --git a/src/vgmstream.c b/src/vgmstream.c index c5a2bf59..f7969294 100644 --- a/src/vgmstream.c +++ b/src/vgmstream.c @@ -379,6 +379,7 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_atsl3, init_vgmstream_sps_n1, init_vgmstream_atx, + init_vgmstream_sqex_sead, init_vgmstream_txth, /* should go at the end (lower priority) */ #ifdef VGM_USE_FFMPEG diff --git a/src/vgmstream.h b/src/vgmstream.h index 9b611f88..5a441927 100644 --- a/src/vgmstream.h +++ b/src/vgmstream.h @@ -657,6 +657,8 @@ typedef enum { meta_OGG_ISD, /* Ogg Vorbis with encryption (Azure Striker Gunvolt PC) */ meta_KMA9, /* Koei Tecmo [Nobunaga no Yabou - Souzou (Vita)] */ meta_XWC, /* Starbreeze games */ + meta_SQEX_SAB, /* Square-Enix newest middleware (sound) */ + meta_SQEX_MAB, /* Square-Enix newest middleware (music) */ #ifdef VGM_USE_MP4V2 meta_MP4, /* AAC (iOS) */ From a0a5143daaf40844382653733bb7563263ee65ed Mon Sep 17 00:00:00 2001 From: bnnm Date: Sat, 27 Jan 2018 23:00:27 +0100 Subject: [PATCH 16/21] Fix some FSB IMA [Dead to Rights 2 (Xbox)] --- src/formats.c | 2 +- src/meta/fsb.c | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/formats.c b/src/formats.c index 004c724e..08b683cf 100644 --- a/src/formats.c +++ b/src/formats.c @@ -479,7 +479,7 @@ static const coding_info coding_info_list[] = { {coding_APPLE_IMA4, "Apple Quicktime 4-bit IMA ADPCM"}, {coding_SNDS_IMA, "Heavy Iron .snds 4-bit IMA ADPCM"}, {coding_OTNS_IMA, "Omikron: The Nomad Soul 4-bit IMA ADPCM"}, - {coding_FSB_IMA, "FSB multichannel 4-bit IMA ADPCM"}, + {coding_FSB_IMA, "FSB 4-bit IMA ADPCM"}, {coding_WWISE_IMA, "Audiokinetic Wwise 4-bit IMA ADPCM"}, {coding_REF_IMA, "Reflections 4-bit IMA ADPCM"}, {coding_AWC_IMA, "Rockstar AWC 4-bit IMA ADPCM"}, diff --git a/src/meta/fsb.c b/src/meta/fsb.c index 94c321e8..9812d178 100644 --- a/src/meta/fsb.c +++ b/src/meta/fsb.c @@ -289,8 +289,9 @@ VGMSTREAM * init_vgmstream_fsb(STREAMFILE *streamFile) { vgmstream->coding_type = coding_XBOX; vgmstream->layout_type = layout_none; - /* "interleaved header" IMA, only used with >2ch (ex. Blade Kitten 5.1) */ - if (vgmstream->channels > 2) + /* "interleaved header" IMA, only used with >2ch (ex. Blade Kitten 6ch) + * or (seemingly) when flag is used (ex. Dead to Rights 2 (Xbox) 2ch in FSB3.1 */ + if (vgmstream->channels > 2 || (fsb.mode & FSOUND_MULTICHANNEL)) vgmstream->coding_type = coding_FSB_IMA; } else if (fsb.mode & FSOUND_VAG) { /* FSB1: Jurassic Park Operation Genesis (PS2), FSB4: Spider Man Web of Shadows (PSP) */ From 694cded5872da1d1dcd3e34e2a4f8662e37d46d8 Mon Sep 17 00:00:00 2001 From: bnnm Date: Sun, 28 Jan 2018 00:31:12 +0100 Subject: [PATCH 17/21] Add stream_size to calculate bitrate in subsongs, print bitrate info --- src/vgmstream.c | 16 ++++++++++++++-- src/vgmstream.h | 1 + 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/vgmstream.c b/src/vgmstream.c index f7969294..3677f6fc 100644 --- a/src/vgmstream.c +++ b/src/vgmstream.c @@ -2200,6 +2200,11 @@ void describe_vgmstream(VGMSTREAM * vgmstream, char * desc, int length) { } concatn(length,desc,temp); + snprintf(temp,TEMPSIZE, + "\nbitrate: %d kbps", + get_vgmstream_average_bitrate(vgmstream) / 1000); + concatn(length,desc,temp); + /* only interesting if more than one */ if (vgmstream->num_streams > 1) { snprintf(temp,TEMPSIZE, @@ -2448,9 +2453,11 @@ static STREAMFILE * get_vgmstream_average_bitrate_channel_streamfile(VGMSTREAM * return vgmstream->ch[channel].streamfile; } +static int get_vgmstream_average_bitrate_from_size(size_t size, int sample_rate, int length_samples) { + return (int)((int64_t)size * 8 * sample_rate / length_samples); +} static int get_vgmstream_average_bitrate_from_streamfile(STREAMFILE * streamfile, int sample_rate, int length_samples) { - // todo: not correct in subsongs or formats which only use part of the data - return (int)((int64_t)get_streamfile_size(streamfile) * 8 * sample_rate / length_samples); + return get_vgmstream_average_bitrate_from_size(get_streamfile_size(streamfile), sample_rate, length_samples); } /* Return the average bitrate in bps of all unique files contained within this stream. */ @@ -2468,6 +2475,11 @@ int get_vgmstream_average_bitrate(VGMSTREAM * vgmstream) { if (!sample_rate || !channels || !length_samples) return 0; + /* subsongs need to report this to properly calculate */ + if (vgmstream->stream_size) { + return get_vgmstream_average_bitrate_from_size(vgmstream->stream_size, sample_rate, length_samples); + } + if (channels >= 1) { streamFile = get_vgmstream_average_bitrate_channel_streamfile(vgmstream, 0); if (streamFile) { diff --git a/src/vgmstream.h b/src/vgmstream.h index 5a441927..ba647706 100644 --- a/src/vgmstream.h +++ b/src/vgmstream.h @@ -737,6 +737,7 @@ typedef struct { int num_streams; /* for multi-stream formats (0=not set/one stream, 1=one stream) */ int stream_index; /* selected stream (also 1-based) */ char stream_name[STREAM_NAME_SIZE]; /* name of the current stream (info), if the file stores it and it's filled */ + size_t stream_size; /* info to properly calculate bitrate */ /* looping */ int loop_flag; /* is this stream looped? */ From 528b5c3197e09f82aa4d55ebf3ff20834e90291c Mon Sep 17 00:00:00 2001 From: bnnm Date: Sun, 28 Jan 2018 00:41:25 +0100 Subject: [PATCH 18/21] Fix bitrate for subsongs and rename streams > subsongs --- src/meta/awc.c | 19 ++++++++++--------- src/meta/bik.c | 41 +++++++++++++++++++++++------------------ src/meta/flx.c | 19 ++++++++++++------- src/meta/fsb.c | 9 +++++---- src/meta/fsb5.c | 19 ++++++++++--------- src/meta/kma9.c | 5 +++-- src/meta/ogg_vorbis.c | 12 ++++++------ src/meta/ps2_rxws.c | 29 +++++++++++++++-------------- src/meta/rws.c | 35 ++++++++++++++++++----------------- src/meta/sab.c | 40 ++++++++++++++++++++-------------------- src/meta/sgxd.c | 35 +++++++++++++++++------------------ src/meta/sqex_scd.c | 3 +-- src/meta/sqex_sead.c | 2 ++ src/meta/sxd.c | 21 +++++++++++---------- src/meta/ubi_sb.c | 1 + src/meta/vxn.c | 18 +++++++++--------- src/meta/xvag.c | 5 +++-- src/meta/xwb.c | 1 + 18 files changed, 167 insertions(+), 147 deletions(-) diff --git a/src/meta/awc.c b/src/meta/awc.c index ab2a605b..a102d1b4 100644 --- a/src/meta/awc.c +++ b/src/meta/awc.c @@ -7,7 +7,7 @@ typedef struct { int is_encrypted; int is_music; - int total_streams; + int total_subsongs; int channel_count; int sample_rate; @@ -47,7 +47,8 @@ VGMSTREAM * init_vgmstream_awc(STREAMFILE *streamFile) { vgmstream->sample_rate = awc.sample_rate; vgmstream->num_samples = awc.num_samples; - vgmstream->num_streams = awc.total_streams; + vgmstream->num_streams = awc.total_subsongs; + vgmstream->stream_size = awc.stream_size; vgmstream->meta_type = meta_AWC; @@ -113,7 +114,7 @@ static int parse_awc_header(STREAMFILE* streamFile, awc_header* awc) { int i, ch, entries; uint32_t flags, info_header, tag_count = 0, tags_skip = 0; off_t off; - int target_stream = streamFile->stream_index; + int target_subsong = streamFile->stream_index; memset(awc,0,sizeof(awc_header)); @@ -161,13 +162,13 @@ static int parse_awc_header(STREAMFILE* streamFile, awc_header* awc) { * Music seems layered (N-1/2 stereo pairs), maybe set with events? */ awc->is_music = (read_32bit(off + 0x00,streamFile) & 0x1FFFFFFF) == 0x00000000; if (awc->is_music) { /* all streams except id 0 is a channel */ - awc->total_streams = 1; - target_stream = 1; /* we only need id 0, though channels may have its own tags/chunks */ + awc->total_subsongs = 1; + target_subsong = 1; /* we only need id 0, though channels may have its own tags/chunks */ } else { /* each stream is a single sound */ - awc->total_streams = entries; - if (target_stream == 0) target_stream = 1; - if (target_stream < 0 || target_stream > awc->total_streams || awc->total_streams < 1) goto fail; + awc->total_subsongs = entries; + if (target_subsong == 0) target_subsong = 1; + if (target_subsong < 0 || target_subsong > awc->total_subsongs || awc->total_subsongs < 1) goto fail; } @@ -176,7 +177,7 @@ static int parse_awc_header(STREAMFILE* streamFile, awc_header* awc) { info_header = read_32bit(off + 0x04*i, streamFile); tag_count = (info_header >> 29) & 0x7; /* 3b */ //id = (info_header >> 0) & 0x1FFFFFFF; /* 29b */ - if (target_stream-1 == i) + if (target_subsong-1 == i) break; tags_skip += tag_count; /* tags to skip to reach target's tags, in the next header */ } diff --git a/src/meta/bik.c b/src/meta/bik.c index f703b2a6..3e359576 100644 --- a/src/meta/bik.c +++ b/src/meta/bik.c @@ -2,13 +2,14 @@ #include "../coding/coding.h" #include "../util.h" -static int bink_get_info(STREAMFILE *streamFile, int * out_total_streams, int * out_channel_count, int * out_sample_rate, int * out_num_samples); +static int bink_get_info(STREAMFILE *streamFile, int * out_total_streams, size_t *out_stream_size, int * out_channel_count, int * out_sample_rate, int * out_num_samples); /* BINK 1/2 - RAD Game Tools movies (audio/video format) */ VGMSTREAM * init_vgmstream_bik(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; - int channel_count = 0, loop_flag = 0, sample_rate = 0, num_samples = 0, total_streams = 0; - int stream_index = streamFile->stream_index; + int channel_count = 0, loop_flag = 0, sample_rate = 0, num_samples = 0; + int total_subsongs = 0, stream_index = streamFile->stream_index; + size_t stream_size; /* check extension, case insensitive (bika = manually demuxed audio) */ @@ -19,7 +20,7 @@ VGMSTREAM * init_vgmstream_bik(STREAMFILE *streamFile) { (read_32bitBE(0x00,streamFile) & 0xffffff00) != 0x4B423200 ) goto fail; /* find target stream info and samples */ - if (!bink_get_info(streamFile, &total_streams, &channel_count, &sample_rate, &num_samples)) + if (!bink_get_info(streamFile, &total_subsongs, &stream_size, &channel_count, &sample_rate, &num_samples)) goto fail; /* build the VGMSTREAM */ @@ -29,7 +30,8 @@ VGMSTREAM * init_vgmstream_bik(STREAMFILE *streamFile) { vgmstream->layout_type = layout_none; vgmstream->sample_rate = sample_rate; vgmstream->num_samples = num_samples; - vgmstream->num_streams = total_streams; + vgmstream->num_streams = total_subsongs; + vgmstream->stream_size = stream_size; vgmstream->meta_type = meta_BINK; #ifdef VGM_USE_FFMPEG @@ -58,12 +60,13 @@ fail: * as they are not in the main header. The header for BINK1 and 2 is the same. * (a ~3 min movie needs ~6000-7000 frames = fseeks, should be fast enough) */ -static int bink_get_info(STREAMFILE *streamFile, int * out_total_streams, int * out_channel_count, int * out_sample_rate, int * out_num_samples) { +static int bink_get_info(STREAMFILE *streamFile, int * out_total_subsongs, size_t * out_stream_size, int * out_channel_count, int * out_sample_rate, int * out_num_samples) { uint32_t *offsets = NULL; uint32_t num_frames, num_samples_b = 0; off_t cur_offset; int i, j, sample_rate, channel_count; - int total_streams, target_stream = streamFile->stream_index; + int total_subsongs, target_subsong = streamFile->stream_index; + size_t stream_size = 0; size_t filesize = get_streamfile_size(streamFile); uint32_t signature = (read_32bitBE(0x00,streamFile) & 0xffffff00); @@ -76,20 +79,20 @@ static int bink_get_info(STREAMFILE *streamFile, int * out_total_streams, int * if (num_frames == 0 || num_frames > 0x100000) goto fail; /* something must be off (avoids big allocs below) */ /* multichannel/multilanguage audio is usually N streams of stereo/mono, no way to know channel layout */ - total_streams = read_32bitLE(0x28,streamFile); - if (target_stream == 0) target_stream = 1; - if (target_stream < 0 || target_stream > total_streams || total_streams < 1 || total_streams > 255) goto fail; + total_subsongs = read_32bitLE(0x28,streamFile); + if (target_subsong == 0) target_subsong = 1; + if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1 || total_subsongs > 255) goto fail; /* find stream info and position in offset table */ cur_offset = 0x2c; if ((signature == 0x42494B00 && (revision == 0x6b)) || /* k */ (signature == 0x4B423200 && (revision == 0x69 || revision == 0x6a || revision == 0x6b))) /* i,j,k */ cur_offset += 0x04; /* unknown v2 header field */ - cur_offset += 0x04*total_streams; /* skip streams max packet bytes */ - sample_rate = (uint16_t)read_16bitLE(cur_offset+0x04*(target_stream-1)+0x00,streamFile); - channel_count = (uint16_t)read_16bitLE(cur_offset+0x04*(target_stream-1)+0x02,streamFile) & 0x2000 ? 2 : 1; /* stereo flag */ - cur_offset += 0x04*total_streams; /* skip streams info */ - cur_offset += 0x04*total_streams; /* skip streams ids */ + cur_offset += 0x04*total_subsongs; /* skip streams max packet bytes */ + sample_rate = (uint16_t)read_16bitLE(cur_offset+0x04*(target_subsong-1)+0x00,streamFile); + channel_count = (uint16_t)read_16bitLE(cur_offset+0x04*(target_subsong-1)+0x02,streamFile) & 0x2000 ? 2 : 1; /* stereo flag */ + cur_offset += 0x04*total_subsongs; /* skip streams info */ + cur_offset += 0x04*total_subsongs; /* skip streams ids */ /* read frame offsets in a buffer, to avoid fseeking to the table back and forth */ @@ -111,10 +114,11 @@ static int bink_get_info(STREAMFILE *streamFile, int * out_total_streams, int * cur_offset = offsets[i]; /* read audio packet headers per stream */ - for (j=0; j < total_streams; j++) { + for (j=0; j < total_subsongs; j++) { uint32_t ap_size = read_32bitLE(cur_offset+0x00,streamFile); /* not counting this int */ - if (j == target_stream-1) { + if (j == target_subsong-1) { + stream_size += 0x04 + ap_size; if (ap_size > 0) num_samples_b += read_32bitLE(cur_offset+0x04,streamFile); /* decoded samples in bytes */ break; /* next frame */ @@ -128,7 +132,8 @@ static int bink_get_info(STREAMFILE *streamFile, int * out_total_streams, int * free(offsets); - if (out_total_streams) *out_total_streams = total_streams; + if (out_total_subsongs) *out_total_subsongs = total_subsongs; + if (out_stream_size) *out_stream_size = stream_size; if (out_sample_rate) *out_sample_rate = sample_rate; if (out_channel_count) *out_channel_count = channel_count; //todo returns a few more samples (~48) than binkconv.exe? diff --git a/src/meta/flx.c b/src/meta/flx.c index ab28a322..d37762e8 100644 --- a/src/meta/flx.c +++ b/src/meta/flx.c @@ -7,7 +7,8 @@ VGMSTREAM * init_vgmstream_flx(STREAMFILE *streamFile) { off_t start_offset, stream_offset = 0; size_t data_size; int loop_flag, channel_count, codec; - int total_streams = 0, target_stream = streamFile->stream_index; + int total_subsongs = 0, target_subsong = streamFile->stream_index; + size_t stream_size = 0; /* check extensions (.flx: name of archive, files inside don't have extensions) */ @@ -24,23 +25,26 @@ VGMSTREAM * init_vgmstream_flx(STREAMFILE *streamFile) { || read_32bitLE(0x58,streamFile) != get_streamfile_size(streamFile)) goto fail; - if (target_stream == 0) target_stream = 1; + if (target_subsong == 0) target_subsong = 1; for (i = 0; i < entries; i++) { off_t entry_offset = read_32bitLE(offset + 0x00, streamFile); - /* 0x04: stream size */ + size_t entry_size = read_32bitLE(offset + 0x04, streamFile); offset += 0x08; if (entry_offset != 0x00) - total_streams++; /* many entries are empty */ - if (total_streams == target_stream && stream_offset == 0) + total_subsongs++; /* many entries are empty */ + if (total_subsongs == target_subsong && stream_offset == 0) { stream_offset = entry_offset; /* found but let's keep adding total_streams */ + stream_size = entry_size; + } } - if (target_stream < 0 || target_stream > total_streams || total_streams < 1) goto fail; + if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail; if (stream_offset == 0x00) goto fail; } else { stream_offset = 0x00; + stream_size = get_streamfile_size(streamFile); } if (read_32bitLE(stream_offset + 0x30,streamFile) != 0x10) @@ -57,7 +61,8 @@ VGMSTREAM * init_vgmstream_flx(STREAMFILE *streamFile) { if (!vgmstream) goto fail; vgmstream->sample_rate = read_32bitLE(stream_offset + 0x2c,streamFile); - vgmstream->num_streams = total_streams; + vgmstream->num_streams = total_subsongs; + vgmstream->stream_size = stream_size; vgmstream->meta_type = meta_PC_FLX; switch(codec) { diff --git a/src/meta/fsb.c b/src/meta/fsb.c index 9812d178..6a6bbf53 100644 --- a/src/meta/fsb.c +++ b/src/meta/fsb.c @@ -100,7 +100,7 @@ VGMSTREAM * init_vgmstream_fsb(STREAMFILE *streamFile) { off_t start_offset; size_t custom_data_offset; int loop_flag = 0; - int target_stream = streamFile->stream_index; + int target_subsong = streamFile->stream_index; fsb_header fsb = {0}; @@ -185,8 +185,8 @@ VGMSTREAM * init_vgmstream_fsb(STREAMFILE *streamFile) { } if (fsb.sample_header_size < fsb.sample_header_min) goto fail; - if (target_stream == 0) target_stream = 1; - if (target_stream < 0 || target_stream > fsb.total_subsongs || fsb.total_subsongs < 1) goto fail; + if (target_subsong == 0) target_subsong = 1; + if (target_subsong < 0 || target_subsong > fsb.total_subsongs || fsb.total_subsongs < 1) goto fail; /* sample header (N-stream) */ { @@ -210,7 +210,7 @@ VGMSTREAM * init_vgmstream_fsb(STREAMFILE *streamFile) { /* FSB3.1/4: 0x40:mindistance 0x44:maxdistance 0x48:varfreq/size_32bits 0x4c:varvol 0x4e:fsb.varpan */ /* FSB3/4: 0x50:extended_data size_32bits (not always given) */ - if (i+1 == target_stream) /* d_off found */ + if (i+1 == target_subsong) /* d_off found */ break; s_off += stream_header_size; @@ -259,6 +259,7 @@ VGMSTREAM * init_vgmstream_fsb(STREAMFILE *streamFile) { vgmstream->loop_start_sample = fsb.loop_start; vgmstream->loop_end_sample = fsb.loop_end; vgmstream->num_streams = fsb.total_subsongs; + vgmstream->stream_size = fsb.stream_size; vgmstream->meta_type = fsb.meta_type; if (fsb.name_offset) read_string(vgmstream->stream_name,fsb.name_size+1, fsb.name_offset,streamFile); diff --git a/src/meta/fsb5.c b/src/meta/fsb5.c index a29fa70e..0d79502b 100644 --- a/src/meta/fsb5.c +++ b/src/meta/fsb5.c @@ -11,7 +11,7 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) { uint32_t NumSamples = 0, LoopStart = 0, LoopEnd = 0; int LoopFlag = 0, ChannelCount = 0, Version, SampleRate = 0, CodingID; - int TotalStreams, TargetStream = streamFile->stream_index; + int TotalSubsongs, TargetSubsong = streamFile->stream_index; int i; /* check extension, case insensitive */ @@ -25,7 +25,7 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) { Version = read_32bitLE(0x04,streamFile); if (Version != 0x00 && Version != 0x01) goto fail; - TotalStreams = read_32bitLE(0x08,streamFile); + TotalSubsongs = read_32bitLE(0x08,streamFile); SampleHeaderLength = read_32bitLE(0x0C,streamFile); NameTableLength = read_32bitLE(0x10,streamFile); SampleDataLength = read_32bitLE(0x14,streamFile); @@ -37,14 +37,14 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) { if ((SampleHeaderLength + NameTableLength + SampleDataLength + BaseHeaderLength) != get_streamfile_size(streamFile)) goto fail; - if (TargetStream == 0) TargetStream = 1; /* default to 1 */ - if (TargetStream > TotalStreams || TotalStreams <= 0) goto fail; + if (TargetSubsong == 0) TargetSubsong = 1; /* default to 1 */ + if (TargetSubsong > TotalSubsongs || TotalSubsongs <= 0) goto fail; SampleHeaderStart = BaseHeaderLength; /* find target stream header and data offset, and read all needed values for later use * (reads one by one as the size of a single stream header is variable) */ - for (i = 1; i <= TotalStreams; i++) { + for (i = 1; i <= TotalSubsongs; i++) { off_t DataStart = 0; size_t StreamHeaderLength = 0; uint32_t SampleMode1, SampleMode2; @@ -156,11 +156,11 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) { } /* stream found */ - if (i == TargetStream) { + if (i == TargetSubsong) { StartOffset = BaseHeaderLength + SampleHeaderLength + NameTableLength + DataStart; /* get stream size from next stream or datasize if there is only one */ - if (i == TotalStreams) { + if (i == TotalSubsongs) { StreamSize = SampleDataLength - DataStart; } else { uint32_t NextSampleMode = (uint32_t)read_32bitLE(SampleHeaderStart+StreamHeaderLength+0x00,streamFile); @@ -178,7 +178,7 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) { /* get stream name */ if (NameTableLength) { - NameOffset = BaseHeaderLength + SampleHeaderLength + read_32bitLE(BaseHeaderLength + SampleHeaderLength + 0x04*(TargetStream-1),streamFile); + NameOffset = BaseHeaderLength + SampleHeaderLength + read_32bitLE(BaseHeaderLength + SampleHeaderLength + 0x04*(TargetSubsong-1),streamFile); } @@ -187,12 +187,13 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) { if (!vgmstream) goto fail; vgmstream->sample_rate = SampleRate; - vgmstream->num_streams = TotalStreams; vgmstream->num_samples = NumSamples; if (LoopFlag) { vgmstream->loop_start_sample = LoopStart; vgmstream->loop_end_sample = LoopEnd; } + vgmstream->num_streams = TotalSubsongs; + vgmstream->stream_size = StreamSize; vgmstream->meta_type = meta_FSB5; if (NameOffset) read_string(vgmstream->stream_name,STREAM_NAME_SIZE, NameOffset,streamFile); diff --git a/src/meta/kma9.c b/src/meta/kma9.c index 30caa7b2..16f2e783 100644 --- a/src/meta/kma9.c +++ b/src/meta/kma9.c @@ -5,6 +5,7 @@ VGMSTREAM * init_vgmstream_kma9(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; off_t start_offset; + size_t stream_size; int loop_flag, channel_count; int total_subsongs = 0, target_subsong = streamFile->stream_index; @@ -25,7 +26,7 @@ VGMSTREAM * init_vgmstream_kma9(STREAMFILE *streamFile) { if (target_subsong == 0) target_subsong = 1; if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail; /* 0x0c: unknown */ - /* 0x14: data size of each subsong */ + stream_size = read_32bitLE(0x14,streamFile); /* per subsong */ /* build the VGMSTREAM */ @@ -37,7 +38,7 @@ VGMSTREAM * init_vgmstream_kma9(STREAMFILE *streamFile) { vgmstream->loop_start_sample = read_32bitLE(0x24,streamFile); /* with skip_samples? */ vgmstream->loop_end_sample = vgmstream->num_samples; /* 0x28 looks like end samples but isn't, no idea */ vgmstream->num_streams = total_subsongs; - + vgmstream->stream_size = stream_size; vgmstream->meta_type = meta_KMA9; #ifdef VGM_USE_ATRAC9 diff --git a/src/meta/ogg_vorbis.c b/src/meta/ogg_vorbis.c index 0093e94f..a0a72ff2 100644 --- a/src/meta/ogg_vorbis.c +++ b/src/meta/ogg_vorbis.c @@ -274,6 +274,9 @@ VGMSTREAM * init_vgmstream_ogg_vorbis_callbacks(STREAMFILE *streamFile, const ch int32_t loop_length = vgm_inf->loop_length; int loop_end_found = vgm_inf->loop_end_found; int32_t loop_end = vgm_inf->loop_end; + size_t stream_size = vgm_inf->stream_size ? + vgm_inf->stream_size : + get_streamfile_size(streamFile) - start; ov_callbacks default_callbacks; @@ -295,9 +298,7 @@ VGMSTREAM * init_vgmstream_ogg_vorbis_callbacks(STREAMFILE *streamFile, const ch temp_streamfile.start = start; temp_streamfile.offset = 0; - temp_streamfile.size = vgm_inf->stream_size ? - vgm_inf->stream_size : - get_streamfile_size(temp_streamfile.streamfile) - start; + temp_streamfile.size = stream_size; temp_streamfile.decryption_callback = vgm_inf->decryption_callback; temp_streamfile.scd_xor = vgm_inf->scd_xor; @@ -324,9 +325,7 @@ VGMSTREAM * init_vgmstream_ogg_vorbis_callbacks(STREAMFILE *streamFile, const ch data->ov_streamfile.start = start; data->ov_streamfile.offset = 0; - data->ov_streamfile.size = vgm_inf->stream_size ? - vgm_inf->stream_size : - get_streamfile_size(data->ov_streamfile.streamfile) - start; + data->ov_streamfile.size = stream_size; data->ov_streamfile.decryption_callback = vgm_inf->decryption_callback; data->ov_streamfile.scd_xor = vgm_inf->scd_xor; @@ -412,6 +411,7 @@ VGMSTREAM * init_vgmstream_ogg_vorbis_callbacks(STREAMFILE *streamFile, const ch vgmstream->channels = vi->channels; vgmstream->sample_rate = vi->rate; vgmstream->num_streams = vgm_inf->total_subsongs; + vgmstream->stream_size = stream_size; vgmstream->num_samples = ov_pcm_total(ovf,-1); /* let libvorbisfile find total samples */ if (loop_flag) { diff --git a/src/meta/ps2_rxws.c b/src/meta/ps2_rxws.c index d5906243..d32b69e0 100644 --- a/src/meta/ps2_rxws.c +++ b/src/meta/ps2_rxws.c @@ -7,10 +7,10 @@ VGMSTREAM * init_vgmstream_ps2_rxws(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; STREAMFILE * streamHeader = NULL; off_t start_offset, chunk_offset, name_offset = 0; - size_t data_size, chunk_size; + size_t stream_size, chunk_size; int loop_flag = 0, channel_count, is_separate = 0, type, sample_rate; int32_t loop_start, loop_end; - int total_streams, target_stream = streamFile->stream_index; + int total_subsongs, target_subsong = streamFile->stream_index; /* check extensions */ /* .xws: header and data, .xwh+xwb: header + data (.bin+dat are also found in Wild Arms 4/5) */ @@ -46,14 +46,14 @@ VGMSTREAM * init_vgmstream_ps2_rxws(STREAMFILE *streamFile) { /* check multi-streams */ - total_streams = read_32bitLE(chunk_offset+0x00,streamHeader); - if (target_stream == 0) target_stream = 1; - if (target_stream < 0 || target_stream > total_streams || total_streams < 1) goto fail; + total_subsongs = read_32bitLE(chunk_offset+0x00,streamHeader); + if (target_subsong == 0) target_subsong = 1; + if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail; /* read stream header */ { - off_t header_offset = chunk_offset + 0x4 + 0x1c * (target_stream-1); /* position in FORM */ + off_t header_offset = chunk_offset + 0x4 + 0x1c * (target_subsong-1); /* position in FORM */ off_t stream_offset, next_stream_offset, data_offset = 0; type = read_8bit(header_offset+0x00, streamHeader); @@ -83,22 +83,22 @@ VGMSTREAM * init_vgmstream_ps2_rxws(STREAMFILE *streamFile) { if (!data_offset) goto fail; } - if (target_stream == total_streams) { + if (target_subsong == total_subsongs) { next_stream_offset = data_offset + get_streamfile_size(is_separate ? streamFile : streamHeader); } else { - off_t next_header_offset = chunk_offset + 0x4 + 0x1c * (target_stream); + off_t next_header_offset = chunk_offset + 0x4 + 0x1c * (target_subsong); next_stream_offset = read_32bitLE(next_header_offset+0x10,streamHeader); } - data_size = next_stream_offset - stream_offset; + stream_size = next_stream_offset - stream_offset; start_offset = data_offset + stream_offset; } /* get stream name (always follows FORM) */ if (read_32bitBE(0x10+0x10 + chunk_size,streamHeader) == 0x46545854) { /* "FTXT" */ chunk_offset = 0x10+0x10 + chunk_size + 0x10; - if (read_32bitLE(chunk_offset+0x00,streamHeader) == total_streams) { - name_offset = chunk_offset + read_32bitLE(chunk_offset+0x04 + (target_stream-1)*0x04,streamHeader); + if (read_32bitLE(chunk_offset+0x00,streamHeader) == total_subsongs) { + name_offset = chunk_offset + read_32bitLE(chunk_offset+0x04 + (target_subsong-1)*0x04,streamHeader); } } @@ -108,7 +108,8 @@ VGMSTREAM * init_vgmstream_ps2_rxws(STREAMFILE *streamFile) { if (!vgmstream) goto fail; vgmstream->sample_rate = sample_rate; - vgmstream->num_streams = total_streams; + vgmstream->num_streams = total_subsongs; + vgmstream->stream_size = stream_size; vgmstream->meta_type = meta_PS2_RXWS; if (name_offset) read_string(vgmstream->stream_name,STREAM_NAME_SIZE, name_offset,streamHeader); @@ -143,10 +144,10 @@ VGMSTREAM * init_vgmstream_ps2_rxws(STREAMFILE *streamFile) { joint_stereo = 0; encoder_delay = 0x0; - bytes = ffmpeg_make_riff_atrac3(buf, 0x100, vgmstream->num_samples, data_size, vgmstream->channels, vgmstream->sample_rate, block_size, joint_stereo, encoder_delay); + bytes = ffmpeg_make_riff_atrac3(buf, 0x100, vgmstream->num_samples, stream_size, vgmstream->channels, vgmstream->sample_rate, block_size, joint_stereo, encoder_delay); if (bytes <= 0) goto fail; - vgmstream->codec_data = init_ffmpeg_header_offset(streamFile, buf,bytes, start_offset,data_size); + vgmstream->codec_data = init_ffmpeg_header_offset(streamFile, buf,bytes, start_offset,stream_size); if (!vgmstream->codec_data) goto fail; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; diff --git a/src/meta/rws.c b/src/meta/rws.c index d2d2366f..3594eb45 100644 --- a/src/meta/rws.c +++ b/src/meta/rws.c @@ -15,7 +15,7 @@ VGMSTREAM * init_vgmstream_rws(STREAMFILE *streamFile) { size_t block_size = 0, block_size_total = 0; int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL; int i, total_segments; - int total_streams, target_stream = streamFile->stream_index; + int total_subsongs, target_subsong = streamFile->stream_index; if (!check_extensions(streamFile,"rws")) @@ -49,7 +49,7 @@ VGMSTREAM * init_vgmstream_rws(STREAMFILE *streamFile) { * 0x1c: null? 0x30: 0x800?, 0x34: block_size_total?, 0x38: data offset, 0x3c: 0?, 0x40-50: file uuid */ read_32bit = (read_32bitLE(off+0x00,streamFile) > header_size) ? read_32bitBE : read_32bitLE; /* GC/Wii/X360 = BE */ total_segments = read_32bit(off+0x20,streamFile); - total_streams = read_32bit(off+0x28,streamFile); + total_subsongs = read_32bit(off+0x28,streamFile); /* skip audio file name */ off += 0x50 + get_rws_string_size(off+0x50, streamFile); @@ -58,8 +58,8 @@ VGMSTREAM * init_vgmstream_rws(STREAMFILE *streamFile) { /* Data is divided into "segments" (cues/divisions within data, ex. intro+main, voice1+2+..N) and "streams" * of interleaved blocks (for multichannel?). last stream (only?) has padding. Segments divide all streams. * ex.- 0x1800 data + 0 pad of stream_0 2ch, 0x1800 data + 0x200 pad of stream1 2ch (xN). */ - if (target_stream == 0) target_stream = 1; - if (target_stream < 0 || target_stream > total_streams || total_streams < 1) goto fail; + if (target_subsong == 0) target_subsong = 1; + if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail; /* get segment info, for all streams */ /* 0x00/04/0c: command?, 0x18: full segment size (including all streams), 0x1c: offset, others: ?) */ @@ -70,9 +70,9 @@ VGMSTREAM * init_vgmstream_rws(STREAMFILE *streamFile) { /* get usable segment sizes (usually ok but sometimes > stream_size), per stream */ for (i = 0; i < total_segments; i++) { /* sum usable segment sizes (no padding) */ - stream_size += read_32bit(off + 0x04*i + 0x04*total_segments*(target_stream-1),streamFile); + stream_size += read_32bit(off + 0x04*i + 0x04*total_segments*(target_subsong-1),streamFile); } - off += 0x04 * (total_segments * total_streams); + off += 0x04 * (total_segments * total_subsongs); /* skip segment uuids */ off += 0x10 * total_segments; @@ -85,21 +85,21 @@ VGMSTREAM * init_vgmstream_rws(STREAMFILE *streamFile) { /* get stream layout */ /* 0x00/04/14: command?, 0x08: null? 0x0c: spf related? (XADPCM=07, VAG=1C, DSP=0E, PCM=01) * 0x24: offset within data chunk, 0x1c: codec related?, others: ?) */ - for (i = 0; i < total_streams; i++) { /* get block_sizes */ + for (i = 0; i < total_subsongs; i++) { /* get block_sizes */ block_size_total += read_32bit(off + 0x10 + 0x28*i, streamFile); /* for all streeams, to skip during decode */ - if (i+1 == target_stream) { + if (i+1 == target_subsong) { //block_size_full = read_32bit(off + 0x10 + 0x28*i, streamFile); /* with padding, can be different per stream */ block_size = read_32bit(off + 0x20 + 0x28*i, streamFile); /* without padding */ stream_offset = read_32bit(off + 0x24 + 0x28*i, streamFile); /* within data */ } } - off += 0x28 * total_streams; + off += 0x28 * total_subsongs; /* get stream config */ /* 0x04: command?, 0x0c(1): bits per sample, others: null? */ - for (i = 0; i < total_streams; i++) { /* size depends on codec so we must parse it */ + for (i = 0; i < total_subsongs; i++) { /* size depends on codec so we must parse it */ int prev_codec = 0; - if (i+1 == target_stream) { + if (i+1 == target_subsong) { sample_rate = read_32bit(off+0x00, streamFile); //unk_size = read_32bit(off+0x08, streamFile); /* segment size again? loop-related? */ channel_count = read_8bit(off+0x0d, streamFile); @@ -110,7 +110,7 @@ VGMSTREAM * init_vgmstream_rws(STREAMFILE *streamFile) { if (prev_codec == 0xF86215B0) { /* if codec is DSP there is an extra field per stream */ /* 0x00: approx num samples? 0x04: approx size/loop related? (can be 0) */ - if (i+1 == target_stream) { + if (i+1 == target_subsong) { coefs_offset = off + 0x1c; } off += 0x60; @@ -120,11 +120,11 @@ VGMSTREAM * init_vgmstream_rws(STREAMFILE *streamFile) { } /* skip stream uuids */ - off += 0x10 * total_streams; + off += 0x10 * total_subsongs; /* get stream name */ - for (i = 0; i < total_streams; i++) { - if (i+1 == target_stream) { + for (i = 0; i < total_subsongs; i++) { + if (i+1 == target_subsong) { name_offset = off; } off += get_rws_string_size(off, streamFile); @@ -137,7 +137,7 @@ VGMSTREAM * init_vgmstream_rws(STREAMFILE *streamFile) { start_offset = 0x0c + 0x0c + header_size + 0x0c + stream_offset; /* sometimes it's wrong for no apparent reason (probably a bug in RWS) */ - stream_size_expected = (stream_size_full / block_size_total) * (block_size * total_streams) / total_streams; + stream_size_expected = (stream_size_full / block_size_total) * (block_size * total_subsongs) / total_subsongs; if (stream_size > stream_size_expected) { VGM_LOG("RWS: readjusting wrong stream size %x vs expected %x\n", stream_size, stream_size_expected); stream_size = stream_size_expected; @@ -149,7 +149,8 @@ VGMSTREAM * init_vgmstream_rws(STREAMFILE *streamFile) { if (!vgmstream) goto fail; vgmstream->sample_rate = sample_rate; - vgmstream->num_streams = total_streams; + vgmstream->num_streams = total_subsongs; + vgmstream->stream_size = stream_size; vgmstream->meta_type = meta_RWS; if (name_offset) read_string(vgmstream->stream_name,STREAM_NAME_SIZE, name_offset,streamFile); diff --git a/src/meta/sab.c b/src/meta/sab.c index 450fd8d4..8532e478 100644 --- a/src/meta/sab.c +++ b/src/meta/sab.c @@ -8,8 +8,8 @@ static void get_stream_name(char * stream_name, STREAMFILE *streamFile, int targ VGMSTREAM * init_vgmstream_sab(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; off_t start_offset; - int loop_flag, channel_count = 0, is_stream, align, codec, sample_rate, data_size, loop_start, loop_end; - int total_streams, target_stream = streamFile->stream_index; + int loop_flag, channel_count = 0, is_stream, align, codec, sample_rate, stream_size, loop_start, loop_end; + int total_subsongs, target_subsong = streamFile->stream_index; /* .sab: main, .sob: config/names */ if (!check_extensions(streamFile,"sab")) @@ -20,29 +20,29 @@ VGMSTREAM * init_vgmstream_sab(STREAMFILE *streamFile) { goto fail; is_stream = read_32bitLE(0x04,streamFile) & 0x04; /* other flags don't seem to matter */ - total_streams = is_stream ? 1 : read_32bitLE(0x08,streamFile); - if (target_stream == 0) target_stream = 1; - if (target_stream < 0 || target_stream > total_streams || total_streams < 1) goto fail; + total_subsongs = is_stream ? 1 : read_32bitLE(0x08,streamFile); + if (target_subsong == 0) target_subsong = 1; + if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail; align = read_32bitLE(0x0c,streamFile); /* doubles as interleave */ /* stream config */ - codec = read_32bitLE(0x18 + 0x1c*(target_stream-1) + 0x00,streamFile); - channel_count = read_32bitLE(0x18 + 0x1c*(target_stream-1) + 0x04,streamFile); - sample_rate = read_32bitLE(0x18 + 0x1c*(target_stream-1) + 0x08,streamFile); - data_size = read_32bitLE(0x18 + 0x1c*(target_stream-1) + 0x0c,streamFile); - loop_start = read_32bitLE(0x18 + 0x1c*(target_stream-1) + 0x10,streamFile); - loop_end = read_32bitLE(0x18 + 0x1c*(target_stream-1) + 0x14,streamFile); + codec = read_32bitLE(0x18 + 0x1c*(target_subsong-1) + 0x00,streamFile); + channel_count = read_32bitLE(0x18 + 0x1c*(target_subsong-1) + 0x04,streamFile); + sample_rate = read_32bitLE(0x18 + 0x1c*(target_subsong-1) + 0x08,streamFile); + stream_size = read_32bitLE(0x18 + 0x1c*(target_subsong-1) + 0x0c,streamFile); + loop_start = read_32bitLE(0x18 + 0x1c*(target_subsong-1) + 0x10,streamFile); + loop_end = read_32bitLE(0x18 + 0x1c*(target_subsong-1) + 0x14,streamFile); loop_flag = (loop_end > 0); - start_offset = 0x18 + 0x1c*total_streams; + start_offset = 0x18 + 0x1c*total_subsongs; if (start_offset % align) start_offset += align - (start_offset % align); - start_offset += read_32bitLE(0x18 + 0x1c*(target_stream-1) + 0x18,streamFile); + start_offset += read_32bitLE(0x18 + 0x1c*(target_subsong-1) + 0x18,streamFile); if (is_stream) { channel_count = read_32bitLE(0x08,streamFile); /* uncommon, but non-stream stereo exists */ - data_size *= channel_count; + stream_size *= channel_count; } /* build the VGMSTREAM */ @@ -50,8 +50,8 @@ VGMSTREAM * init_vgmstream_sab(STREAMFILE *streamFile) { if (!vgmstream) goto fail; vgmstream->sample_rate = sample_rate; - - vgmstream->num_streams = total_streams; + vgmstream->num_streams = total_subsongs; + vgmstream->stream_size = stream_size; vgmstream->meta_type = meta_SAB; switch(codec) { @@ -60,7 +60,7 @@ VGMSTREAM * init_vgmstream_sab(STREAMFILE *streamFile) { vgmstream->layout_type = layout_interleave; vgmstream->interleave_block_size = is_stream ? align : 0x02; - vgmstream->num_samples = pcm_bytes_to_samples(data_size, vgmstream->channels, 16); + vgmstream->num_samples = pcm_bytes_to_samples(stream_size, vgmstream->channels, 16); vgmstream->loop_start_sample = pcm_bytes_to_samples(loop_start, vgmstream->channels, 16); vgmstream->loop_end_sample = pcm_bytes_to_samples(loop_end, vgmstream->channels, 16); @@ -71,7 +71,7 @@ VGMSTREAM * init_vgmstream_sab(STREAMFILE *streamFile) { vgmstream->layout_type = layout_interleave; vgmstream->interleave_block_size = is_stream ? align : 0x10; - vgmstream->num_samples = ps_bytes_to_samples(data_size, vgmstream->channels); + vgmstream->num_samples = ps_bytes_to_samples(stream_size, vgmstream->channels); vgmstream->loop_start_sample = ps_bytes_to_samples(loop_start, vgmstream->channels); vgmstream->loop_end_sample = ps_bytes_to_samples(loop_end, vgmstream->channels); break; @@ -81,7 +81,7 @@ VGMSTREAM * init_vgmstream_sab(STREAMFILE *streamFile) { vgmstream->layout_type = is_stream ? layout_interleave : layout_none; vgmstream->interleave_block_size = is_stream ? align : 0x00; - vgmstream->num_samples = ms_ima_bytes_to_samples(data_size, 0x24*vgmstream->channels, vgmstream->channels); + vgmstream->num_samples = ms_ima_bytes_to_samples(stream_size, 0x24*vgmstream->channels, vgmstream->channels); vgmstream->loop_start_sample = ms_ima_bytes_to_samples(loop_start, 0x24*vgmstream->channels, vgmstream->channels); vgmstream->loop_end_sample = ms_ima_bytes_to_samples(loop_end, 0x24*vgmstream->channels, vgmstream->channels); break; @@ -91,7 +91,7 @@ VGMSTREAM * init_vgmstream_sab(STREAMFILE *streamFile) { goto fail; } - get_stream_name(vgmstream->stream_name, streamFile, target_stream); + get_stream_name(vgmstream->stream_name, streamFile, target_subsong); if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) goto fail; diff --git a/src/meta/sgxd.c b/src/meta/sgxd.c index 07cfba50..33c04628 100644 --- a/src/meta/sgxd.c +++ b/src/meta/sgxd.c @@ -2,19 +2,17 @@ #include "../coding/coding.h" -/* SGXD - Sony/SCEI's format (SGB+SGH / SGD / SGX), found in: - * PS3: Genji, Folklore, Afrika (Short VAG), Tokyo Jungle - * PSP: Brave Story, Sarugetchu Sarusaru Daisakusen, Kurohyo 1/2, Pathwork Heroes */ +/* SGXD - Sony/SCEI's format (SGB+SGH / SGD / SGX) */ VGMSTREAM * init_vgmstream_sgxd(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; STREAMFILE * streamHeader = NULL; off_t start_offset, data_offset, chunk_offset, name_offset = 0; - size_t data_size; + size_t stream_size; int is_sgx, is_sgb = 0; int loop_flag, channels, type; int sample_rate, num_samples, loop_start_sample, loop_end_sample; - int total_streams, target_stream = streamFile->stream_index; + int total_subsongs, target_subsong = streamFile->stream_index; /* check extension, case insensitive */ @@ -59,14 +57,14 @@ VGMSTREAM * init_vgmstream_sgxd(STREAMFILE *streamFile) { /* 0x04 SGX: unknown; SGD/SGH: chunk length, 0x08 null */ /* check multi-streams (usually only SE containers; Puppeteer) */ - total_streams = read_32bitLE(chunk_offset+0x04,streamHeader); - if (target_stream == 0) target_stream = 1; - if (target_stream < 0 || target_stream > total_streams || total_streams < 1) goto fail; + total_subsongs = read_32bitLE(chunk_offset+0x04,streamHeader); + if (target_subsong == 0) target_subsong = 1; + if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail; /* read stream header */ { off_t stream_offset; - chunk_offset += 0x08 + 0x38 * (target_stream-1); /* position in target header*/ + chunk_offset += 0x08 + 0x38 * (target_subsong-1); /* position in target header*/ /* 0x00 ? (00/01/02) */ if (!is_sgx) /* meaning unknown in .sgx; offset 0 = not a stream (a RGND sample) */ @@ -85,7 +83,7 @@ VGMSTREAM * init_vgmstream_sgxd(STREAMFILE *streamFile) { num_samples = read_32bitLE(chunk_offset+0x20,streamHeader); loop_start_sample = read_32bitLE(chunk_offset+0x24,streamHeader); loop_end_sample = read_32bitLE(chunk_offset+0x28,streamHeader); - data_size = read_32bitLE(chunk_offset+0x2c,streamHeader); /* stream size (without padding) / interleave (for type3) */ + stream_size = read_32bitLE(chunk_offset+0x2c,streamHeader); /* stream size (without padding) / interleave (for type3) */ if (is_sgx) { stream_offset = 0x0; @@ -107,7 +105,8 @@ VGMSTREAM * init_vgmstream_sgxd(STREAMFILE *streamFile) { vgmstream->num_samples = num_samples; vgmstream->loop_start_sample = loop_start_sample; vgmstream->loop_end_sample = loop_end_sample; - vgmstream->num_streams = total_streams; + vgmstream->num_streams = total_subsongs; + vgmstream->stream_size = stream_size; vgmstream->meta_type = meta_SGXD; if (name_offset) read_string(vgmstream->stream_name,STREAM_NAME_SIZE, name_offset,streamHeader); @@ -118,23 +117,23 @@ VGMSTREAM * init_vgmstream_sgxd(STREAMFILE *streamFile) { vgmstream->loop_end_sample -= 1; switch (type) { - case 0x03: /* PS-ADPCM */ + case 0x03: /* PS-ADPCM [Genji (PS3), Ape Escape Move (PS3)]*/ vgmstream->coding_type = coding_PSX; vgmstream->layout_type = layout_interleave; if (is_sgx || is_sgb) { vgmstream->interleave_block_size = 0x10; } else { /* this only seems to happen with SFX */ - vgmstream->interleave_block_size = data_size; + vgmstream->interleave_block_size = stream_size; } break; #ifdef VGM_USE_FFMPEG - case 0x04: { /* ATRAC3plus */ + case 0x04: { /* ATRAC3plus [Kurohyo 1/2 (PSP), BraveStory (PSP)] */ ffmpeg_codec_data *ffmpeg_data; /* internally has a RIFF header; but the SGXD header / sample rate has priority over it (may not match) */ - ffmpeg_data = init_ffmpeg_offset(streamFile, start_offset, data_size); + ffmpeg_data = init_ffmpeg_offset(streamFile, start_offset, stream_size); if ( !ffmpeg_data ) goto fail; vgmstream->codec_data = ffmpeg_data; vgmstream->coding_type = coding_FFmpeg; @@ -158,7 +157,7 @@ VGMSTREAM * init_vgmstream_sgxd(STREAMFILE *streamFile) { break; } #endif - case 0x05: /* Short PS-ADPCM */ + case 0x05: /* Short PS-ADPCM [Afrika (PS3)] */ vgmstream->coding_type = coding_PSX_cfg; vgmstream->layout_type = layout_interleave; vgmstream->interleave_block_size = 0x4; @@ -166,10 +165,10 @@ VGMSTREAM * init_vgmstream_sgxd(STREAMFILE *streamFile) { break; #ifdef VGM_USE_FFMPEG - case 0x06: { /* AC3 */ + case 0x06: { /* AC3 [Tokyo Jungle (PS3), Afrika (PS3)] */ ffmpeg_codec_data *ffmpeg_data; - ffmpeg_data = init_ffmpeg_offset(streamFile, start_offset, data_size); + ffmpeg_data = init_ffmpeg_offset(streamFile, start_offset, stream_size); if ( !ffmpeg_data ) goto fail; vgmstream->codec_data = ffmpeg_data; vgmstream->coding_type = coding_FFmpeg; diff --git a/src/meta/sqex_scd.c b/src/meta/sqex_scd.c index 9862b69e..203fbd9d 100644 --- a/src/meta/sqex_scd.c +++ b/src/meta/sqex_scd.c @@ -185,10 +185,9 @@ VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE *streamFile) { vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; - /* fill in the vital statistics */ - vgmstream->channels = channel_count; vgmstream->sample_rate = sample_rate; vgmstream->num_streams = total_subsongs; + vgmstream->stream_size = stream_size; vgmstream->meta_type = meta_SQEX_SCD; switch (codec) { diff --git a/src/meta/sqex_sead.c b/src/meta/sqex_sead.c index 4e324953..2c98eaa2 100644 --- a/src/meta/sqex_sead.c +++ b/src/meta/sqex_sead.c @@ -140,6 +140,7 @@ VGMSTREAM * init_vgmstream_sqex_sead(STREAMFILE * streamFile) { vgmstream->sample_rate = sample_rate; vgmstream->num_streams = total_subsongs; + vgmstream->stream_size = stream_size; vgmstream->meta_type = is_sab ? meta_SQEX_SAB : meta_SQEX_MAB; switch(codec) { @@ -219,6 +220,7 @@ VGM_LOG("2\n"); if (temp_vgmstream) { /* loops can be slightly different (~1000 samples) but probably HCA's are more accurate */ temp_vgmstream->num_streams = vgmstream->num_streams; + temp_vgmstream->stream_size = vgmstream->stream_size; temp_vgmstream->meta_type = vgmstream->meta_type; close_streamfile(temp_streamFile); diff --git a/src/meta/sxd.c b/src/meta/sxd.c index c0173294..13801cad 100644 --- a/src/meta/sxd.c +++ b/src/meta/sxd.c @@ -7,13 +7,13 @@ VGMSTREAM * init_vgmstream_sxd(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; STREAMFILE * streamHeader = NULL; off_t start_offset, chunk_offset, first_offset = 0x60, name_offset = 0; - size_t chunk_size; + size_t chunk_size, stream_size = 0; int is_separate; int loop_flag, channels, codec; int sample_rate, num_samples, loop_start_sample, loop_end_sample; uint32_t at9_config_data = 0; - int total_streams, target_stream = streamFile->stream_index; + int total_subsongs, target_subsong = streamFile->stream_index; /* check extension, case insensitive */ @@ -38,16 +38,16 @@ VGMSTREAM * init_vgmstream_sxd(STREAMFILE *streamFile) { if (!find_chunk_le(streamHeader, 0x57415645,first_offset,0, &chunk_offset,&chunk_size)) goto fail; /* "WAVE" */ /* check multi-streams (usually only in SFX containers) */ - total_streams = read_32bitLE(chunk_offset+0x04,streamHeader); - if (target_stream == 0) target_stream = 1; - if (target_stream < 0 || target_stream > total_streams || total_streams < 1) goto fail; + total_subsongs = read_32bitLE(chunk_offset+0x04,streamHeader); + if (target_subsong == 0) target_subsong = 1; + if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail; /* read stream header */ { off_t table_offset, header_offset, stream_offset; /* get target offset using table of relative offsets within WAVE */ - table_offset = chunk_offset + 0x08 + 4*(target_stream-1); + table_offset = chunk_offset + 0x08 + 4*(target_subsong-1); header_offset = table_offset + read_32bitLE(table_offset,streamHeader); /* 0x00(4): type/location? (00/01=sxd/RAM?, 02/03=sxd2/stream?) */ @@ -59,7 +59,7 @@ VGMSTREAM * init_vgmstream_sxd(STREAMFILE *streamFile) { num_samples = read_32bitLE(header_offset+0x14,streamHeader); loop_start_sample = read_32bitLE(header_offset+0x18,streamHeader); loop_end_sample = read_32bitLE(header_offset+0x1c,streamHeader); - /* 0x20(4): data size */ + stream_size = read_32bitLE(header_offset+0x20,streamHeader); stream_offset = read_32bitLE(header_offset+0x24,streamHeader); /* Extra data, variable sized and uses some kind of TLVs (HEVAG's is optional and much smaller). @@ -100,7 +100,7 @@ VGMSTREAM * init_vgmstream_sxd(STREAMFILE *streamFile) { int num_entries = read_16bitLE(chunk_offset+0x04,streamHeader); /* can be bigger than streams */ for (i = 0; i < num_entries; i++) { uint32_t index = (uint32_t)read_32bitLE(chunk_offset+0x08 + 0x08 + i*0x0c,streamHeader); - if (index+1 == target_stream) { + if (index+1 == target_subsong) { name_offset = chunk_offset+0x08 + 0x00 + i*0x0c + read_32bitLE(chunk_offset+0x08 + 0x00 + i*0x0c,streamHeader); break; } @@ -116,7 +116,8 @@ VGMSTREAM * init_vgmstream_sxd(STREAMFILE *streamFile) { vgmstream->num_samples = num_samples; vgmstream->loop_start_sample = loop_start_sample; vgmstream->loop_end_sample = loop_end_sample; - vgmstream->num_streams = total_streams; + vgmstream->num_streams = total_subsongs; + vgmstream->stream_size = stream_size; vgmstream->meta_type = meta_SXD; if (name_offset) read_string(vgmstream->stream_name,STREAM_NAME_SIZE, name_offset,streamHeader); @@ -148,7 +149,7 @@ VGMSTREAM * init_vgmstream_sxd(STREAMFILE *streamFile) { break; } #endif - //case 0x28: /* dummy codec? (found with 0 samples) */ + //case 0x28: /* dummy codec? (found with 0 samples) [Hot Shots Golf: World Invitational (Vita) sfx] */ default: VGM_LOG("SXD: unknown codec 0x%x\n", codec); goto fail; diff --git a/src/meta/ubi_sb.c b/src/meta/ubi_sb.c index 27495f35..d0ad0272 100644 --- a/src/meta/ubi_sb.c +++ b/src/meta/ubi_sb.c @@ -127,6 +127,7 @@ VGMSTREAM * init_vgmstream_ubi_sb(STREAMFILE *streamFile) { vgmstream->sample_rate = sb.sample_rate; vgmstream->num_streams = sb.total_streams; + vgmstream->stream_size = sb.stream_size; vgmstream->meta_type = meta_UBI_SB; diff --git a/src/meta/vxn.c b/src/meta/vxn.c index 238d3e24..27aacce0 100644 --- a/src/meta/vxn.c +++ b/src/meta/vxn.c @@ -7,7 +7,7 @@ VGMSTREAM * init_vgmstream_vxn(STREAMFILE *streamFile) { int loop_flag = 0, channel_count, codec, sample_rate, block_align, bits, num_samples; off_t start_offset, stream_offset, chunk_offset, first_offset = 0x00; size_t stream_size; - int total_streams, target_stream = streamFile->stream_index; + int total_subsongs, target_subsong = streamFile->stream_index; /* check extensions */ if (!check_extensions(streamFile,"vxn")) @@ -31,13 +31,13 @@ VGMSTREAM * init_vgmstream_vxn(STREAMFILE *streamFile) { * (the "Plst" and "Rule" chunks may have order info) */ if (!find_chunk_le(streamFile, 0x5365676D,first_offset,0, &chunk_offset,NULL)) /* "Segm" */ goto fail; - total_streams = read_32bitLE(chunk_offset+0x00, streamFile); - if (target_stream == 0) target_stream = 1; - if (target_stream < 0 || target_stream > total_streams || total_streams < 1) goto fail; + total_subsongs = read_32bitLE(chunk_offset+0x00, streamFile); + if (target_subsong == 0) target_subsong = 1; + if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail; - stream_offset = read_32bitLE(chunk_offset+0x04 + (target_stream-1)*0x18 + 0x00, streamFile); - stream_size = read_32bitLE(chunk_offset+0x04 + (target_stream-1)*0x18 + 0x04, streamFile); - num_samples = read_32bitLE(chunk_offset+0x04 + (target_stream-1)*0x18 + 0x08, streamFile); + stream_offset = read_32bitLE(chunk_offset+0x04 + (target_subsong-1)*0x18 + 0x00, streamFile); + stream_size = read_32bitLE(chunk_offset+0x04 + (target_subsong-1)*0x18 + 0x04, streamFile); + num_samples = read_32bitLE(chunk_offset+0x04 + (target_subsong-1)*0x18 + 0x08, streamFile); if (!find_chunk_le(streamFile, 0x44617461,first_offset,0, &chunk_offset,NULL)) /* "Data" */ goto fail; @@ -50,8 +50,8 @@ VGMSTREAM * init_vgmstream_vxn(STREAMFILE *streamFile) { vgmstream->sample_rate = sample_rate; vgmstream->num_samples = num_samples; - vgmstream->num_streams = total_streams; - + vgmstream->num_streams = total_subsongs; + vgmstream->stream_size = stream_size; vgmstream->meta_type = meta_VXN; switch (codec) { diff --git a/src/meta/xvag.c b/src/meta/xvag.c index 57a9a9f8..382a4e0a 100644 --- a/src/meta/xvag.c +++ b/src/meta/xvag.c @@ -16,7 +16,7 @@ VGMSTREAM * init_vgmstream_xvag(STREAMFILE *streamFile) { off_t start_offset, loop_start = 0, loop_end = 0, chunk_offset; off_t first_offset = 0x20; - size_t chunk_size; + size_t chunk_size, stream_size; /* check extension, case insensitive */ if (!check_extensions(streamFile,"xvag")) @@ -51,7 +51,7 @@ VGMSTREAM * init_vgmstream_xvag(STREAMFILE *streamFile) { interleave_factor = read_32bit(chunk_offset+0x10,streamFile); sample_rate = read_32bit(chunk_offset+0x14,streamFile); - /* 0x18: datasize */ + stream_size = read_32bit(chunk_offset+0x18,streamFile); /* extra data, seen in versions 0x61+ */ if (chunk_size > 0x1c) { @@ -86,6 +86,7 @@ VGMSTREAM * init_vgmstream_xvag(STREAMFILE *streamFile) { vgmstream->sample_rate = sample_rate; vgmstream->num_samples = num_samples; vgmstream->num_streams = total_subsongs; + vgmstream->stream_size = (stream_size / total_subsongs); vgmstream->meta_type = meta_XVAG; switch (codec) { diff --git a/src/meta/xwb.c b/src/meta/xwb.c index 876940d3..88335d95 100644 --- a/src/meta/xwb.c +++ b/src/meta/xwb.c @@ -354,6 +354,7 @@ VGMSTREAM * init_vgmstream_xwb(STREAMFILE *streamFile) { vgmstream->loop_start_sample = xwb.loop_start_sample; vgmstream->loop_end_sample = xwb.loop_end_sample; vgmstream->num_streams = xwb.streams; + vgmstream->stream_size = xwb.stream_size; vgmstream->meta_type = meta_XWB; get_xsb_name(vgmstream->stream_name,STREAM_NAME_SIZE, target_stream, &xwb, streamFile); From 86ce73d1816b338bb8c2d430b997b145b3ac99db Mon Sep 17 00:00:00 2001 From: bnnm Date: Sun, 28 Jan 2018 00:47:06 +0100 Subject: [PATCH 19/21] Rename Wwise Vorbis enums with prefix for clarity --- src/coding/vorbis_custom_utils_wwise.c | 20 ++++++++-------- src/meta/wwise.c | 32 +++++++++++++------------- src/vgmstream.h | 6 ++--- 3 files changed, 29 insertions(+), 29 deletions(-) diff --git a/src/coding/vorbis_custom_utils_wwise.c b/src/coding/vorbis_custom_utils_wwise.c index 7fba9417..73ee1508 100644 --- a/src/coding/vorbis_custom_utils_wwise.c +++ b/src/coding/vorbis_custom_utils_wwise.c @@ -47,7 +47,7 @@ int vorbis_custom_setup_init_wwise(STREAMFILE *streamFile, off_t start_offset, v size_t header_size, packet_size; vorbis_custom_config cfg = data->config; - if (cfg.setup_type == HEADER_TRIAD) { + if (cfg.setup_type == WWV_HEADER_TRIAD) { /* read 3 Wwise packets with triad (id/comment/setup), each with a Wwise header */ off_t offset = start_offset; @@ -132,17 +132,17 @@ static int get_packet_header(STREAMFILE *streamFile, off_t offset, wwise_header_ /* packet size doesn't include header size */ switch(header_type) { - case TYPE_8: /* size 4+4 */ + case WWV_TYPE_8: /* size 4+4 */ *packet_size = (uint32_t)read_32bit(offset, streamFile); *granulepos = read_32bit(offset+4, streamFile); return 8; - case TYPE_6: /* size 4+2 */ + case WWV_TYPE_6: /* size 4+2 */ *packet_size = (uint16_t)read_16bit(offset, streamFile); *granulepos = read_32bit(offset+2, streamFile); return 6; - case TYPE_2: /* size 2 */ + case WWV_TYPE_2: /* size 2 */ *packet_size = (uint16_t)read_16bit(offset, streamFile); *granulepos = 0; /* granule is an arbitrary unit so we could use offset instead; libvorbis has no actually need it actually */ return 2; @@ -306,7 +306,7 @@ static int ww2ogg_generate_vorbis_packet(vgm_bitstream * ow, vgm_bitstream * iw, //VGM_ASSERT(granule < 0, "Wwise Vorbis: negative granule %i @ 0x%lx\n", granule, offset); - if (data->config.packet_type == MODIFIED) { + if (data->config.packet_type == WWV_MODIFIED) { /* rebuild first bits of packet type and window info (for the i-MDCT) */ uint32_t packet_type = 0, mode_number = 0, remainder = 0; @@ -423,13 +423,13 @@ static int ww2ogg_generate_vorbis_setup(vgm_bitstream * ow, vgm_bitstream * iw, w_bits(ow, 8, codebook_count_less1); codebook_count = codebook_count_less1 + 1; - if (data->config.setup_type == FULL_SETUP) { + if (data->config.setup_type == WWV_FULL_SETUP) { /* rebuild Wwise codebooks: untouched */ for (i = 0; i < codebook_count; i++) { if(!ww2ogg_codebook_library_copy(ow, iw)) goto fail; } } - else if (data->config.setup_type == INLINE_CODEBOOKS) { + else if (data->config.setup_type == WWV_INLINE_CODEBOOKS) { /* rebuild Wwise codebooks: inline in simplified format */ for (i = 0; i < codebook_count; i++) { if(!ww2ogg_codebook_library_rebuild(ow, iw, 0, streamFile)) goto fail; @@ -456,7 +456,7 @@ static int ww2ogg_generate_vorbis_setup(vgm_bitstream * ow, vgm_bitstream * iw, w_bits(ow, 16, dummy_time_value); - if (data->config.setup_type == FULL_SETUP) { + if (data->config.setup_type == WWV_FULL_SETUP) { /* rest of setup is untouched, copy bits */ uint32_t bitly = 0; uint32_t total_bits_read = iw->b_off; @@ -1174,11 +1174,11 @@ static int load_wvc_array(uint8_t * buf, size_t bufsize, uint32_t codebook_id, w const wvc_info * wvc_list; switch (setup_type) { - case EXTERNAL_CODEBOOKS: + case WWV_EXTERNAL_CODEBOOKS: wvc_list = wvc_list_standard; list_length = sizeof(wvc_list_standard) / sizeof(wvc_info); break; - case AOTUV603_CODEBOOKS: + case WWV_AOTUV603_CODEBOOKS: wvc_list = wvc_list_aotuv603; list_length = sizeof(wvc_list_standard) / sizeof(wvc_info); break; diff --git a/src/meta/wwise.c b/src/meta/wwise.c index e4619791..bf5e97bc 100644 --- a/src/meta/wwise.c +++ b/src/meta/wwise.c @@ -233,25 +233,25 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) { case 0x28: /* early (~2009), ex. The Lord of the Rings: Conquest PC */ data_offsets = 0x18; block_offsets = 0; /* no need, full headers are present */ - cfg.header_type = TYPE_8; - cfg.packet_type = STANDARD; - cfg.setup_type = HEADER_TRIAD; + cfg.header_type = WWV_TYPE_8; + cfg.packet_type = WWV_STANDARD; + cfg.setup_type = WWV_HEADER_TRIAD; break; //case 0x32: /* ? */ case 0x34: /* common (2010~2011) */ data_offsets = 0x18; block_offsets = 0x30; - cfg.header_type = TYPE_6; - cfg.packet_type = STANDARD; - cfg.setup_type = EXTERNAL_CODEBOOKS; /* setup_type will be corrected later */ + cfg.header_type = WWV_TYPE_6; + cfg.packet_type = WWV_STANDARD; + cfg.setup_type = WWV_EXTERNAL_CODEBOOKS; /* setup_type will be corrected later */ break; case 0x2a: /* uncommon (mid 2011), ex. infamous 2 PS3 */ data_offsets = 0x10; block_offsets = 0x28; - cfg.header_type = TYPE_2; - cfg.packet_type = MODIFIED; - cfg.setup_type = EXTERNAL_CODEBOOKS; + cfg.header_type = WWV_TYPE_2; + cfg.packet_type = WWV_MODIFIED; + cfg.setup_type = WWV_EXTERNAL_CODEBOOKS; break; default: VGM_LOG("WWISE: unknown vorb size 0x%x\n", vorb_size); @@ -277,11 +277,11 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) { /* if the setup after header starts with "(data)BCV" it's an inline codebook) */ if ((id & 0x00FFFFFF) == 0x00424356) { /* 0"BCV" */ - cfg.setup_type = FULL_SETUP; + cfg.setup_type = WWV_FULL_SETUP; } /* if the setup is suspiciously big it's probably trimmed inline codebooks */ else if (setup_size > 0x200) { /* an external setup it's ~0x100 max + some threshold */ - cfg.setup_type = INLINE_CODEBOOKS; + cfg.setup_type = WWV_INLINE_CODEBOOKS; } } @@ -297,13 +297,13 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) { case 0x30: data_offsets = 0x10; block_offsets = 0x28; - cfg.header_type = TYPE_2; - cfg.packet_type = MODIFIED; + cfg.header_type = WWV_TYPE_2; + cfg.packet_type = WWV_MODIFIED; /* setup not detectable by header, so we'll try both; hopefully libvorbis will reject wrong codebooks * - standard: early (<2012), ex. The King of Fighters XIII X360 (2011/11), .ogg (cbs are from aoTuV, too) * - aoTuV603: later (>2012), ex. Sonic & All-Stars Racing Transformed PC (2012/11), .wem */ - cfg.setup_type = is_wem ? AOTUV603_CODEBOOKS : EXTERNAL_CODEBOOKS; /* aoTuV came along .wem */ + cfg.setup_type = is_wem ? WWV_AOTUV603_CODEBOOKS : WWV_EXTERNAL_CODEBOOKS; /* aoTuV came along .wem */ break; //case 0x2a: /* Rocksmith 2011 X360? */ @@ -326,14 +326,14 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) { /* all blocksizes I've seen are 0x08+0x0B except Oddworld PSV, that uses 0x09+0x09 * (maybe lower spec machines = needs simpler packets) */ if (cfg.blocksize_0_exp == cfg.blocksize_1_exp) - cfg.packet_type = STANDARD; + cfg.packet_type = WWV_STANDARD; } /* try with the selected codebooks */ vgmstream->codec_data = init_vorbis_custom(streamFile, start_offset + setup_offset, VORBIS_WWISE, &cfg); if (!vgmstream->codec_data) { /* codebooks failed: try again with the other type */ - cfg.setup_type = is_wem ? EXTERNAL_CODEBOOKS : AOTUV603_CODEBOOKS; + cfg.setup_type = is_wem ? WWV_EXTERNAL_CODEBOOKS : WWV_AOTUV603_CODEBOOKS; vgmstream->codec_data = init_vorbis_custom(streamFile, start_offset + setup_offset, VORBIS_WWISE, &cfg); if (!vgmstream->codec_data) goto fail; } diff --git a/src/vgmstream.h b/src/vgmstream.h index ba647706..63c9d2bc 100644 --- a/src/vgmstream.h +++ b/src/vgmstream.h @@ -831,9 +831,9 @@ typedef enum { } vorbis_custom_t; /* config for Wwise Vorbis (3 types for flexibility though not all combinations exist) */ -typedef enum { HEADER_TRIAD, FULL_SETUP, INLINE_CODEBOOKS, EXTERNAL_CODEBOOKS, AOTUV603_CODEBOOKS } wwise_setup_t; /* Vorbis setup style */ -typedef enum { TYPE_8, TYPE_6, TYPE_2 } wwise_header_t; /* size of packet headers */ -typedef enum { STANDARD, MODIFIED } wwise_packet_t; /* type of Vorbis packets */ +typedef enum { WWV_HEADER_TRIAD, WWV_FULL_SETUP, WWV_INLINE_CODEBOOKS, WWV_EXTERNAL_CODEBOOKS, WWV_AOTUV603_CODEBOOKS } wwise_setup_t; +typedef enum { WWV_TYPE_8, WWV_TYPE_6, WWV_TYPE_2 } wwise_header_t; +typedef enum { WWV_STANDARD, WWV_MODIFIED } wwise_packet_t; typedef struct { /* to reconstruct init packets */ From 2da7cf06479f848550daa56e6b9edf04b2944ab5 Mon Sep 17 00:00:00 2001 From: bnnm Date: Sun, 28 Jan 2018 00:48:44 +0100 Subject: [PATCH 20/21] Add ADX keystring --- src/meta/adx_keys.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/meta/adx_keys.h b/src/meta/adx_keys.h index 4c047f89..9d5b750e 100644 --- a/src/meta/adx_keys.h +++ b/src/meta/adx_keys.h @@ -177,7 +177,7 @@ static const adxkey_info adxkey8_list[] = { {0x4133,0x5a01,0x5723, NULL,0}, // ? /* Shounen Onmyouji: Tsubasa yo Ima, Sora e Kaere [PS2] */ - {0x55d9,0x46d3,0x5b01, NULL,0}, // ? + {0x55d9,0x46d3,0x5b01, "SONMYOJI",0}, }; From 20ca3fc30d9c18ccfe88fe004870f0c138353b87 Mon Sep 17 00:00:00 2001 From: bnnm Date: Sun, 28 Jan 2018 00:48:57 +0100 Subject: [PATCH 21/21] Add HCA key --- src/meta/hca_keys.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/meta/hca_keys.h b/src/meta/hca_keys.h index fba74c2a..231307cd 100644 --- a/src/meta/hca_keys.h +++ b/src/meta/hca_keys.h @@ -225,6 +225,9 @@ static const hcakey_info hcakey_list[] = { // Kai-ri-Sei Million Arthur (Vita) {1782351729464341796}, // 18BC2F7463867524 + // Dx2 Shin Megami Tensei Liberation (iOS/Android) + {118714477}, // 000000000713706D + }; #endif/*_HCA_KEYS_H_*/