From 19e26fa4fbccbf9bdb07f37ad61e1da179bef5b1 Mon Sep 17 00:00:00 2001 From: bnnm Date: Fri, 9 Jun 2017 21:44:39 +0200 Subject: [PATCH 1/7] Minor tweaks --- BUILD.md | 6 +++--- readme.txt | 5 +++++ 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/BUILD.md b/BUILD.md index 1a07d664..fa792abd 100644 --- a/BUILD.md +++ b/BUILD.md @@ -53,8 +53,8 @@ Open *./vgmstream_full.sln* as a base, which expects the above dependencies. The - For *foo_input_vgmstream* add *../../WTL/Include* to the compilers's *additional includes* - For *foo_input_vgmstream* add *../../foobar/foobar2000/shared/shared.lib* to the linker's *additional dependencies* -VS2013 may not be compatible with the SDK in release mode due to compiler bugs. -FDK-AAC/QAAC can be disabled by removing *VGM_USE_MP4V2* and *VGM_USE_FDKAAC* in the compiler/linker options and the project dependencies. +VS2013/VS2015/VS2017 may not be compatible with the SDK in release mode when some options are enabled due to compiler bugs. +FDK-AAC/QAAC can be safely disabled by removing *VGM_USE_MP4V2* and *VGM_USE_FDKAAC* in the compiler/linker options and the project dependencies, MAIATRAC3 too, as FFmpeg is used instead to support their codecs. You can also use the command line to compile with MSBuild, if you don't want to touch the .vcxproj files, register VS2015 after trial, or only have VC++/MSBuild tools. @@ -167,6 +167,6 @@ For new simple formats, assuming existing layout/coding: - *src/vgmstream.h*: register new meta - *src/vgmstream.c*: add parser init to the init list - *src/formats.c*: add new extension to the format list, add meta description -- *fb2k/foo_vgmstream.cpp*: add new extension to the file register list (optional) +- *fb2k/foo_filetypes.h*: add new extension to the file register list (optional) - *src/libvgmstream.vcproj/vcxproj/filters*: add to compile new (format-name).c parser in VS - if the format needs an external library don't forget to mark optional parts with: *#ifdef VGM_USE_X ... #endif* diff --git a/readme.txt b/readme.txt index 62ed32a7..f1c08c4b 100644 --- a/readme.txt +++ b/readme.txt @@ -57,6 +57,10 @@ the above instructions for installing the other files needed. Drop the xmp-vgmstream.dll in your XMPlay plugins directory. Please follow the above instructions for installing the other files needed. +Because the XMPlay MP3 decoder incorrectly tries to play some vgmstream exts, +you need to manually fix it by going to options > plugins > input > vgmstream +and in the "priority filetypes" put: ckd,fsb,genh,msf,rak,scd,xvag + --- foo_input_vgmstream --- Every should be installed automatically by the .fb2k-component bundle. @@ -127,6 +131,7 @@ PS2/PSX ADPCM: - .wp2 - .xa2 - .xa30 +- .xwb+xwh GC/Wii/3DS DSP ADPCM: - .aaap From 818dfd026e1d334cb39d3f54637588f096bc27f7 Mon Sep 17 00:00:00 2001 From: bnnm Date: Fri, 9 Jun 2017 21:45:15 +0200 Subject: [PATCH 2/7] Fix some XBOX XAU with more chunks --- src/meta/xau.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/meta/xau.c b/src/meta/xau.c index 06d80de6..8e12db04 100644 --- a/src/meta/xau.c +++ b/src/meta/xau.c @@ -37,7 +37,7 @@ VGMSTREAM * init_vgmstream_xau(STREAMFILE *streamFile) { /* miniheader over a common header with some tweaks, so we'll simplify parsing */ switch(type) { case 0x50533200: /* "PS2\0" */ - if (read_32bitBE(0x40,streamFile) != 0x56414770) goto fail; /* "VAGp" */ + if (read_32bitBE(0x40,streamFile) != 0x56414770) goto fail; /* mutant "VAGp" (long header size) */ start_offset = 0x800; vgmstream->sample_rate = read_32bitBE(0x50, streamFile); @@ -48,22 +48,24 @@ VGMSTREAM * init_vgmstream_xau(STREAMFILE *streamFile) { vgmstream->coding_type = coding_PSX; vgmstream->layout_type = layout_interleave; vgmstream->interleave_block_size = 0x8000; - break; - case 0x58420000: /* "XB\0\0" */ - if (read_32bitBE(0x40,streamFile) != 0x52494646) goto fail; /* "RIFF" */ - start_offset = 0x70; + case 0x58420000: /* "XB\0\0" */ + if (read_32bitBE(0x40,streamFile) != 0x52494646) goto fail; /* mutant "RIFF" (sometimes wrong RIFF size) */ + + /* start offset: find "data" chunk, as sometimes there is a "smpl" chunk at the start or the end (same as loop_start/end) */ + if (!find_chunk_le(streamFile, 0x64617461, 0x4c, 0, &start_offset, NULL) ) + goto fail; + vgmstream->sample_rate = read_32bitLE(0x58, streamFile); - vgmstream->num_samples = ms_ima_bytes_to_samples(read_32bitLE(0x6c, streamFile), read_16bitLE(0x60, streamFile), channel_count); + vgmstream->num_samples = ms_ima_bytes_to_samples(read_32bitLE(start_offset-4, streamFile), read_16bitLE(0x60, streamFile), channel_count); vgmstream->loop_start_sample = loop_start; vgmstream->loop_end_sample = loop_end; - /* there is also a "smpl" chunk at the end, same as loop_start/end */ vgmstream->coding_type = coding_XBOX; vgmstream->layout_type = layout_none; - break; + default: goto fail; } From 7e42eeca584447d5a67de48662032dc258143fb1 Mon Sep 17 00:00:00 2001 From: bnnm Date: Fri, 9 Jun 2017 22:26:09 +0200 Subject: [PATCH 3/7] Add u-Law decoder + ULW meta [Burnout 1 GC] --- fb2k/foo_filetypes.h | 1 + src/coding/coding.h | 1 + src/coding/pcm_decoder.c | 32 +++++++++++++++ src/formats.c | 3 ++ src/libvgmstream.vcproj | 4 ++ src/libvgmstream.vcxproj | 1 + src/libvgmstream.vcxproj.filters | 3 ++ src/meta/meta.h | 2 + src/meta/ngc_ulw.c | 69 ++++++++++++++++++++++++++++++++ src/vgmstream.c | 10 +++++ src/vgmstream.h | 2 + 11 files changed, 128 insertions(+) create mode 100644 src/meta/ngc_ulw.c diff --git a/fb2k/foo_filetypes.h b/fb2k/foo_filetypes.h index 2af141d0..13a8d731 100644 --- a/fb2k/foo_filetypes.h +++ b/fb2k/foo_filetypes.h @@ -291,6 +291,7 @@ DECLARE_MULTIPLE_FILE_TYPE("TRA Audio File (*.TRA)", tra); DECLARE_MULTIPLE_FILE_TYPE("TUN Audio File (*.TUN)", tun); DECLARE_MULTIPLE_FILE_TYPE("TYDSP Audio File (*.TYDSP)", tydsp); +DECLARE_MULTIPLE_FILE_TYPE("ULW Audio File (*.ULW)", ulw); DECLARE_MULTIPLE_FILE_TYPE("UM3 Audio File (*.UM3)", um3); DECLARE_MULTIPLE_FILE_TYPE("VAG Audio File (*.VAG)", vag); diff --git a/src/coding/coding.h b/src/coding/coding.h index da20e0ec..b994be54 100644 --- a/src/coding/coding.h +++ b/src/coding/coding.h @@ -57,6 +57,7 @@ void decode_pcm8_int(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspac void decode_pcm8_sb_int(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); void decode_pcm8_unsigned_int(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); void decode_pcm8_unsigned(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); +void decode_ulaw(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); size_t pcm_bytes_to_samples(size_t bytes, int channels, int bits_per_sample); /* psx_decoder */ diff --git a/src/coding/pcm_decoder.c b/src/coding/pcm_decoder.c index 3dc308c8..2342582f 100644 --- a/src/coding/pcm_decoder.c +++ b/src/coding/pcm_decoder.c @@ -86,6 +86,38 @@ void decode_pcm16LE_XOR_int(VGMSTREAMCHANNEL * stream, sample * outbuf, int chan } } +/* decodes u-law (ITU G.711 non-linear PCM) */ +void decode_ulaw(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) { + int i; + int32_t sample_count; + int sign, segment, quantization, sample; + const int bias = 0x84; + + + for (i=first_sample,sample_count=0; ioffset+i,stream->streamfile); + + ulawbyte = ~ulawbyte; /* stored in complement */ + sign = (ulawbyte & 0x80); + segment = (ulawbyte & 0x70) >> 4; /* exponent */ + quantization = ulawbyte & 0x0F; /* mantissa */ + + sample = (quantization << 3) + bias; /* add bias */ + sample <<= segment; + sample = (sign) ? (bias - sample) : (sample - bias); /* remove bias */ + +#if 0 // the above follows Sun's implementation, but this works too + { + static int exp_lut[8] = {0,132,396,924,1980,4092,8316,16764}; /* precalcs from bias */ + sample = exp_lut[segment] + (quantization << (segment + 3)); + if (sign != 0) sample = -sample; + } +#endif + + outbuf[sample_count] = sample; + } +} + size_t pcm_bytes_to_samples(size_t bytes, int channels, int bits_per_sample) { return bytes / channels / (bits_per_sample/8); } diff --git a/src/formats.c b/src/formats.c index c9c438be..9f23317d 100644 --- a/src/formats.c +++ b/src/formats.c @@ -283,6 +283,7 @@ static const char* extension_list[] = { "tun", "tydsp", + "ulw", "um3", "vag", @@ -394,6 +395,7 @@ static const coding_info coding_info_list[] = { {coding_PCM8_U_int, "8-bit unsigned PCM with 1 byte interleave"}, {coding_PCM8_int, "8-bit PCM with 1 byte interleave"}, {coding_PCM8_SB_int, "8-bit PCM with sign bit, 1 byte interleave"}, + {coding_ULAW, "8-bit u-Law"}, {coding_CRI_ADX, "CRI ADX 4-bit ADPCM"}, {coding_CRI_ADX_exp, "CRI ADX 4-bit ADPCM with exponential scale"}, {coding_CRI_ADX_fixed, "CRI ADX 4-bit ADPCM with fixed coefficients"}, @@ -866,6 +868,7 @@ static const meta_info meta_info_list[] = { {meta_TA_AAC_X360, "tri-Ace AAC (X360) header"}, {meta_TA_AAC_PS3, "tri-Ace AAC (PS3) header"}, {meta_PS3_MTA2, "Konami MTA2 header"}, + {meta_NGC_ULW, "Criterion ULW raw header"}, #ifdef VGM_USE_VORBIS {meta_OGG_VORBIS, "Ogg Vorbis"}, diff --git a/src/libvgmstream.vcproj b/src/libvgmstream.vcproj index f017f036..51f20ba3 100644 --- a/src/libvgmstream.vcproj +++ b/src/libvgmstream.vcproj @@ -594,6 +594,10 @@ RelativePath=".\meta\ngc_ymf.c" > + + diff --git a/src/libvgmstream.vcxproj b/src/libvgmstream.vcxproj index f101e173..e7826f5c 100644 --- a/src/libvgmstream.vcxproj +++ b/src/libvgmstream.vcxproj @@ -265,6 +265,7 @@ + diff --git a/src/libvgmstream.vcxproj.filters b/src/libvgmstream.vcxproj.filters index e6bc4e8c..fc3a9741 100644 --- a/src/libvgmstream.vcxproj.filters +++ b/src/libvgmstream.vcxproj.filters @@ -343,6 +343,9 @@ meta\Source Files + + meta\Source Files + meta\Source Files diff --git a/src/meta/meta.h b/src/meta/meta.h index 1e259634..83e879f9 100644 --- a/src/meta/meta.h +++ b/src/meta/meta.h @@ -672,4 +672,6 @@ VGMSTREAM * init_vgmstream_ta_aac_ps3(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_ps3_mta2(STREAMFILE *streamFile); +VGMSTREAM * init_vgmstream_ngc_ulw(STREAMFILE * streamFile); + #endif /*_META_H*/ diff --git a/src/meta/ngc_ulw.c b/src/meta/ngc_ulw.c new file mode 100644 index 00000000..662bdcf5 --- /dev/null +++ b/src/meta/ngc_ulw.c @@ -0,0 +1,69 @@ +#include "meta.h" +#include "../coding/coding.h" + +/* ULW - headerless U-law, found in Burnout (GC) */ +VGMSTREAM * init_vgmstream_ngc_ulw(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + off_t start_offset; + int loop_flag = 0, channel_count; + + + /* check extension, case insensitive */ + if ( !check_extensions(streamFile,"ulw")) + goto fail; + + /* raw data, the info is in the filename (really!) */ + { + char* path; + char basename[PATH_LIMIT]; + char filename[PATH_LIMIT]; + + /* get base name */ + streamFile->get_name(streamFile,filename,sizeof(filename)); + path = strrchr(filename,DIR_SEPARATOR); + if (path!=NULL) + path = path+1; + else + path = filename; + strcpy(basename,path); + + /* first letter gives the channels */ + if (basename[0]=='M') /* Mono */ + channel_count = 1; + else if (basename[0]=='S' || basename[0]=='D') /* Stereo/Dolby */ + channel_count = 2; + else + goto fail; + + /* not very robust but meh (other tracks don't loop) */ + if (strcmp(basename,"MMenu.ulw")==0 || strcmp(basename,"DMenu.ulw")==0) { + loop_flag = 1; + } + } + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count,loop_flag); + if (!vgmstream) goto fail; + + vgmstream->sample_rate = 32000; + vgmstream->coding_type = coding_ULAW; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x01; + vgmstream->meta_type = meta_NGC_ULW; + vgmstream->num_samples = pcm_bytes_to_samples(get_streamfile_size(streamFile), channel_count, 8); + if (loop_flag) { + vgmstream->loop_start_sample = 0; + vgmstream->loop_end_sample = vgmstream->num_samples; + } + + start_offset = 0; + + 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 dfe32aa8..fb4455f2 100644 --- a/src/vgmstream.c +++ b/src/vgmstream.c @@ -361,6 +361,7 @@ VGMSTREAM * (*init_vgmstream_fcns[])(STREAMFILE *streamFile) = { init_vgmstream_ta_aac_x360, init_vgmstream_ta_aac_ps3, init_vgmstream_ps3_mta2, + init_vgmstream_ngc_ulw, #ifdef VGM_USE_FFMPEG init_vgmstream_mp4_aac_ffmpeg, @@ -979,6 +980,7 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) { case coding_PCM8_int: case coding_PCM8_SB_int: case coding_PCM8_U_int: + case coding_ULAW: #ifdef VGM_USE_VORBIS case coding_ogg_vorbis: case coding_fsb_vorbis: @@ -1136,6 +1138,7 @@ int get_vgmstream_frame_size(VGMSTREAM * vgmstream) { case coding_PCM8_int: case coding_PCM8_SB_int: case coding_PCM8_U_int: + case coding_ULAW: case coding_SDX2: case coding_SDX2_int: case coding_CBD2: @@ -1356,6 +1359,13 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to samples_to_do); } break; + case coding_ULAW: + for (chan=0;chanchannels;chan++) { + decode_ulaw(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, + vgmstream->channels,vgmstream->samples_into_block, + samples_to_do); + } + break; case coding_NDS_IMA: for (chan=0;chanchannels;chan++) { decode_nds_ima(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, diff --git a/src/vgmstream.h b/src/vgmstream.h index 70187a4f..1e0e0af4 100644 --- a/src/vgmstream.h +++ b/src/vgmstream.h @@ -83,6 +83,7 @@ typedef enum { coding_PCM8_U, /* 8-bit PCM, unsigned (0x80 = 0) */ coding_PCM8_U_int, /* 8-bit PCM, unsigned (0x80 = 0) with sample-level interleave */ coding_PCM8_SB_int, /* 8-bit PCM, sign bit (others are 2's complement) with sample-level interleave */ + coding_ULAW, /* 8-bit u-Law (non-linear PCM) */ /* 4-bit ADPCM */ coding_CRI_ADX, /* CRI ADX */ @@ -617,6 +618,7 @@ typedef enum { meta_TA_AAC_X360, /* tri-ace AAC (Star Ocean 4, End of Eternity, Infinite Undiscovery) */ meta_TA_AAC_PS3, /* tri-ace AAC (Star Ocean International, Resonance of Fate) */ meta_PS3_MTA2, /* Metal Gear Solid 4 MTA2 */ + meta_NGC_ULW, /* Burnout 1 (GC only) */ #ifdef VGM_USE_VORBIS meta_OGG_VORBIS, /* Ogg Vorbis */ From f513d53999d3450ebb504aaddae839347f1c72dd Mon Sep 17 00:00:00 2001 From: bnnm Date: Fri, 9 Jun 2017 22:31:33 +0200 Subject: [PATCH 4/7] Rename ps2_rws.c to rws.c and do other rws cleanup --- src/formats.c | 2 +- src/libvgmstream.vcproj | 8 ++++---- src/libvgmstream.vcxproj | 2 +- src/libvgmstream.vcxproj.filters | 6 +++--- src/meta/{ps2_rws.c => rws.c} | 2 +- src/vgmstream.h | 2 +- 6 files changed, 11 insertions(+), 11 deletions(-) rename src/meta/{ps2_rws.c => rws.c} (97%) diff --git a/src/formats.c b/src/formats.c index 9f23317d..ce3b8045 100644 --- a/src/formats.c +++ b/src/formats.c @@ -616,7 +616,7 @@ static const meta_info meta_info_list[] = { {meta_XSS, "Dino Crisis 3 XSS File"}, {meta_HGC1, "Knights of the Temple 2 hgC1 Header"}, {meta_AUS, "Capcom AUS Header"}, - {meta_RWS, "RWS Header"}, + {meta_RWS, "RenderWare RWS header"}, {meta_EACS_PC, "EACS Header (PC)"}, {meta_EACS_PSX, "EACS Header (PSX)"}, {meta_EACS_SAT, "EACS Header (SATURN)"}, diff --git a/src/libvgmstream.vcproj b/src/libvgmstream.vcproj index 51f20ba3..1fe138f6 100644 --- a/src/libvgmstream.vcproj +++ b/src/libvgmstream.vcproj @@ -834,10 +834,6 @@ RelativePath=".\meta\ps2_rstm.c" > - - @@ -1030,6 +1026,10 @@ RelativePath=".\meta\rsf.c" > + + diff --git a/src/libvgmstream.vcxproj b/src/libvgmstream.vcxproj index e7826f5c..b343f9bd 100644 --- a/src/libvgmstream.vcxproj +++ b/src/libvgmstream.vcxproj @@ -316,7 +316,6 @@ - @@ -357,6 +356,7 @@ + diff --git a/src/libvgmstream.vcxproj.filters b/src/libvgmstream.vcxproj.filters index fc3a9741..567b934a 100644 --- a/src/libvgmstream.vcxproj.filters +++ b/src/libvgmstream.vcxproj.filters @@ -496,9 +496,6 @@ meta\Source Files - - meta\Source Files - meta\Source Files @@ -619,6 +616,9 @@ meta\Source Files + + meta\Source Files + meta\Source Files diff --git a/src/meta/ps2_rws.c b/src/meta/rws.c similarity index 97% rename from src/meta/ps2_rws.c rename to src/meta/rws.c index 976c5e44..4ac32b60 100644 --- a/src/meta/ps2_rws.c +++ b/src/meta/rws.c @@ -1,7 +1,7 @@ #include "meta.h" #include "../util.h" -/* RWS (Silent Hill Origins, Ghost Rider, Max Payne 2) */ +/* RWS - RenderWare Stream (from games using RenderWare Audio middleware) */ VGMSTREAM * init_vgmstream_rws(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; char filename[PATH_LIMIT]; diff --git a/src/vgmstream.h b/src/vgmstream.h index 1e0e0af4..c617656b 100644 --- a/src/vgmstream.h +++ b/src/vgmstream.h @@ -340,7 +340,7 @@ typedef enum { meta_SL3, /* Test Drive Unlimited */ meta_HGC1, /* Knights of the Temple 2 */ meta_AUS, /* Various Capcom games */ - meta_RWS, /* Various Konami games */ + meta_RWS, /* RenderWare games (only when using RW Audio middleware) */ meta_FSB1, /* FMOD Sample Bank, version 1 */ meta_FSB2, /* FMOD Sample Bank, version 2 */ meta_FSB3, /* FMOD Sample Bank, version 3.0/3.1 */ From d7e3f6915d0cb85de727d2f78caced860affeba9 Mon Sep 17 00:00:00 2001 From: bnnm Date: Fri, 9 Jun 2017 23:54:14 +0200 Subject: [PATCH 5/7] Add renamed .RWS at3 [Climax PSP games: Silent Hill Origins, Oblivion] --- src/meta/riff.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/meta/riff.c b/src/meta/riff.c index 3eafe01c..2268525f 100644 --- a/src/meta/riff.c +++ b/src/meta/riff.c @@ -253,7 +253,7 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { else if (!strcasecmp("sns",filename_extension(filename))) sns = 1; #if defined(VGM_USE_MAIATRAC3PLUS) || defined(VGM_USE_FFMPEG) - else if (!strcasecmp("at3",filename_extension(filename))) + else if ( check_extensions(streamFile, "at3,rws") ) /* Renamed .RWS AT3 found in Climax games (Silent Hill Origins PSP, Oblivion PSP) */ at3 = 1; #endif else From 519c5d71de304c6c03909f20cde04575abb99e0d Mon Sep 17 00:00:00 2001 From: bnnm Date: Sat, 10 Jun 2017 02:25:49 +0200 Subject: [PATCH 6/7] Redo RWS with PCM/PS-ADPCM/DSP/IMA-ADPCM + block support [many games] --- src/formats.c | 1 + src/layout/blocked.c | 9 +- src/layout/layout.h | 2 + src/layout/rws_blocked.c | 21 +++ src/libvgmstream.vcproj | 4 + src/libvgmstream.vcxproj | 1 + src/libvgmstream.vcxproj.filters | 3 + src/meta/rws.c | 238 ++++++++++++++++++++----------- src/vgmstream.c | 3 +- src/vgmstream.h | 7 +- 10 files changed, 203 insertions(+), 86 deletions(-) create mode 100644 src/layout/rws_blocked.c diff --git a/src/formats.c b/src/formats.c index ce3b8045..6a911ec0 100644 --- a/src/formats.c +++ b/src/formats.c @@ -517,6 +517,7 @@ static const layout_info layout_info_list[] = { {layout_ivaud_blocked, "GTA IV blocked"}, {layout_ps2_iab_blocked, "IAB blocked"}, {layout_ps2_strlr_blocked, "The Bouncer STR blocked"}, + {layout_rws_blocked, "RWS blocked"}, {layout_tra_blocked, "TRA blocked"}, {layout_acm, "ACM blocked"}, {layout_mus_acm, "multiple ACM files, ACM blocked"}, diff --git a/src/layout/blocked.c b/src/layout/blocked.c index 9b9e4b7a..21587c8b 100644 --- a/src/layout/blocked.c +++ b/src/layout/blocked.c @@ -127,9 +127,12 @@ void render_vgmstream_blocked(sample * buffer, int32_t sample_count, VGMSTREAM * case layout_ps2_iab_blocked: ps2_iab_block_update(vgmstream->next_block_offset,vgmstream); break; - case layout_ps2_strlr_blocked: - ps2_strlr_block_update(vgmstream->next_block_offset,vgmstream); - break; + case layout_ps2_strlr_blocked: + ps2_strlr_block_update(vgmstream->next_block_offset,vgmstream); + break; + case layout_rws_blocked: + rws_block_update(vgmstream->next_block_offset,vgmstream); + break; default: break; } diff --git a/src/layout/layout.h b/src/layout/layout.h index 4cb1df78..bb4cb560 100644 --- a/src/layout/layout.h +++ b/src/layout/layout.h @@ -59,6 +59,8 @@ void ps2_iab_block_update(off_t block_offset, VGMSTREAM * vgmstream); void ps2_strlr_block_update(off_t block_offset, VGMSTREAM * vgmstream); +void rws_block_update(off_t block_offset, VGMSTREAM * vgmstream); + /* other layouts */ void render_vgmstream_interleave(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstream); diff --git a/src/layout/rws_blocked.c b/src/layout/rws_blocked.c new file mode 100644 index 00000000..23b90c53 --- /dev/null +++ b/src/layout/rws_blocked.c @@ -0,0 +1,21 @@ +#include "layout.h" +#include "../vgmstream.h" + +/* a simple headerless block with padding; configured in the main header */ +void rws_block_update(off_t block_offset, VGMSTREAM * vgmstream) { + int i; + size_t block_size; + size_t interleave; + + /* no header; size is configured in the main header */ + block_size = vgmstream->full_block_size; + interleave = vgmstream->interleave_block_size; + + vgmstream->current_block_offset = block_offset; + vgmstream->next_block_offset = block_offset + + block_size; + + for (i=0;ichannels;i++) { + vgmstream->ch[i].offset = block_offset + interleave*i; + } +} diff --git a/src/libvgmstream.vcproj b/src/libvgmstream.vcproj index 1fe138f6..b31ccbdc 100644 --- a/src/libvgmstream.vcproj +++ b/src/libvgmstream.vcproj @@ -1546,6 +1546,10 @@ RelativePath=".\layout\psx_mgav_blocked.c" > + + diff --git a/src/libvgmstream.vcxproj b/src/libvgmstream.vcxproj index b343f9bd..a1ae76ce 100644 --- a/src/libvgmstream.vcxproj +++ b/src/libvgmstream.vcxproj @@ -458,6 +458,7 @@ + diff --git a/src/libvgmstream.vcxproj.filters b/src/libvgmstream.vcxproj.filters index 567b934a..cf25b539 100644 --- a/src/libvgmstream.vcxproj.filters +++ b/src/libvgmstream.vcxproj.filters @@ -916,6 +916,9 @@ layout\Source Files + + layout\Source Files + layout\Source Files diff --git a/src/meta/rws.c b/src/meta/rws.c index 4ac32b60..e33f23f0 100644 --- a/src/meta/rws.c +++ b/src/meta/rws.c @@ -1,103 +1,181 @@ #include "meta.h" -#include "../util.h" +#include "../coding/coding.h" +#include "../layout/layout.h" + + +static off_t get_rws_string_size(off_t off, STREAMFILE *streamFile); + /* RWS - RenderWare Stream (from games using RenderWare Audio middleware) */ VGMSTREAM * init_vgmstream_rws(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; - off_t start_offset; + off_t start_offset, off, coefs_offset = 0, stream_offset = 0; + int loop_flag = 0, channel_count, codec; + size_t file_size, header_size, data_size, stream_size = 0, info_size; + int block_size_max = 0, block_size = 0, sample_rate; + int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL; + int i, total_segments, total_streams, target_stream = 0; - int loop_flag = 0; - int channel_count; - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("rws",filename_extension(filename))) goto fail; + if (!check_extensions(streamFile,"rws")) + goto fail; - /* check header */ - if (read_32bitBE(0x00,streamFile) != 0x0D080000) - goto fail; -#if 0 - /* check if is used as container file */ - if (read_32bitBE(0x38,streamFile) != 0x01000000) - goto fail; -#endif + /* check chunks (always LE) */ + /* Made of a file chunk with header and data chunks (other chunks exist for non-audio .RWS). + * A chunk is: id, size, RW version (no real diffs), data of size (version is repeated but same for all chunks). + * Version: 16b main + 16b build (can vary between files) ex: 0c02, 1003, 1400 = 3.5, 1803 = 3.6, 1C02 = 3.7. */ + if (read_32bitLE(0x00,streamFile) != 0x0000080d) /* audio file chunk id */ + goto fail; + file_size = read_32bitLE(0x04,streamFile); /* audio file chunk size */ + if (file_size + 0x0c != get_streamfile_size(streamFile)) goto fail; - loop_flag = 1; - channel_count = 2; - - /* build the VGMSTREAM */ + if (read_32bitLE(0x0c,streamFile) != 0x0000080e) /* header chunk id */ + goto fail; + header_size = read_32bitLE(0x10,streamFile); /* header chunk size */ + + off = 0x0c + 0x0c + header_size; + if (read_32bitLE(off+0x00,streamFile) != 0x0000080f) /* data chunk id */ + goto fail; + data_size = read_32bitLE(off+0x04,streamFile); /* data chunk size */ + if (data_size+0x0c + off != get_streamfile_size(streamFile)) + goto fail; + + /* inside header chunk (many unknown fields are probably IDs/config, as two same-sized files vary a lot) */ + off = 0x0c + 0x0c; + + /* 0x00: actual header size (less than chunk size), useful to check endianness (Wii/X360 = BE) */ + read_32bit = (read_32bitLE(off+0x00,streamFile) > header_size) ? read_32bitBE : read_32bitLE; + + /* 0x04-14: sizes of various sections?, others: ? */ + total_segments = read_32bit(off+0x20,streamFile); + total_streams = read_32bit(off+0x28,streamFile); + /* 0x2c: unk, 0x30: 0x800?, 0x34: max block size?, 0x38: data offset, 0x3c: 0?, 0x40-50: file uuid? */ + off += 0x50 + get_rws_string_size(off+0x50, streamFile); /* skip audio file name */ + + /* check streams/segments */ + /* Data can be divided into segments (cues/divisions within data, ex. intro+main, voice1+2+..N) or + * tracks/streams in interleaved blocks that can contain padding and don't need to match between tracks + * (ex 0x1800 data + 0 pad of stream_0 2ch, 0x1800 data + 0x200 pad of stream1 2ch, etc). */ + if (target_stream == 0) target_stream = 1; + if (target_stream < 0 || target_stream > total_streams || total_streams < 1) goto fail; + + /* skip segment stuff and get stream size (from sizes for all segments, repeated per track) */ + off += 0x20 * total_segments; /* segment data (mostly unknown except @ 0x18: full data size, 0x1c: offset) */ + for (i = 0; i < total_segments; i++) { /* sum usable segment sizes (no padding) */ + stream_size += read_32bit(off + 0x04 * i + total_segments*(target_stream-1),streamFile); + } + off += 0x04 * (total_segments * total_streams); + off += 0x10 * total_segments; /* segment uuids? */ + for (i = 0; i < total_segments; i++) { /* skip segments names */ + off += get_rws_string_size(off, streamFile); + } + + /* get stream layout: 0xc: samples per frame (ex. 28 in VAG), 0x24: offset within data chunk, others: ? */ + /* get block_size for our target stream and from all streams, to skip their blocks during decode */ + for (i = 0; i < total_streams; i++) { /* get block_sizes */ + block_size_max += read_32bit(off+0x10 + 0x28*i,streamFile); /* includes padding and can be different per stream */ + if (target_stream-1 == i) { + block_size = read_32bit(off+0x20 + 0x28*i,streamFile); /* actual size */ + stream_offset = read_32bit(off+0x24 + 0x28*i,streamFile); /* within data */ + } + } + off += 0x28 * total_streams; + + /* get stream config: 0x0c(1): bits per sample, others: ? */ + info_size = total_streams > 1 ? 0x30 : 0x2c; //todo this doesn't look right + sample_rate = read_32bit(off+0x00 + info_size*(target_stream-1),streamFile); + //unk_size = read_32bit(off+0x08 + info_size*(target_stream-1),streamFile); /* segment size? loop-related? */ + channel_count = read_8bit(off+0x0d + info_size*(target_stream-1),streamFile); + codec = read_32bitBE(off+0x1c + info_size*(target_stream-1),streamFile); /* uuid of 128b but the first is enough */ + off += info_size * total_streams; + + + /* if codec is DSP there is an extra field */ + if (codec == 0xF86215B0) { + /* 0x00: approx num samples? 0x04: approx size? */ + coefs_offset = off + 0x1c; + } + + /* next is 0x14 * streams = ?(4) + uuid? (header ends), rest is garbage/padding until chunk end (may contain strings and weird stuff) */ + + start_offset = 0x0c + 0x0c + header_size + 0x0c + stream_offset; /* usually 0x800 but not always */ + + /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; - /* fill in the vital statistics */ - start_offset = read_32bitLE(0x50,streamFile); - vgmstream->channels = channel_count; - - - switch (read_32bitLE(0x38,streamFile)) { - case 0x01: - vgmstream->sample_rate = read_32bitLE(0xE4,streamFile); - vgmstream->num_samples = read_32bitLE(0x98,streamFile)/16*28/vgmstream->channels; - if (loop_flag) { - vgmstream->loop_start_sample = 0; - vgmstream->loop_end_sample = read_32bitLE(0x98,streamFile)/16*28/vgmstream->channels; - } - break; - case 0x02: - if (start_offset < 0x800) // Max Payne 2 - { - vgmstream->sample_rate = read_32bitLE(0x178,streamFile); - vgmstream->num_samples = read_32bitLE(0x150,streamFile)/16*28/vgmstream->channels; - if (loop_flag) - { - vgmstream->loop_start_sample = 0; - vgmstream->loop_end_sample = read_32bitLE(0x150,streamFile)/16*28/vgmstream->channels; - } - } - else // Nana (2005)(Konami) - { - vgmstream->sample_rate = read_32bitLE(0x128,streamFile); - vgmstream->num_samples = read_32bitLE(0x7F8,streamFile)/16*28/vgmstream->channels; - if (loop_flag) - { - vgmstream->loop_start_sample = 0; - vgmstream->loop_end_sample = read_32bitLE(0x7F8,streamFile)/16*28/vgmstream->channels; - } - } - break; - default: - goto fail; -} - - -vgmstream->coding_type = coding_PSX; - - - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = read_32bitLE(0x4C,streamFile)/2; vgmstream->meta_type = meta_RWS; + vgmstream->sample_rate = sample_rate; + vgmstream->num_streams = total_streams; - /* open the file for reading */ - { - int i; - STREAMFILE * file; - file = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); - if (!file) goto fail; - for (i=0;ich[i].streamfile = file; + vgmstream->layout_type = layout_rws_blocked; + vgmstream->current_block_size = block_size / vgmstream->channels; + vgmstream->full_block_size = block_size_max; - vgmstream->ch[i].channel_start_offset= - vgmstream->ch[i].offset=start_offset+ - vgmstream->interleave_block_size*i; + switch(codec) { + case 0xD01BD217: /* PCM X360 (D01BD217 35874EED B9D9B8E8 6EA9B995) */ + /* The Legend of Spyro (X360) */ + vgmstream->coding_type = coding_PCM16BE; + //vgmstream->interleave_block_size = block_size / 2; //0x2; //todo 2ch PCM not working correctly (interleaved PCM not ok?) - } + vgmstream->num_samples = pcm_bytes_to_samples(stream_size, channel_count, 16); + break; + + case 0x9897EAD9: /* PS-ADPCM PS2 (9897EAD9 BCBB7B44 96B26547 59102E16) */ + /* ex. Silent Hill Origins (PS2), Ghost Rider (PS2), Max Payne 2 (PS2), Nana (PS2) */ + vgmstream->coding_type = coding_PSX; + vgmstream->interleave_block_size = block_size / 2; + + vgmstream->num_samples = ps_bytes_to_samples(stream_size, channel_count); + break; + + case 0xF86215B0: /* DSP Wii (F86215B0 31D54C29 BD37CDBF 9BD10C53) */ + /* ex. Alice in Wonderland (Wii) */ + vgmstream->coding_type = coding_NGC_DSP; + vgmstream->interleave_block_size = block_size / 2; + + /* get coefs (all channels share them so 0 spacing; also seem fixed for all RWS) */ + dsp_read_coefs_be(vgmstream,streamFile,coefs_offset, 0); + + vgmstream->num_samples = dsp_bytes_to_samples(stream_size, channel_count); + break; + + case 0x936538EF: /* MS-IMA PC (936538EF 11B62D43 957FA71A DE44227A) */ + case 0x2BA22F63: /* MS-IMA Xbox (2BA22F63 DD118F45 AA27A5C3 46E9790E) */ + /* ex. Broken Sword 3 (PC), Jacked (PC/Xbox), Burnout 2 (Xbox) */ + vgmstream->coding_type = coding_XBOX; + vgmstream->interleave_block_size = 0; /* uses regular XBOX/MS-IMA interleave */ + + vgmstream->num_samples = ms_ima_bytes_to_samples(stream_size, 0x48, channel_count); + break; + + default: + VGM_LOG("RSW: unknown codec 0x%08x\n", codec); + goto fail; } + + if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + goto fail; + + rws_block_update(start_offset, vgmstream); /* block init */ + return vgmstream; - /* clean up anything we may have opened */ fail: - if (vgmstream) close_vgmstream(vgmstream); + close_vgmstream(vgmstream); return NULL; } + + +/* rws-strings are null-terminated then padded to 0x10 (weirdly the padding contains garbage) */ +static off_t get_rws_string_size(off_t off, STREAMFILE *streamFile) { + int i; + for (i = 0; i < 0x800; i++) { /* 0x800=arbitrary max */ + if (read_8bit(off+i,streamFile) == 0) { /* null terminator */ + return i + (0x10 - (i % 0x10)); /* size is padded */ + } + } + + return 0; +} diff --git a/src/vgmstream.c b/src/vgmstream.c index fb4455f2..c0889b3d 100644 --- a/src/vgmstream.c +++ b/src/vgmstream.c @@ -936,7 +936,8 @@ void render_vgmstream(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstre case layout_dsp_bdsp_blocked: case layout_tra_blocked: case layout_ps2_iab_blocked: - case layout_ps2_strlr_blocked: + case layout_ps2_strlr_blocked: + case layout_rws_blocked: render_vgmstream_blocked(buffer,sample_count,vgmstream); break; case layout_interleave_byte: diff --git a/src/vgmstream.h b/src/vgmstream.h index c617656b..d57f3bff 100644 --- a/src/vgmstream.h +++ b/src/vgmstream.h @@ -235,6 +235,7 @@ typedef enum { layout_tra_blocked, /* DefJam Rapstar .tra blocks */ layout_ps2_iab_blocked, layout_ps2_strlr_blocked, + layout_rws_blocked, /* otherwise odd */ layout_acm, /* libacm layout */ @@ -726,9 +727,11 @@ typedef struct { size_t interleave_smallblock_size; /* smaller interleave for last block */ /* headered blocks */ off_t current_block_offset; /* start of this block (offset of block header) */ - size_t current_block_size; /* size of the block we're in now */ + size_t current_block_size; /* size of the block we're in now (usable data) */ + size_t full_block_size; /* size including padding and other unusable data */ off_t next_block_offset; /* offset of header of the next block */ - int block_count; /* count of "semi" block in total block */ + int block_count; /* count of "semi" block in total block */ + /* loop layout (saved values) */ int32_t loop_sample; /* saved from current_sample, should be loop_start_sample... */ From 3ca2ac1ee8e30109afd9db48c51f2592b4ea6ccf Mon Sep 17 00:00:00 2001 From: bnnm Date: Sun, 11 Jun 2017 22:38:05 +0200 Subject: [PATCH 7/7] Fix segfault when reading MTA2 files after EOF (ex. trunkated) --- src/coding/mta2_decoder.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/coding/mta2_decoder.c b/src/coding/mta2_decoder.c index 650bb856..21c950cd 100644 --- a/src/coding/mta2_decoder.c +++ b/src/coding/mta2_decoder.c @@ -97,11 +97,12 @@ static void mta2_block_update(VGMSTREAMCHANNEL * stream) { void decode_mta2(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { int samples_done = 0, sample_count = 0, channel_block_samples, channel_first_sample, frame_size = 0; int i, group, row, col; - int num_track = 0, channel_layout, track_channels = 0, track_channel; + int track_channels = 0, track_channel; /* block/track skip */ do { + int num_track = 0, channel_layout; /* autodetect and skip macroblock header */ mta2_block_update(stream); @@ -112,8 +113,13 @@ void decode_mta2(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, frame_size = read_16bitBE(stream->offset+0x06,stream->streamfile); /* not including this header */ /* 0x08(8): null */ - if (num_track < 0) - break; /* EOF: whatever */ + /* EOF: 0-fill buffer (or, as track_channels = 0 > divs by 0) */ + if (num_track < 0) { + for (i = 0; i < samples_to_do; i++) + outbuf[i * channelspacing] = 0; + return; + } + track_channels = 0; for (i = 0; i < 8; i++) {