Merge pull request #314 from bnnm/xpcm-nwav-xvag

xpcm nwav xvag
This commit is contained in:
Christopher Snowhill 2018-11-11 15:24:49 -08:00 committed by GitHub
commit 9e84e8cabc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 283 additions and 43 deletions

View File

@ -1088,9 +1088,13 @@ int clHCA_DecodeBlock(clHCA *hca, void *data, unsigned int size) {
} }
} }
/* should read all frame sans checksum at most */ /* should read all frame sans checksum (16b) at most */
if (br.bit > br.size - 16) /* one frame was found to read up to 14b left (cross referenced with CRI's tools),
* perhaps some encoding hiccup [World of Final Fantasy Maxima (Switch) am_ev21_0170 video],
* though this validation makes more sense when testing keys and isn't normally done on decode */
if (br.bit + 14 > br.size) { /* relax validation a bit for that case */
return HCA_ERROR_BITREADER; return HCA_ERROR_BITREADER;
}
return 0; return 0;
} }
@ -1159,7 +1163,7 @@ static int decode1_unpack_channel(stChannel *ch, clData *br,
if (delta != expected_delta) { if (delta != expected_delta) {
/* may happen with bad keycodes, scalefactors must be 6b indexes */ /* may happen with bad keycodes, scalefactors must be 6b indexes */
int scalefactor_test = (int)scalefactor_prev + ((int)delta - (int)extra_delta); int scalefactor_test = (int)scalefactor_prev + ((int)delta - (int)extra_delta);
if (scalefactor_test < 0 || scalefactor_test > 64) { if (scalefactor_test < 0 || scalefactor_test >= 64) {
return HCA_ERROR_UNPACK; return HCA_ERROR_UNPACK;
} }

View File

@ -0,0 +1,29 @@
#include "coding.h"
/* Circus XPCM mode 2 decoding, verified vs EF.exe (info from foo_adpcm/libpcm and https://github.com/lioncash/ExtractData) */
void decode_circus_adpcm(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) {
int i, sample_pos = 0;
int32_t hist = stream->adpcm_history1_32;
int scale = stream->adpcm_scale;
off_t frame_offset = stream->offset; /* frame size is 1 */
for (i = first_sample; i < first_sample + samples_to_do; i++) {
int8_t code = read_8bit(frame_offset+i,stream->streamfile);
hist += code << scale;
if (code == 0) {
if (scale > 0)
scale--;
}
else if (code == 127 || code == -128) {
if (scale < 8)
scale++;
}
outbuf[sample_pos] = hist;
}
stream->adpcm_history1_32 = hist;
stream->adpcm_scale = scale;
}

View File

@ -165,6 +165,8 @@ void decode_xmd(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing,
/* derf_decoder */ /* derf_decoder */
void decode_derf(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); void decode_derf(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
/* circus_decoder */
void decode_circus_adpcm(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
/* ea_mt_decoder*/ /* ea_mt_decoder*/
ea_mt_codec_data *init_ea_mt(int channels, int type); ea_mt_codec_data *init_ea_mt(int channels, int type);

View File

@ -262,6 +262,7 @@ static const char* extension_list[] = {
"npsf", //fake extension/header id for .nps (in bigfiles) "npsf", //fake extension/header id for .nps (in bigfiles)
"nus3bank", "nus3bank",
"nwa", "nwa",
"nwav",
"nxa", "nxa",
//"ogg", //common //"ogg", //common
@ -604,6 +605,7 @@ static const coding_info coding_info_list[] = {
{coding_DERF, "Xilam DERF 8-bit DPCM"}, {coding_DERF, "Xilam DERF 8-bit DPCM"},
{coding_ACM, "InterPlay ACM"}, {coding_ACM, "InterPlay ACM"},
{coding_NWA, "VisualArt's NWA DPCM"}, {coding_NWA, "VisualArt's NWA DPCM"},
{coding_CIRCUS_ADPCM, "Circus 8-bit ADPCM"},
{coding_EA_MT, "Electronic Arts MicroTalk"}, {coding_EA_MT, "Electronic Arts MicroTalk"},
@ -1107,6 +1109,8 @@ static const meta_info meta_info_list[] = {
{meta_VA3, "Konami VA3 header" }, {meta_VA3, "Konami VA3 header" },
{meta_XOPUS, "Exient XOPUS header"}, {meta_XOPUS, "Exient XOPUS header"},
{meta_VS_FFX, "Square VS header"}, {meta_VS_FFX, "Square VS header"},
{meta_NWAV, "Chunsoft NWAV header"},
{meta_XPCM, "Circus XPCM header"},
}; };

View File

@ -850,6 +850,10 @@
RelativePath=".\meta\nwa.c" RelativePath=".\meta\nwa.c"
> >
</File> </File>
<File
RelativePath=".\meta\nwav.c"
>
</File>
<File <File
RelativePath=".\meta\nxa.c" RelativePath=".\meta\nxa.c"
> >
@ -1613,6 +1617,10 @@
<File <File
RelativePath=".\meta\xopus.c" RelativePath=".\meta\xopus.c"
> >
</File>
<File
RelativePath=".\meta\xpcm.c"
>
</File> </File>
<File <File
RelativePath=".\meta\xss.c" RelativePath=".\meta\xss.c"
@ -1718,6 +1726,14 @@
RelativePath=".\coding\yamaha_decoder.c" RelativePath=".\coding\yamaha_decoder.c"
> >
</File> </File>
<File
RelativePath=".\coding\celt_fsb_decoder.c"
>
</File>
<File
RelativePath=".\coding\circus_decoder.c"
>
</File>
<File <File
RelativePath=".\coding\coding_utils.c" RelativePath=".\coding\coding_utils.c"
> >

View File

@ -126,6 +126,7 @@
<ClCompile Include="coding\at3plus_decoder.c" /> <ClCompile Include="coding\at3plus_decoder.c" />
<ClCompile Include="coding\atrac9_decoder.c" /> <ClCompile Include="coding\atrac9_decoder.c" />
<ClCompile Include="coding\celt_fsb_decoder.c" /> <ClCompile Include="coding\celt_fsb_decoder.c" />
<ClCompile Include="coding\circus_decoder.c" />
<ClCompile Include="coding\coding_utils.c" /> <ClCompile Include="coding\coding_utils.c" />
<ClCompile Include="coding\ffmpeg_decoder.c" /> <ClCompile Include="coding\ffmpeg_decoder.c" />
<ClCompile Include="coding\ffmpeg_decoder_custom_opus.c" /> <ClCompile Include="coding\ffmpeg_decoder_custom_opus.c" />
@ -319,6 +320,7 @@
<ClCompile Include="meta\nub_xma.c" /> <ClCompile Include="meta\nub_xma.c" />
<ClCompile Include="meta\nus3bank.c" /> <ClCompile Include="meta\nus3bank.c" />
<ClCompile Include="meta\nwa.c" /> <ClCompile Include="meta\nwa.c" />
<ClCompile Include="meta\nwav.c" />
<ClCompile Include="meta\nxa.c" /> <ClCompile Include="meta\nxa.c" />
<ClCompile Include="meta\nxap.c" /> <ClCompile Include="meta\nxap.c" />
<ClCompile Include="meta\ogg_vorbis.c" /> <ClCompile Include="meta\ogg_vorbis.c" />
@ -477,6 +479,7 @@
<ClCompile Include="meta\xma.c" /> <ClCompile Include="meta\xma.c" />
<ClCompile Include="meta\xnb.c" /> <ClCompile Include="meta\xnb.c" />
<ClCompile Include="meta\xopus.c" /> <ClCompile Include="meta\xopus.c" />
<ClCompile Include="meta\xpcm.c" />
<ClCompile Include="meta\xss.c" /> <ClCompile Include="meta\xss.c" />
<ClCompile Include="meta\xvag.c" /> <ClCompile Include="meta\xvag.c" />
<ClCompile Include="meta\xmd.c" /> <ClCompile Include="meta\xmd.c" />

View File

@ -523,6 +523,9 @@
<ClCompile Include="meta\nwa.c"> <ClCompile Include="meta\nwa.c">
<Filter>meta\Source Files</Filter> <Filter>meta\Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="meta\nwav.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\nxa.c"> <ClCompile Include="meta\nxa.c">
<Filter>meta\Source Files</Filter> <Filter>meta\Source Files</Filter>
</ClCompile> </ClCompile>
@ -1414,6 +1417,9 @@
<ClCompile Include="coding\celt_fsb_decoder.c"> <ClCompile Include="coding\celt_fsb_decoder.c">
<Filter>coding\Source Files</Filter> <Filter>coding\Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="coding\circus_decoder.c">
<Filter>coding\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\bcstm.c"> <ClCompile Include="meta\bcstm.c">
<Filter>meta\Source Files</Filter> <Filter>meta\Source Files</Filter>
</ClCompile> </ClCompile>
@ -1453,6 +1459,9 @@
<ClCompile Include="meta\xopus.c"> <ClCompile Include="meta\xopus.c">
<Filter>meta\Source Files</Filter> <Filter>meta\Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="meta\xpcm.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\x360_cxs.c"> <ClCompile Include="meta\x360_cxs.c">
<Filter>meta\Source Files</Filter> <Filter>meta\Source Files</Filter>
</ClCompile> </ClCompile>

View File

@ -1,7 +1,7 @@
#include "meta.h" #include "meta.h"
#include "../coding/coding.h" #include "../coding/coding.h"
typedef enum { PSX, PCM16, ATRAC9 } bnk_codec; typedef enum { PSX, PCM16, ATRAC9, HEVAG } bnk_codec;
/* BNK - Sony's Scream Tool bank format [Puyo Puyo Tetris (PS4), NekoBuro: Cats Block (Vita)] */ /* BNK - Sony's Scream Tool bank format [Puyo Puyo Tetris (PS4), NekoBuro: Cats Block (Vita)] */
VGMSTREAM * init_vgmstream_bnk_sony(STREAMFILE *streamFile) { VGMSTREAM * init_vgmstream_bnk_sony(STREAMFILE *streamFile) {
@ -313,7 +313,7 @@ VGMSTREAM * init_vgmstream_bnk_sony(STREAMFILE *streamFile) {
loop_length = read_32bit(start_offset+0x1c,streamFile); loop_length = read_32bit(start_offset+0x1c,streamFile);
loop_end = loop_start + loop_length; /* loop_start is -1 if not set */ loop_end = loop_start + loop_length; /* loop_start is -1 if not set */
codec = PSX; codec = HEVAG;
break; break;
default: default:
@ -381,6 +381,17 @@ VGMSTREAM * init_vgmstream_bnk_sony(STREAMFILE *streamFile) {
vgmstream->layout_type = layout_interleave; vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = interleave; vgmstream->interleave_block_size = interleave;
vgmstream->num_samples = ps_bytes_to_samples(stream_size,channel_count);
vgmstream->loop_start_sample = loop_start;
vgmstream->loop_end_sample = loop_end;
break;
case HEVAG:
vgmstream->sample_rate = 48000;
vgmstream->coding_type = coding_HEVAG;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = interleave;
vgmstream->num_samples = ps_bytes_to_samples(stream_size,channel_count); vgmstream->num_samples = ps_bytes_to_samples(stream_size,channel_count);
vgmstream->loop_start_sample = loop_start; vgmstream->loop_start_sample = loop_start;
vgmstream->loop_end_sample = loop_end; vgmstream->loop_end_sample = loop_end;

View File

@ -804,4 +804,8 @@ VGMSTREAM * init_vgmstream_vs_ffx(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_msf_banpresto_wmsf(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_msf_banpresto_wmsf(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_msf_banpresto_2msf(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_msf_banpresto_2msf(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_nwav(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_xpcm(STREAMFILE * streamFile);
#endif /*_META_H*/ #endif /*_META_H*/

57
src/meta/nwav.c Normal file
View File

@ -0,0 +1,57 @@
#include "meta.h"
#include "../coding/coding.h"
/* NWAV - from Chunsoft games [Fuurai no Shiren Gaiden: Onnakenshi Asuka Kenzan! (PC)] */
VGMSTREAM * init_vgmstream_nwav(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
off_t start_offset;
/* checks */
/* .nwav: header id (no filenames in bigfiles) */
if ( !check_extensions(streamFile,"nwav") )
goto fail;
if (read_32bitBE(0x00,streamFile) != 0x4E574156) /* "NWAV" */
goto fail;
#ifdef VGM_USE_VORBIS
{
ogg_vorbis_meta_info_t ovmi = {0};
int channels;
/* 0x04: version? */
/* 0x08: crc? */
ovmi.stream_size = read_32bitLE(0x0c, streamFile);
ovmi.loop_end = read_32bitLE(0x10, streamFile); /* num_samples, actually */
/* 0x14: sample rate */
/* 0x18: bps? (16) */
channels = read_8bit(0x19, streamFile);
start_offset = read_16bitLE(0x1a, streamFile);
ovmi.loop_flag = read_16bitLE(0x1c, streamFile) != 0; /* loop count? -1 = loops */
/* 0x1e: always 2? */
/* 0x20: always 1? */
ovmi.loop_start = read_32bitLE(0x24, streamFile);
/* 0x28: always 1? */
/* 0x2a: always 1? */
/* 0x2c: always null? */
ovmi.meta_type = meta_NWAV;
/* values are in resulting bytes */
ovmi.loop_start = ovmi.loop_start / sizeof(int16_t) / channels;
ovmi.loop_end = ovmi.loop_end / sizeof(int16_t) / channels;
vgmstream = init_vgmstream_ogg_vorbis_callbacks(streamFile, NULL, start_offset, &ovmi);
}
#else
goto fail;
#endif
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;
}

View File

@ -111,11 +111,6 @@ VGMSTREAM * init_vgmstream_sgxd(STREAMFILE *streamFile) {
if (name_offset) if (name_offset)
read_string(vgmstream->stream_name,STREAM_NAME_SIZE, name_offset,streamHeader); read_string(vgmstream->stream_name,STREAM_NAME_SIZE, name_offset,streamHeader);
/* needs -1 to match RIFF AT3's loop chunk
* (maybe SGXD = "loop before this sample" rather than "loop after this sample" as expected by vgmstream) */
if (vgmstream->loop_end_sample > 0)
vgmstream->loop_end_sample -= 1;
switch (type) { switch (type) {
case 0x03: /* PS-ADPCM [Genji (PS3), Ape Escape Move (PS3)]*/ case 0x03: /* PS-ADPCM [Genji (PS3), Ape Escape Move (PS3)]*/
vgmstream->coding_type = coding_PSX; vgmstream->coding_type = coding_PSX;

View File

@ -10,7 +10,7 @@ VGMSTREAM * init_vgmstream_sxd(STREAMFILE *streamFile) {
size_t chunk_size, stream_size = 0; size_t chunk_size, stream_size = 0;
int is_dual = 0, is_external = 0; int is_dual = 0, is_external = 0;
int loop_flag, channels, codec, location; int loop_flag, channels, codec, flags;
int sample_rate, num_samples, loop_start_sample, loop_end_sample; int sample_rate, num_samples, loop_start_sample, loop_end_sample;
uint32_t at9_config_data = 0; uint32_t at9_config_data = 0;
int total_subsongs, target_subsong = streamFile->stream_index; int total_subsongs, target_subsong = streamFile->stream_index;
@ -35,12 +35,15 @@ VGMSTREAM * init_vgmstream_sxd(STREAMFILE *streamFile) {
/* typical chunks: NAME, WAVE and many control chunks (SXDs don't need to contain any sound data) */ /* typical chunks: NAME, WAVE and many control chunks (SXDs don't need to contain any sound data) */
if (!find_chunk_le(streamHeader, 0x57415645,first_offset,0, &chunk_offset,&chunk_size)) goto fail; /* "WAVE" */ if (!find_chunk_le(streamHeader, 0x57415645,first_offset,0, &chunk_offset,&chunk_size)) /* "WAVE" */
goto fail;
/* check multi-streams (usually only in SFX containers) */ /* check multi-streams (usually only in SFX containers) */
total_subsongs = read_32bitLE(chunk_offset+0x04,streamHeader); total_subsongs = read_32bitLE(chunk_offset+0x04,streamHeader);
if (target_subsong == 0) target_subsong = 1; if (target_subsong == 0) target_subsong = 1;
if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail; if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail;
// todo rarely a WAVE subsong may point to a repeated data offset, with different tags only
/* read stream header */ /* read stream header */
{ {
@ -50,9 +53,10 @@ VGMSTREAM * init_vgmstream_sxd(STREAMFILE *streamFile) {
table_offset = chunk_offset + 0x08 + 4*(target_subsong-1); table_offset = chunk_offset + 0x08 + 4*(target_subsong-1);
header_offset = table_offset + read_32bitLE(table_offset,streamHeader); header_offset = table_offset + read_32bitLE(table_offset,streamHeader);
location = read_32bitLE(header_offset+0x00,streamHeader); flags = read_32bitLE(header_offset+0x00,streamHeader);
codec = read_8bit (header_offset+0x04,streamHeader); codec = read_8bit (header_offset+0x04,streamHeader);
channels = read_8bit (header_offset+0x05,streamHeader); channels = read_8bit (header_offset+0x05,streamHeader);
/* 0x06(2): unknown, rarely 0xFF */
sample_rate = read_32bitLE(header_offset+0x08,streamHeader); sample_rate = read_32bitLE(header_offset+0x08,streamHeader);
/* 0x0c(4): unknown size? (0x4000/0x3999/0x3333/etc, not related to extra data) */ /* 0x0c(4): unknown size? (0x4000/0x3999/0x3333/etc, not related to extra data) */
/* 0x10(4): ? + volume? + pan? (can be 0 for music) */ /* 0x10(4): ? + volume? + pan? (can be 0 for music) */
@ -62,14 +66,32 @@ VGMSTREAM * init_vgmstream_sxd(STREAMFILE *streamFile) {
stream_size = read_32bitLE(header_offset+0x20,streamHeader); stream_size = read_32bitLE(header_offset+0x20,streamHeader);
stream_offset = read_32bitLE(header_offset+0x24,streamHeader); stream_offset = read_32bitLE(header_offset+0x24,streamHeader);
/* Extra data, variable sized and uses some kind of TLVs (HEVAG's is optional and much smaller). loop_flag = loop_start_sample != -1 && loop_end_sample != -1;
* One tag seems to add a small part of the ATRAC9 data, for RAM preloding I guess. */
/* known flag combos:
* 0x00: Chaos Rings 2 sfx (RAM + no tags)
* 0x01: common (RAM + tags)
* 0x02: Chaos Rings 3 sfx (stream + no tags)
* 0x03: common (stream + tags)
* 0x05: Gravity Rush 2 sfx (RAM + tags) */
//has_tags = flags & 1;
is_external = flags & 2;
//unknown = flags & 4; /* no apparent differences with/without it? */
/* flag 1 signals TLV-like extra data. Format appears to be 0x00(1)=tag?, 0x01(1)=extra size*32b?, 0x02(2)=config?
* but not always (ex. size=0x07 but actually=0), perhaps only some bits are used or varies with tag, or are subflags.
* A tag may appear with or without extra data (even 0x0a), 0x01/03/04/06 are common at the beginnig (imply number of tags?),
* 0x64/7F are common at the end (but not always), 0x0A=ATRAC9 config, 0x0B/0C appear with RAM preloading data
* (most variable in Soul Sacrifice; total TLVs size isn't plainly found in the SXD header AFAIK). */
/* manually try to find ATRAC9 tag */
if (codec == 0x42) { if (codec == 0x42) {
off_t extra_offset = header_offset+0x28; off_t extra_offset = header_offset+0x28;
off_t max_offset = chunk_offset + chunk_size; off_t max_offset = chunk_offset + chunk_size;
/* manually try to find certain tag, no idea about the actual format if (!(flags & 1))
* (most variable in Soul Sacrifice; extra data size isn't found in the header AFAIK) */ goto fail;
while (extra_offset < max_offset) { while (extra_offset < max_offset) {
uint32_t tag = read_32bitBE(extra_offset, streamHeader); uint32_t tag = read_32bitBE(extra_offset, streamHeader);
if (tag == 0x0A010000 || tag == 0x0A010600) { if (tag == 0x0A010000 || tag == 0x0A010600) {
@ -83,26 +105,7 @@ VGMSTREAM * init_vgmstream_sxd(STREAMFILE *streamFile) {
goto fail; goto fail;
} }
loop_flag = loop_start_sample != -1 && loop_end_sample != -1; /* usually .sxd=header+data and .sxd1=header + .sxd2=data, but rarely sxd1 may contain data [The Last Guardian (PS4)] */
/* usually sxd=header+data and sxd1=header + sxd2=data, but rarely sxd1 contain data [The Last Guardian (PS4)] */
switch(location) { /* might not be exact but seems the only difference in TLG */
case 0x00: /* some Chaos Rings 2 sfx */
case 0x01: /* most common */
case 0x05: /* some Gradity Rush 2 sfx */
is_external = 0; /* RAM asset? */
break;
case 0x02: /* some Chaos Rings 3 sfx */
case 0x03: /* most common */
is_external = 1; /* stream asset? */
break;
default:
VGM_LOG("SXD: unknown location 0x%x\n", location);
goto fail;
}
if (is_external) { if (is_external) {
start_offset = stream_offset; /* absolute if external */ start_offset = stream_offset; /* absolute if external */
} else { } else {

View File

@ -184,7 +184,7 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) {
* We'll add basic support to avoid complaints of this or that .wem not playing */ * We'll add basic support to avoid complaints of this or that .wem not playing */
if (ww.data_size > ww.file_size) { if (ww.data_size > ww.file_size) {
//VGM_LOG("WWISE: truncated data size (prefetch): (real=0x%x > riff=0x%x)\n", ww.data_size, ww.file_size); //VGM_LOG("WWISE: truncated data size (prefetch): (real=0x%x > riff=0x%x)\n", ww.data_size, ww.file_size);
if (ww.codec == IMA || ww.codec == VORBIS) /* only seen those, probably others exist */ if (ww.codec == IMA || ww.codec == VORBIS || ww.codec == XMA2) /* only seen those, probably others exist */
ww.truncated = 1; ww.truncated = 1;
else else
goto fail; goto fail;
@ -228,8 +228,10 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) {
vgmstream->interleave_block_size = ww.block_align / ww.channels; vgmstream->interleave_block_size = ww.block_align / ww.channels;
vgmstream->codec_endian = ww.big_endian; vgmstream->codec_endian = ww.big_endian;
if (ww.truncated) /* enough to get real samples */ /* enough to get real samples */
if (ww.truncated) {
ww.data_size = ww.file_size - ww.data_offset; ww.data_size = ww.file_size - ww.data_offset;
}
vgmstream->num_samples = xbox_ima_bytes_to_samples(ww.data_size, ww.channels); vgmstream->num_samples = xbox_ima_bytes_to_samples(ww.data_size, ww.channels);
break; break;
@ -370,9 +372,10 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) {
start_offset += audio_offset; start_offset += audio_offset;
/* Vorbis is VBR so this is very approximate percent, meh */ /* Vorbis is VBR so this is very approximate percent, meh */
if (ww.truncated) if (ww.truncated) {
vgmstream->num_samples = (int32_t)(vgmstream->num_samples * vgmstream->num_samples = (int32_t)(vgmstream->num_samples *
(double)(ww.file_size - start_offset) / (double)ww.data_size); (double)(ww.file_size - start_offset) / (double)ww.data_size);
}
break; break;
} }
@ -443,6 +446,12 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) {
//VGM_ASSERT(find_chunk(streamFile, 0x584D4163,first_offset,0, NULL,NULL, ww.big_endian, 0), "WWISE: XMAc chunk found\n"); //VGM_ASSERT(find_chunk(streamFile, 0x584D4163,first_offset,0, NULL,NULL, ww.big_endian, 0), "WWISE: XMAc chunk found\n");
/* other chunks: "seek", regular XMA2 seek table */ /* other chunks: "seek", regular XMA2 seek table */
/* XMA is VBR so this is very approximate percent, meh */
if (ww.truncated) {
vgmstream->num_samples = (int32_t)(vgmstream->num_samples *
(double)(ww.file_size - start_offset) / (double)ww.data_size);
}
break; break;
} }

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

@ -0,0 +1,69 @@
#include "meta.h"
#include "../coding/coding.h"
/* XPCM - from Circus games [Eternal Fantasy (PC), D.C. White Season (PC)] */
VGMSTREAM * init_vgmstream_xpcm(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
off_t start_offset;
size_t decompressed_size;
int loop_flag, channel_count, codec, subcodec, sample_rate;
/* checks */
if (!check_extensions(streamFile, "pcm"))
goto fail;
if (read_32bitBE(0x00,streamFile) != 0x5850434D) /* "XPCM" "*/
goto fail;
decompressed_size = read_32bitLE(0x04,streamFile); /* (data_size for PCM) */
codec = read_8bit(0x08, streamFile);
subcodec = read_8bit(0x09, streamFile);
/* 0x0a: always null */
/* 0x0c: always 0x01 (PCM codec) */
channel_count = read_16bitLE(0x0e,streamFile);
sample_rate = read_32bitLE(0x10,streamFile);
/* 0x14: average bitrate */
/* 0x18: block size */
/* 0x1a: output bits (16) */
start_offset = 0x1c; /* compressed size in codec 0x01/03 */
loop_flag = 0;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count, loop_flag);
if (!vgmstream) goto fail;
vgmstream->meta_type = meta_XPCM;
vgmstream->sample_rate = sample_rate;
vgmstream->num_samples = decompressed_size / sizeof(int16_t) / channel_count;
switch(codec) {
case 0x00:
if (subcodec != 0) goto fail;
vgmstream->coding_type = coding_PCM16LE;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = 0x02;
break;
case 0x02:
if (subcodec != 0) goto fail;
vgmstream->coding_type = coding_CIRCUS_ADPCM;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = 0x01;
break;
case 0x01: /* LZSS + VQ */
case 0x03: /* unknown */
default:
goto fail;
}
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
goto fail;
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;
}

View File

@ -106,6 +106,15 @@ VGMSTREAM * init_vgmstream_xvag(STREAMFILE *streamFile) {
xvag.loop_end = ps_bytes_to_samples(file_size - start_offset, xvag.channels); xvag.loop_end = ps_bytes_to_samples(file_size - start_offset, xvag.channels);
} }
/* May use 'MP3 Surround' for multichannel [Twisted Metal (PS3), The Last of Us (PS4) test file]
* It's a mutant MP3 that decodes as 2ch but output can be routed to 6ch somehow, if manually
* activated. Fraunhofer IIS's MP3sPlayer can do it, as can PS3 (fw v2.40+) but no others seems to.
* So simply play as 2ch, they sound ok with slightly wider feel. No XVAG/MP3 flag exists to detect,
* can be found in v0x60 (without layers/subsongs) and v0x61 (with them set as 1) */
if (xvag.codec == 0x08 && xvag.channels == 6 && xvag.layers == 1) {
xvag.channels = 2;
}
/* build the VGMSTREAM */ /* build the VGMSTREAM */
vgmstream = allocate_vgmstream(xvag.channels,xvag.loop_flag); vgmstream = allocate_vgmstream(xvag.channels,xvag.loop_flag);

View File

@ -447,6 +447,8 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = {
init_vgmstream_vs_ffx, init_vgmstream_vs_ffx,
init_vgmstream_msf_banpresto_wmsf, init_vgmstream_msf_banpresto_wmsf,
init_vgmstream_msf_banpresto_2msf, init_vgmstream_msf_banpresto_2msf,
init_vgmstream_nwav,
init_vgmstream_xpcm,
/* lowest priority metas (should go after all metas, and TXTH should go before raw formats) */ /* lowest priority metas (should go after all metas, and TXTH should go before raw formats) */
init_vgmstream_txth, /* proper parsers should supersede TXTH, once added */ init_vgmstream_txth, /* proper parsers should supersede TXTH, once added */
@ -1122,6 +1124,7 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) {
case coding_DERF: case coding_DERF:
case coding_NWA: case coding_NWA:
case coding_SASSC: case coding_SASSC:
case coding_CIRCUS_ADPCM:
return 1; return 1;
case coding_IMA: case coding_IMA:
@ -1297,6 +1300,7 @@ int get_vgmstream_frame_size(VGMSTREAM * vgmstream) {
case coding_DERF: case coding_DERF:
case coding_NWA: case coding_NWA:
case coding_SASSC: case coding_SASSC:
case coding_CIRCUS_ADPCM:
return 0x01; return 0x01;
case coding_IMA: case coding_IMA:
@ -1741,6 +1745,12 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to
vgmstream->channels,vgmstream->samples_into_block,samples_to_do); vgmstream->channels,vgmstream->samples_into_block,samples_to_do);
} }
break; break;
case coding_CIRCUS_ADPCM:
for (ch = 0; ch < vgmstream->channels; ch++) {
decode_circus_adpcm(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch,
vgmstream->channels,vgmstream->samples_into_block,samples_to_do);
}
break;
case coding_IMA: case coding_IMA:
case coding_IMA_int: case coding_IMA_int:

View File

@ -159,6 +159,7 @@ typedef enum {
coding_DERF, /* DERF 8-bit DPCM */ coding_DERF, /* DERF 8-bit DPCM */
coding_ACM, /* InterPlay ACM */ coding_ACM, /* InterPlay ACM */
coding_NWA, /* VisualArt's NWA */ coding_NWA, /* VisualArt's NWA */
coding_CIRCUS_ADPCM, /* Circus 8-bit ADPCM */
coding_EA_MT, /* Electronic Arts MicroTalk (linear-predictive speech codec) */ coding_EA_MT, /* Electronic Arts MicroTalk (linear-predictive speech codec) */
@ -702,6 +703,8 @@ typedef enum {
meta_VA3, /* DDR Supernova 2 AC */ meta_VA3, /* DDR Supernova 2 AC */
meta_XOPUS, meta_XOPUS,
meta_VS_FFX, meta_VS_FFX,
meta_NWAV,
meta_XPCM,
} meta_t; } meta_t;

View File

@ -1450,6 +1450,7 @@ winamp_tags last_tags;
* Winamp requests one tag at a time and may reask for the same tag several times */ * Winamp requests one tag at a time and may reask for the same tag several times */
static void load_tagfile_info(in_char* filename) { static void load_tagfile_info(in_char* filename) {
STREAMFILE *tagFile = NULL; STREAMFILE *tagFile = NULL;
in_char filename_clean[PATH_LIMIT];
char filename_utf8[PATH_LIMIT]; char filename_utf8[PATH_LIMIT];
char tagfile_path_utf8[PATH_LIMIT]; char tagfile_path_utf8[PATH_LIMIT];
in_char tagfile_path_i[PATH_LIMIT]; in_char tagfile_path_i[PATH_LIMIT];
@ -1461,13 +1462,15 @@ static void load_tagfile_info(in_char* filename) {
return; return;
} }
/* clean extra part for subsong tags */
parse_fn_string(filename, NULL, filename_clean,PATH_LIMIT);
if (wa_strcmp(last_tags.filename, filename) == 0) { if (wa_strcmp(last_tags.filename, filename_clean) == 0) {
return; /* not changed, tags still apply */ return; /* not changed, tags still apply */
} }
/* tags are now for this filename, find tagfile path */ /* tags are now for this filename, find tagfile path */
wa_ichar_to_char(filename_utf8, PATH_LIMIT, filename); wa_ichar_to_char(filename_utf8, PATH_LIMIT, filename_clean);
strcpy(tagfile_path_utf8,filename_utf8); strcpy(tagfile_path_utf8,filename_utf8);
path = strrchr(tagfile_path_utf8,'\\'); path = strrchr(tagfile_path_utf8,'\\');
@ -1480,7 +1483,7 @@ static void load_tagfile_info(in_char* filename) {
} }
wa_char_to_ichar(tagfile_path_i, PATH_LIMIT, tagfile_path_utf8); wa_char_to_ichar(tagfile_path_i, PATH_LIMIT, tagfile_path_utf8);
wa_strcpy(last_tags.filename, filename); wa_strcpy(last_tags.filename, filename_clean);
last_tags.tag_count = 0; last_tags.tag_count = 0;
/* load all tags from tagfile */ /* load all tags from tagfile */