mirror of
https://github.com/vgmstream/vgmstream.git
synced 2024-11-12 17:50:50 +01:00
commit
9e84e8cabc
@ -1088,9 +1088,13 @@ int clHCA_DecodeBlock(clHCA *hca, void *data, unsigned int size) {
|
||||
}
|
||||
}
|
||||
|
||||
/* should read all frame sans checksum at most */
|
||||
if (br.bit > br.size - 16)
|
||||
/* should read all frame sans checksum (16b) at most */
|
||||
/* 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 0;
|
||||
}
|
||||
@ -1159,7 +1163,7 @@ static int decode1_unpack_channel(stChannel *ch, clData *br,
|
||||
if (delta != expected_delta) {
|
||||
/* may happen with bad keycodes, scalefactors must be 6b indexes */
|
||||
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;
|
||||
}
|
||||
|
||||
|
29
src/coding/circus_decoder.c
Normal file
29
src/coding/circus_decoder.c
Normal 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;
|
||||
}
|
@ -165,6 +165,8 @@ void decode_xmd(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing,
|
||||
/* derf_decoder */
|
||||
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_codec_data *init_ea_mt(int channels, int type);
|
||||
|
@ -262,6 +262,7 @@ static const char* extension_list[] = {
|
||||
"npsf", //fake extension/header id for .nps (in bigfiles)
|
||||
"nus3bank",
|
||||
"nwa",
|
||||
"nwav",
|
||||
"nxa",
|
||||
|
||||
//"ogg", //common
|
||||
@ -604,6 +605,7 @@ static const coding_info coding_info_list[] = {
|
||||
{coding_DERF, "Xilam DERF 8-bit DPCM"},
|
||||
{coding_ACM, "InterPlay ACM"},
|
||||
{coding_NWA, "VisualArt's NWA DPCM"},
|
||||
{coding_CIRCUS_ADPCM, "Circus 8-bit ADPCM"},
|
||||
|
||||
{coding_EA_MT, "Electronic Arts MicroTalk"},
|
||||
|
||||
@ -1107,6 +1109,8 @@ static const meta_info meta_info_list[] = {
|
||||
{meta_VA3, "Konami VA3 header" },
|
||||
{meta_XOPUS, "Exient XOPUS header"},
|
||||
{meta_VS_FFX, "Square VS header"},
|
||||
{meta_NWAV, "Chunsoft NWAV header"},
|
||||
{meta_XPCM, "Circus XPCM header"},
|
||||
|
||||
};
|
||||
|
||||
|
@ -850,6 +850,10 @@
|
||||
RelativePath=".\meta\nwa.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\meta\nwav.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\meta\nxa.c"
|
||||
>
|
||||
@ -1613,6 +1617,10 @@
|
||||
<File
|
||||
RelativePath=".\meta\xopus.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\meta\xpcm.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\meta\xss.c"
|
||||
@ -1718,6 +1726,14 @@
|
||||
RelativePath=".\coding\yamaha_decoder.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\coding\celt_fsb_decoder.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\coding\circus_decoder.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\coding\coding_utils.c"
|
||||
>
|
||||
|
@ -126,6 +126,7 @@
|
||||
<ClCompile Include="coding\at3plus_decoder.c" />
|
||||
<ClCompile Include="coding\atrac9_decoder.c" />
|
||||
<ClCompile Include="coding\celt_fsb_decoder.c" />
|
||||
<ClCompile Include="coding\circus_decoder.c" />
|
||||
<ClCompile Include="coding\coding_utils.c" />
|
||||
<ClCompile Include="coding\ffmpeg_decoder.c" />
|
||||
<ClCompile Include="coding\ffmpeg_decoder_custom_opus.c" />
|
||||
@ -319,6 +320,7 @@
|
||||
<ClCompile Include="meta\nub_xma.c" />
|
||||
<ClCompile Include="meta\nus3bank.c" />
|
||||
<ClCompile Include="meta\nwa.c" />
|
||||
<ClCompile Include="meta\nwav.c" />
|
||||
<ClCompile Include="meta\nxa.c" />
|
||||
<ClCompile Include="meta\nxap.c" />
|
||||
<ClCompile Include="meta\ogg_vorbis.c" />
|
||||
@ -477,6 +479,7 @@
|
||||
<ClCompile Include="meta\xma.c" />
|
||||
<ClCompile Include="meta\xnb.c" />
|
||||
<ClCompile Include="meta\xopus.c" />
|
||||
<ClCompile Include="meta\xpcm.c" />
|
||||
<ClCompile Include="meta\xss.c" />
|
||||
<ClCompile Include="meta\xvag.c" />
|
||||
<ClCompile Include="meta\xmd.c" />
|
||||
|
@ -523,6 +523,9 @@
|
||||
<ClCompile Include="meta\nwa.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\nwav.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\nxa.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
@ -1414,6 +1417,9 @@
|
||||
<ClCompile Include="coding\celt_fsb_decoder.c">
|
||||
<Filter>coding\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="coding\circus_decoder.c">
|
||||
<Filter>coding\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\bcstm.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
@ -1453,6 +1459,9 @@
|
||||
<ClCompile Include="meta\xopus.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\xpcm.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\x360_cxs.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
|
@ -1,7 +1,7 @@
|
||||
#include "meta.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)] */
|
||||
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_end = loop_start + loop_length; /* loop_start is -1 if not set */
|
||||
|
||||
codec = PSX;
|
||||
codec = HEVAG;
|
||||
break;
|
||||
|
||||
default:
|
||||
@ -381,6 +381,17 @@ VGMSTREAM * init_vgmstream_bnk_sony(STREAMFILE *streamFile) {
|
||||
vgmstream->layout_type = layout_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->loop_start_sample = loop_start;
|
||||
vgmstream->loop_end_sample = loop_end;
|
||||
|
@ -804,4 +804,8 @@ VGMSTREAM * init_vgmstream_vs_ffx(STREAMFILE * streamFile);
|
||||
VGMSTREAM * init_vgmstream_msf_banpresto_wmsf(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*/
|
||||
|
57
src/meta/nwav.c
Normal file
57
src/meta/nwav.c
Normal 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;
|
||||
}
|
@ -111,11 +111,6 @@ VGMSTREAM * init_vgmstream_sgxd(STREAMFILE *streamFile) {
|
||||
if (name_offset)
|
||||
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) {
|
||||
case 0x03: /* PS-ADPCM [Genji (PS3), Ape Escape Move (PS3)]*/
|
||||
vgmstream->coding_type = coding_PSX;
|
||||
|
@ -10,7 +10,7 @@ VGMSTREAM * init_vgmstream_sxd(STREAMFILE *streamFile) {
|
||||
size_t chunk_size, stream_size = 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;
|
||||
uint32_t at9_config_data = 0;
|
||||
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) */
|
||||
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) */
|
||||
total_subsongs = read_32bitLE(chunk_offset+0x04,streamHeader);
|
||||
if (target_subsong == 0) target_subsong = 1;
|
||||
if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail;
|
||||
// todo rarely a WAVE subsong may point to a repeated data offset, with different tags only
|
||||
|
||||
|
||||
/* read stream header */
|
||||
{
|
||||
@ -50,9 +53,10 @@ VGMSTREAM * init_vgmstream_sxd(STREAMFILE *streamFile) {
|
||||
table_offset = chunk_offset + 0x08 + 4*(target_subsong-1);
|
||||
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);
|
||||
channels = read_8bit (header_offset+0x05,streamHeader);
|
||||
/* 0x06(2): unknown, rarely 0xFF */
|
||||
sample_rate = read_32bitLE(header_offset+0x08,streamHeader);
|
||||
/* 0x0c(4): unknown size? (0x4000/0x3999/0x3333/etc, not related to extra data) */
|
||||
/* 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_offset = read_32bitLE(header_offset+0x24,streamHeader);
|
||||
|
||||
/* Extra data, variable sized and uses some kind of TLVs (HEVAG's is optional and much smaller).
|
||||
* One tag seems to add a small part of the ATRAC9 data, for RAM preloding I guess. */
|
||||
loop_flag = loop_start_sample != -1 && loop_end_sample != -1;
|
||||
|
||||
/* 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) {
|
||||
off_t extra_offset = header_offset+0x28;
|
||||
off_t max_offset = chunk_offset + chunk_size;
|
||||
|
||||
/* manually try to find certain tag, no idea about the actual format
|
||||
* (most variable in Soul Sacrifice; extra data size isn't found in the header AFAIK) */
|
||||
if (!(flags & 1))
|
||||
goto fail;
|
||||
|
||||
while (extra_offset < max_offset) {
|
||||
uint32_t tag = read_32bitBE(extra_offset, streamHeader);
|
||||
if (tag == 0x0A010000 || tag == 0x0A010600) {
|
||||
@ -83,26 +105,7 @@ VGMSTREAM * init_vgmstream_sxd(STREAMFILE *streamFile) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
loop_flag = loop_start_sample != -1 && loop_end_sample != -1;
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/* usually .sxd=header+data and .sxd1=header + .sxd2=data, but rarely sxd1 may contain data [The Last Guardian (PS4)] */
|
||||
if (is_external) {
|
||||
start_offset = stream_offset; /* absolute if external */
|
||||
} else {
|
||||
|
@ -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 */
|
||||
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);
|
||||
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;
|
||||
else
|
||||
goto fail;
|
||||
@ -228,8 +228,10 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) {
|
||||
vgmstream->interleave_block_size = ww.block_align / ww.channels;
|
||||
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;
|
||||
}
|
||||
|
||||
vgmstream->num_samples = xbox_ima_bytes_to_samples(ww.data_size, ww.channels);
|
||||
break;
|
||||
@ -370,9 +372,10 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) {
|
||||
start_offset += audio_offset;
|
||||
|
||||
/* Vorbis is VBR so this is very approximate percent, meh */
|
||||
if (ww.truncated)
|
||||
if (ww.truncated) {
|
||||
vgmstream->num_samples = (int32_t)(vgmstream->num_samples *
|
||||
(double)(ww.file_size - start_offset) / (double)ww.data_size);
|
||||
}
|
||||
|
||||
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");
|
||||
/* 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;
|
||||
}
|
||||
|
||||
|
69
src/meta/xpcm.c
Normal file
69
src/meta/xpcm.c
Normal 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;
|
||||
}
|
@ -106,6 +106,15 @@ VGMSTREAM * init_vgmstream_xvag(STREAMFILE *streamFile) {
|
||||
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 */
|
||||
vgmstream = allocate_vgmstream(xvag.channels,xvag.loop_flag);
|
||||
|
@ -447,6 +447,8 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = {
|
||||
init_vgmstream_vs_ffx,
|
||||
init_vgmstream_msf_banpresto_wmsf,
|
||||
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) */
|
||||
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_NWA:
|
||||
case coding_SASSC:
|
||||
case coding_CIRCUS_ADPCM:
|
||||
return 1;
|
||||
|
||||
case coding_IMA:
|
||||
@ -1297,6 +1300,7 @@ int get_vgmstream_frame_size(VGMSTREAM * vgmstream) {
|
||||
case coding_DERF:
|
||||
case coding_NWA:
|
||||
case coding_SASSC:
|
||||
case coding_CIRCUS_ADPCM:
|
||||
return 0x01;
|
||||
|
||||
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);
|
||||
}
|
||||
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_int:
|
||||
|
@ -159,6 +159,7 @@ typedef enum {
|
||||
coding_DERF, /* DERF 8-bit DPCM */
|
||||
coding_ACM, /* InterPlay ACM */
|
||||
coding_NWA, /* VisualArt's NWA */
|
||||
coding_CIRCUS_ADPCM, /* Circus 8-bit ADPCM */
|
||||
|
||||
coding_EA_MT, /* Electronic Arts MicroTalk (linear-predictive speech codec) */
|
||||
|
||||
@ -702,6 +703,8 @@ typedef enum {
|
||||
meta_VA3, /* DDR Supernova 2 AC */
|
||||
meta_XOPUS,
|
||||
meta_VS_FFX,
|
||||
meta_NWAV,
|
||||
meta_XPCM,
|
||||
|
||||
} meta_t;
|
||||
|
||||
|
@ -1450,6 +1450,7 @@ winamp_tags last_tags;
|
||||
* Winamp requests one tag at a time and may reask for the same tag several times */
|
||||
static void load_tagfile_info(in_char* filename) {
|
||||
STREAMFILE *tagFile = NULL;
|
||||
in_char filename_clean[PATH_LIMIT];
|
||||
char filename_utf8[PATH_LIMIT];
|
||||
char tagfile_path_utf8[PATH_LIMIT];
|
||||
in_char tagfile_path_i[PATH_LIMIT];
|
||||
@ -1461,13 +1462,15 @@ static void load_tagfile_info(in_char* filename) {
|
||||
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 */
|
||||
}
|
||||
|
||||
/* 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);
|
||||
|
||||
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_strcpy(last_tags.filename, filename);
|
||||
wa_strcpy(last_tags.filename, filename_clean);
|
||||
last_tags.tag_count = 0;
|
||||
|
||||
/* load all tags from tagfile */
|
||||
|
Loading…
Reference in New Issue
Block a user