From 5df7a8f5c6f8c73b8a6fde4cce8c9bcb673e9a7f Mon Sep 17 00:00:00 2001 From: Christopher Snowhill Date: Sun, 11 Jun 2017 17:35:03 -0700 Subject: [PATCH] Updated VGMStream to r1050-532-gba4e6d1f. --- .../vgmstream.xcodeproj/project.pbxproj | 8 +- .../vgmstream/vgmstream/src/coding/coding.h | 1 + .../vgmstream/src/coding/mta2_decoder.c | 12 +- .../vgmstream/src/coding/pcm_decoder.c | 32 ++++ Frameworks/vgmstream/vgmstream/src/formats.c | 6 +- .../vgmstream/vgmstream/src/layout/blocked.c | 9 +- .../vgmstream/vgmstream/src/layout/layout.h | 2 + .../vgmstream/src/layout/rws_blocked.c | 21 ++ .../vgmstream/vgmstream/src/meta/meta.h | 2 + .../vgmstream/vgmstream/src/meta/ngc_ulw.c | 69 +++++++ .../vgmstream/vgmstream/src/meta/ps2_rws.c | 103 ---------- .../vgmstream/vgmstream/src/meta/riff.c | 2 +- Frameworks/vgmstream/vgmstream/src/meta/rws.c | 181 ++++++++++++++++++ Frameworks/vgmstream/vgmstream/src/meta/xau.c | 18 +- .../vgmstream/vgmstream/src/streamfile.c | 14 +- .../vgmstream/vgmstream/src/vgmstream.c | 13 +- .../vgmstream/vgmstream/src/vgmstream.h | 11 +- 17 files changed, 371 insertions(+), 133 deletions(-) create mode 100644 Frameworks/vgmstream/vgmstream/src/layout/rws_blocked.c create mode 100644 Frameworks/vgmstream/vgmstream/src/meta/ngc_ulw.c delete mode 100644 Frameworks/vgmstream/vgmstream/src/meta/ps2_rws.c create mode 100644 Frameworks/vgmstream/vgmstream/src/meta/rws.c diff --git a/Frameworks/vgmstream/vgmstream.xcodeproj/project.pbxproj b/Frameworks/vgmstream/vgmstream.xcodeproj/project.pbxproj index abc4a96f7..a79dee04f 100644 --- a/Frameworks/vgmstream/vgmstream.xcodeproj/project.pbxproj +++ b/Frameworks/vgmstream/vgmstream.xcodeproj/project.pbxproj @@ -245,7 +245,7 @@ 836F6FF118BDC2190095E648 /* ps2_psw.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EB518BDC2180095E648 /* ps2_psw.c */; }; 836F6FF218BDC2190095E648 /* ps2_rnd.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EB618BDC2180095E648 /* ps2_rnd.c */; }; 836F6FF318BDC2190095E648 /* ps2_rstm.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EB718BDC2180095E648 /* ps2_rstm.c */; }; - 836F6FF418BDC2190095E648 /* ps2_rws.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EB818BDC2180095E648 /* ps2_rws.c */; }; + 836F6FF418BDC2190095E648 /* rws.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EB818BDC2180095E648 /* rws.c */; }; 836F6FF618BDC2190095E648 /* ps2_sfs.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EBA18BDC2180095E648 /* ps2_sfs.c */; }; 836F6FF718BDC2190095E648 /* ps2_sl3.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EBB18BDC2180095E648 /* ps2_sl3.c */; }; 836F6FF818BDC2190095E648 /* ps2_smpl.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EBC18BDC2180095E648 /* ps2_smpl.c */; }; @@ -725,7 +725,7 @@ 836F6EB518BDC2180095E648 /* ps2_psw.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_psw.c; sourceTree = ""; }; 836F6EB618BDC2180095E648 /* ps2_rnd.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_rnd.c; sourceTree = ""; }; 836F6EB718BDC2180095E648 /* ps2_rstm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_rstm.c; sourceTree = ""; }; - 836F6EB818BDC2180095E648 /* ps2_rws.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_rws.c; sourceTree = ""; }; + 836F6EB818BDC2180095E648 /* rws.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = rws.c; sourceTree = ""; }; 836F6EBA18BDC2180095E648 /* ps2_sfs.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_sfs.c; sourceTree = ""; }; 836F6EBB18BDC2180095E648 /* ps2_sl3.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_sl3.c; sourceTree = ""; }; 836F6EBC18BDC2180095E648 /* ps2_smpl.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_smpl.c; sourceTree = ""; }; @@ -1264,7 +1264,7 @@ 836F6EB518BDC2180095E648 /* ps2_psw.c */, 836F6EB618BDC2180095E648 /* ps2_rnd.c */, 836F6EB718BDC2180095E648 /* ps2_rstm.c */, - 836F6EB818BDC2180095E648 /* ps2_rws.c */, + 836F6EB818BDC2180095E648 /* rws.c */, 836F6EBA18BDC2180095E648 /* ps2_sfs.c */, 836F6EBB18BDC2180095E648 /* ps2_sl3.c */, 836F6EBC18BDC2180095E648 /* ps2_smpl.c */, @@ -1811,7 +1811,7 @@ 83709E051ECBC1A4005C03D3 /* gtd.c in Sources */, 836F704A18BDC2190095E648 /* xbox_wavm.c in Sources */, 836F6F8618BDC2190095E648 /* excitebots.c in Sources */, - 836F6FF418BDC2190095E648 /* ps2_rws.c in Sources */, + 836F6FF418BDC2190095E648 /* rws.c in Sources */, 836F6F2518BDC2190095E648 /* g721_decoder.c in Sources */, 836F6FE818BDC2190095E648 /* ps2_mic.c in Sources */, 836F6F3C18BDC2190095E648 /* xa_decoder.c in Sources */, diff --git a/Frameworks/vgmstream/vgmstream/src/coding/coding.h b/Frameworks/vgmstream/vgmstream/src/coding/coding.h index da20e0ec0..b994be54c 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/coding.h +++ b/Frameworks/vgmstream/vgmstream/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/Frameworks/vgmstream/vgmstream/src/coding/mta2_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/mta2_decoder.c index 650bb8560..21c950cd6 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/mta2_decoder.c +++ b/Frameworks/vgmstream/vgmstream/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++) { diff --git a/Frameworks/vgmstream/vgmstream/src/coding/pcm_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/pcm_decoder.c index 3dc308c87..2342582f2 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/pcm_decoder.c +++ b/Frameworks/vgmstream/vgmstream/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/Frameworks/vgmstream/vgmstream/src/formats.c b/Frameworks/vgmstream/vgmstream/src/formats.c index c9c438be1..6a911ec02 100644 --- a/Frameworks/vgmstream/vgmstream/src/formats.c +++ b/Frameworks/vgmstream/vgmstream/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"}, @@ -515,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"}, @@ -614,7 +617,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)"}, @@ -866,6 +869,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/Frameworks/vgmstream/vgmstream/src/layout/blocked.c b/Frameworks/vgmstream/vgmstream/src/layout/blocked.c index 9b9e4b7a3..21587c8b9 100644 --- a/Frameworks/vgmstream/vgmstream/src/layout/blocked.c +++ b/Frameworks/vgmstream/vgmstream/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/Frameworks/vgmstream/vgmstream/src/layout/layout.h b/Frameworks/vgmstream/vgmstream/src/layout/layout.h index 4cb1df782..bb4cb5605 100644 --- a/Frameworks/vgmstream/vgmstream/src/layout/layout.h +++ b/Frameworks/vgmstream/vgmstream/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/Frameworks/vgmstream/vgmstream/src/layout/rws_blocked.c b/Frameworks/vgmstream/vgmstream/src/layout/rws_blocked.c new file mode 100644 index 000000000..23b90c535 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/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/Frameworks/vgmstream/vgmstream/src/meta/meta.h b/Frameworks/vgmstream/vgmstream/src/meta/meta.h index 1e2596343..83e879f92 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/meta.h +++ b/Frameworks/vgmstream/vgmstream/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/Frameworks/vgmstream/vgmstream/src/meta/ngc_ulw.c b/Frameworks/vgmstream/vgmstream/src/meta/ngc_ulw.c new file mode 100644 index 000000000..662bdcf57 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/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/Frameworks/vgmstream/vgmstream/src/meta/ps2_rws.c b/Frameworks/vgmstream/vgmstream/src/meta/ps2_rws.c deleted file mode 100644 index 976c5e449..000000000 --- a/Frameworks/vgmstream/vgmstream/src/meta/ps2_rws.c +++ /dev/null @@ -1,103 +0,0 @@ -#include "meta.h" -#include "../util.h" - -/* RWS (Silent Hill Origins, Ghost Rider, Max Payne 2) */ -VGMSTREAM * init_vgmstream_rws(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; - off_t start_offset; - - int loop_flag = 0; - int channel_count; - - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("rws",filename_extension(filename))) 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 - - loop_flag = 1; - channel_count = 2; - - /* 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; - - /* open the file for reading */ - { - int i; - STREAMFILE * file; - file = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); - if (!file) goto fail; - for (i=0;ich[i].streamfile = file; - - vgmstream->ch[i].channel_start_offset= - vgmstream->ch[i].offset=start_offset+ - vgmstream->interleave_block_size*i; - - } - } - - return vgmstream; - - /* clean up anything we may have opened */ -fail: - if (vgmstream) close_vgmstream(vgmstream); - return NULL; -} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/riff.c b/Frameworks/vgmstream/vgmstream/src/meta/riff.c index 3eafe01c8..2268525f8 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/riff.c +++ b/Frameworks/vgmstream/vgmstream/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 diff --git a/Frameworks/vgmstream/vgmstream/src/meta/rws.c b/Frameworks/vgmstream/vgmstream/src/meta/rws.c new file mode 100644 index 000000000..e33f23f0f --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/rws.c @@ -0,0 +1,181 @@ +#include "meta.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; + 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; + + + if (!check_extensions(streamFile,"rws")) + goto fail; + + /* 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; + + 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; + + vgmstream->meta_type = meta_RWS; + vgmstream->sample_rate = sample_rate; + vgmstream->num_streams = total_streams; + + vgmstream->layout_type = layout_rws_blocked; + vgmstream->current_block_size = block_size / vgmstream->channels; + vgmstream->full_block_size = block_size_max; + + 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; + +fail: + 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/Frameworks/vgmstream/vgmstream/src/meta/xau.c b/Frameworks/vgmstream/vgmstream/src/meta/xau.c index 06d80de63..8e12db044 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/xau.c +++ b/Frameworks/vgmstream/vgmstream/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; } diff --git a/Frameworks/vgmstream/vgmstream/src/streamfile.c b/Frameworks/vgmstream/vgmstream/src/streamfile.c index 26bada22d..861910a93 100644 --- a/Frameworks/vgmstream/vgmstream/src/streamfile.c +++ b/Frameworks/vgmstream/vgmstream/src/streamfile.c @@ -485,7 +485,8 @@ int check_extensions(STREAMFILE *streamFile, const char * cmp_exts) { char filename[PATH_LIMIT]; const char * ext = NULL; const char * cmp_ext = NULL; - int ext_len, cmp_len; + const char * ststr_res = NULL; + size_t ext_len, cmp_len; streamFile->get_name(streamFile,filename,sizeof(filename)); ext = filename_extension(filename); @@ -493,14 +494,15 @@ int check_extensions(STREAMFILE *streamFile, const char * cmp_exts) { cmp_ext = cmp_exts; do { - cmp_len = strstr(cmp_ext, ",") - cmp_ext; /* find next ext; becomes negative if not found */ - if (cmp_len < 0) - cmp_len = strlen(cmp_ext); /* total length if more not found */ + ststr_res = strstr(cmp_ext, ","); + cmp_len = ststr_res == NULL + ? strlen(cmp_ext) /* total length if more not found */ + : (intptr_t)ststr_res - (intptr_t)cmp_ext; /* find next ext; ststr_res should always be greater than cmp_ext, resulting in a positive cmp_len */ - if (strncasecmp(ext,cmp_ext, ext_len) == 0 && ext_len == cmp_len) + if (ext_len == cmp_len && strncasecmp(ext,cmp_ext, ext_len) == 0) return 1; - cmp_ext = strstr(cmp_ext, ","); + cmp_ext = ststr_res; if (cmp_ext != NULL) cmp_ext = cmp_ext + 1; /* skip comma */ diff --git a/Frameworks/vgmstream/vgmstream/src/vgmstream.c b/Frameworks/vgmstream/vgmstream/src/vgmstream.c index dfe32aa80..c0889b3df 100644 --- a/Frameworks/vgmstream/vgmstream/src/vgmstream.c +++ b/Frameworks/vgmstream/vgmstream/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, @@ -935,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: @@ -979,6 +981,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 +1139,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 +1360,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/Frameworks/vgmstream/vgmstream/src/vgmstream.h b/Frameworks/vgmstream/vgmstream/src/vgmstream.h index 24e6be3a6..54549f685 100644 --- a/Frameworks/vgmstream/vgmstream/src/vgmstream.h +++ b/Frameworks/vgmstream/vgmstream/src/vgmstream.h @@ -91,6 +91,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 */ @@ -242,6 +243,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 */ @@ -347,7 +349,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 */ @@ -625,6 +627,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 */ @@ -732,9 +735,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... */