Merge pull request #98 from bnnm/ulw-xau2-rws

ULW, XAU 2, RWS
This commit is contained in:
Christopher Snowhill 2017-06-11 16:31:44 -07:00 committed by GitHub
commit ba4e6d1fa2
21 changed files with 392 additions and 134 deletions

View File

@ -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*

View File

@ -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);

View File

@ -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

View File

@ -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 */

View File

@ -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++) {

View File

@ -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; i<first_sample+samples_to_do; i++,sample_count+=channelspacing) {
uint8_t ulawbyte = read_8bit(stream->offset+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);
}

View File

@ -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"},

View File

@ -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;
}

View File

@ -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);

21
src/layout/rws_blocked.c Normal file
View File

@ -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;i<vgmstream->channels;i++) {
vgmstream->ch[i].offset = block_offset + interleave*i;
}
}

View File

@ -594,6 +594,10 @@
RelativePath=".\meta\ngc_ymf.c"
>
</File>
<File
RelativePath=".\meta\ngc_ulw.c"
>
</File>
<File
RelativePath=".\meta\ngca.c"
>
@ -830,10 +834,6 @@
RelativePath=".\meta\ps2_rstm.c"
>
</File>
<File
RelativePath=".\meta\ps2_rws.c"
>
</File>
<File
RelativePath=".\meta\ps2_rxws.c"
>
@ -1026,6 +1026,10 @@
RelativePath=".\meta\rsf.c"
>
</File>
<File
RelativePath=".\meta\rws.c"
>
</File>
<File
RelativePath=".\meta\rwsd.c"
>
@ -1542,6 +1546,10 @@
RelativePath=".\layout\psx_mgav_blocked.c"
>
</File>
<File
RelativePath=".\layout\rws_blocked.c"
>
</File>
<File
RelativePath=".\layout\scd_int_layout.c"
>

View File

@ -265,6 +265,7 @@
<ClCompile Include="meta\ngc_ssm.c" />
<ClCompile Include="meta\ngc_tydsp.c" />
<ClCompile Include="meta\ngc_ymf.c" />
<ClCompile Include="meta\ngc_ulw.c" />
<ClCompile Include="meta\nwa.c" />
<ClCompile Include="meta\ogg_vorbis_file.c" />
<ClCompile Include="meta\ogl.c" />
@ -315,7 +316,6 @@
<ClCompile Include="meta\ps2_psw.c" />
<ClCompile Include="meta\ps2_rnd.c" />
<ClCompile Include="meta\ps2_rstm.c" />
<ClCompile Include="meta\ps2_rws.c" />
<ClCompile Include="meta\ps2_rxws.c" />
<ClCompile Include="meta\ps2_sfs.c" />
<ClCompile Include="meta\ps2_sl3.c" />
@ -356,6 +356,7 @@
<ClCompile Include="meta\rs03.c" />
<ClCompile Include="meta\rsd.c" />
<ClCompile Include="meta\rsf.c" />
<ClCompile Include="meta\rws.c" />
<ClCompile Include="meta\rwsd.c" />
<ClCompile Include="meta\rwx.c" />
<ClCompile Include="meta\s14_sss.c" />
@ -457,6 +458,7 @@
<ClCompile Include="layout\nolayout.c" />
<ClCompile Include="layout\ps2_adm_blocked.c" />
<ClCompile Include="layout\psx_mgav_blocked.c" />
<ClCompile Include="layout\rws_blocked.c" />
<ClCompile Include="layout\str_snds_blocked.c" />
<ClCompile Include="layout\thp_blocked.c" />
<ClCompile Include="layout\vs_blocked.c" />

View File

@ -343,6 +343,9 @@
<ClCompile Include="meta\ngc_ymf.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\ngc_ulw.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\nwa.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
@ -493,9 +496,6 @@
<ClCompile Include="meta\ps2_rstm.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\ps2_rws.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\ps2_rxws.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
@ -616,6 +616,9 @@
<ClCompile Include="meta\rsf.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\rws.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\rwsd.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
@ -913,6 +916,9 @@
<ClCompile Include="layout\psx_mgav_blocked.c">
<Filter>layout\Source Files</Filter>
</ClCompile>
<ClCompile Include="layout\rws_blocked.c">
<Filter>layout\Source Files</Filter>
</ClCompile>
<ClCompile Include="layout\str_snds_blocked.c">
<Filter>layout\Source Files</Filter>
</ClCompile>

View File

@ -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*/

69
src/meta/ngc_ulw.c Normal file
View File

@ -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;
}

View File

@ -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;i<channel_count;i++) {
vgmstream->ch[i].streamfile = file;
vgmstream->ch[i].channel_start_offset=
vgmstream->ch[i].offset=start_offset+
vgmstream->interleave_block_size*i;
}
}
return vgmstream;
/* clean up anything we may have opened */
fail:
if (vgmstream) close_vgmstream(vgmstream);
return NULL;
}

View File

@ -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

181
src/meta/rws.c Normal file
View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;chan<vgmstream->channels;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;chan<vgmstream->channels;chan++) {
decode_nds_ima(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan,

View File

@ -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 */
@ -234,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 */
@ -339,7 +341,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 */
@ -617,6 +619,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 */
@ -724,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... */