mirror of
https://github.com/vgmstream/vgmstream.git
synced 2025-02-20 20:41:08 +01:00
commit
3e916676e3
@ -635,7 +635,7 @@ fail:
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int convert_subsongs(cli_config* cfg) {
|
static int convert_subsongs(cli_config* cfg) {
|
||||||
int res, oks, kos;
|
int res, kos;
|
||||||
int subsong;
|
int subsong;
|
||||||
/* restore original values in case of multiple parsed files */
|
/* restore original values in case of multiple parsed files */
|
||||||
int start_temp = cfg->subsong_index;
|
int start_temp = cfg->subsong_index;
|
||||||
@ -651,7 +651,6 @@ static int convert_subsongs(cli_config* cfg) {
|
|||||||
//;VGM_LOG("CLI: subsongs %i to %i\n", cfg->subsong_index, cfg->subsong_end + 1);
|
//;VGM_LOG("CLI: subsongs %i to %i\n", cfg->subsong_index, cfg->subsong_end + 1);
|
||||||
|
|
||||||
/* convert subsong range */
|
/* convert subsong range */
|
||||||
oks = 0;
|
|
||||||
kos = 0 ;
|
kos = 0 ;
|
||||||
for (subsong = cfg->subsong_index; subsong < cfg->subsong_end + 1; subsong++) {
|
for (subsong = cfg->subsong_index; subsong < cfg->subsong_end + 1; subsong++) {
|
||||||
cfg->subsong_index = subsong;
|
cfg->subsong_index = subsong;
|
||||||
@ -661,7 +660,7 @@ static int convert_subsongs(cli_config* cfg) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (kos) {
|
if (kos) {
|
||||||
fprintf(stderr, "failed %i of %i subsongs\n", kos, oks);
|
fprintf(stderr, "failed %i subsongs\n", kos);
|
||||||
}
|
}
|
||||||
|
|
||||||
cfg->subsong_index = start_temp;
|
cfg->subsong_index = start_temp;
|
||||||
|
@ -204,7 +204,7 @@ void decode_mtaf(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing,
|
|||||||
|
|
||||||
|
|
||||||
/* mta2_decoder */
|
/* mta2_decoder */
|
||||||
void decode_mta2(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel);
|
void decode_mta2(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, int config);
|
||||||
|
|
||||||
|
|
||||||
/* mc3_decoder */
|
/* mc3_decoder */
|
||||||
|
@ -38,19 +38,30 @@ static const int mta2_scales[32] = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/* decodes a block for a channel */
|
/* decodes a block for a channel */
|
||||||
void decode_mta2(VGMSTREAMCHANNEL *stream, sample_t *outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) {
|
void decode_mta2(VGMSTREAMCHANNEL *stream, sample_t *outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, int config) {
|
||||||
uint8_t frame[0x10 + 0x90*8] = {0};
|
uint8_t frame[0x10 + 0x90*8] = {0};
|
||||||
int samples_done = 0, sample_count = 0, channel_block_samples, channel_first_sample, frame_size = 0;
|
int samples_done = 0, sample_count = 0, channel_block_samples, channel_first_sample, frame_size = 0;
|
||||||
int i, group, row, col;
|
int i, group, row, col;
|
||||||
int track_channels = 0, track_channel;
|
int track_channel;
|
||||||
|
uint32_t frame_offset = stream->offset;
|
||||||
|
uint32_t head_size;
|
||||||
|
|
||||||
|
|
||||||
/* track skip */
|
if (config == 1) {
|
||||||
|
/* regular frames (sfx) */
|
||||||
|
frame_size = 0x90 * channelspacing;
|
||||||
|
track_channel = channel;
|
||||||
|
head_size = 0x00;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/* track info (bgm): parse header and skip tracks that our current channel doesn't belong to */
|
||||||
|
int track_channels;
|
||||||
|
|
||||||
|
head_size = 0x10;
|
||||||
do {
|
do {
|
||||||
int num_track = 0, channel_layout;
|
int num_track = 0, channel_layout;
|
||||||
|
|
||||||
/* parse track header (0x10) and skip tracks that our current channel doesn't belong to */
|
read_streamfile(frame, frame_offset, head_size, stream->streamfile); /* ignore EOF errors */
|
||||||
read_streamfile(frame, stream->offset, 0x10, stream->streamfile); /* ignore EOF errors */
|
|
||||||
num_track = get_u8 (frame + 0x00); /* 0=first */
|
num_track = get_u8 (frame + 0x00); /* 0=first */
|
||||||
/* 0x01(3): num_frame (0=first) */
|
/* 0x01(3): num_frame (0=first) */
|
||||||
/* 0x04(1): 0? */
|
/* 0x04(1): 0? */
|
||||||
@ -58,7 +69,7 @@ void decode_mta2(VGMSTREAMCHANNEL *stream, sample_t *outbuf, int channelspacing,
|
|||||||
frame_size = get_u16be(frame + 0x06); /* not including this header */
|
frame_size = get_u16be(frame + 0x06); /* not including this header */
|
||||||
/* 0x08(8): null */
|
/* 0x08(8): null */
|
||||||
|
|
||||||
VGM_ASSERT(frame_size == 0, "MTA2: empty frame at %x\n", (uint32_t)stream->offset);
|
VGM_ASSERT(frame_size == 0, "MTA2: empty frame at %x\n", frame_offset);
|
||||||
/* frame_size 0 means silent/empty frame (rarely found near EOF for one track but not others)
|
/* frame_size 0 means silent/empty frame (rarely found near EOF for one track but not others)
|
||||||
* negative track only happens for truncated files (EOF) */
|
* negative track only happens for truncated files (EOF) */
|
||||||
if (frame_size == 0 || num_track < 0) {
|
if (frame_size == 0 || num_track < 0) {
|
||||||
@ -68,6 +79,7 @@ void decode_mta2(VGMSTREAMCHANNEL *stream, sample_t *outbuf, int channelspacing,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
track_channels = 0;
|
track_channels = 0;
|
||||||
for (i = 0; i < 8; i++) { /* max 8ch */
|
for (i = 0; i < 8; i++) { /* max 8ch */
|
||||||
if ((channel_layout >> i) & 0x01)
|
if ((channel_layout >> i) & 0x01)
|
||||||
@ -75,7 +87,7 @@ void decode_mta2(VGMSTREAMCHANNEL *stream, sample_t *outbuf, int channelspacing,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (track_channels == 0) { /* bad data, avoid div by 0 */
|
if (track_channels == 0) { /* bad data, avoid div by 0 */
|
||||||
VGM_LOG("MTA2: track_channels 0 at %x\n", (uint32_t)stream->offset);
|
VGM_LOG("MTA2: track_channels 0 at %x\n", frame_offset);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -84,20 +96,28 @@ void decode_mta2(VGMSTREAMCHANNEL *stream, sample_t *outbuf, int channelspacing,
|
|||||||
break; /* channel belongs to this track */
|
break; /* channel belongs to this track */
|
||||||
|
|
||||||
/* keep looping for our track */
|
/* keep looping for our track */
|
||||||
stream->offset += 0x10 + frame_size;
|
stream->offset += head_size + frame_size;
|
||||||
|
frame_offset = stream->offset;
|
||||||
}
|
}
|
||||||
while (1);
|
while (1);
|
||||||
|
|
||||||
/* parse stuff */
|
frame_offset += head_size; /* point to actual data */
|
||||||
read_streamfile(frame + 0x10, stream->offset + 0x10, frame_size, stream->streamfile); /* ignore EOF errors */
|
|
||||||
track_channel = channel % track_channels;
|
track_channel = channel % track_channels;
|
||||||
|
if (frame_size > sizeof(frame))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* parse stuff */
|
||||||
|
read_streamfile(frame, frame_offset, frame_size, stream->streamfile); /* ignore EOF errors */
|
||||||
channel_block_samples = (0x80*2);
|
channel_block_samples = (0x80*2);
|
||||||
channel_first_sample = first_sample % (0x80*2);
|
channel_first_sample = first_sample % (0x80*2);
|
||||||
|
|
||||||
|
|
||||||
/* parse channel frame (header 0x04*4 + data 0x20*4) */
|
/* parse channel frame (header 0x04*4 + data 0x20*4) */
|
||||||
for (group = 0; group < 4; group++) {
|
for (group = 0; group < 4; group++) {
|
||||||
short hist2, hist1, coefs, scale;
|
short hist2, hist1, coefs, scale;
|
||||||
uint32_t group_header = get_u32be(frame + 0x10 + track_channel*0x90 + group*0x4);
|
uint32_t group_header = get_u32be(frame + track_channel*0x90 + group*0x4);
|
||||||
hist2 = (short) ((group_header >> 16) & 0xfff0); /* upper 16b discarding 4b */
|
hist2 = (short) ((group_header >> 16) & 0xfff0); /* upper 16b discarding 4b */
|
||||||
hist1 = (short) ((group_header >> 4) & 0xfff0); /* lower 16b discarding 4b */
|
hist1 = (short) ((group_header >> 4) & 0xfff0); /* lower 16b discarding 4b */
|
||||||
coefs = (group_header >> 5) & 0x7; /* mid 3b */
|
coefs = (group_header >> 5) & 0x7; /* mid 3b */
|
||||||
@ -118,7 +138,7 @@ void decode_mta2(VGMSTREAMCHANNEL *stream, sample_t *outbuf, int channelspacing,
|
|||||||
|
|
||||||
/* decode nibbles */
|
/* decode nibbles */
|
||||||
for (row = 0; row < 8; row++) {
|
for (row = 0; row < 8; row++) {
|
||||||
int pos = 0x10 + track_channel*0x90 + 0x10 + group*0x4 + row*0x10;
|
int pos = track_channel*0x90 + 0x10 + group*0x4 + row*0x10;
|
||||||
for (col = 0; col < 4*2; col++) {
|
for (col = 0; col < 4*2; col++) {
|
||||||
uint8_t nibbles = frame[pos + col/2];
|
uint8_t nibbles = frame[pos + col/2];
|
||||||
int32_t sample;
|
int32_t sample;
|
||||||
@ -148,6 +168,6 @@ void decode_mta2(VGMSTREAMCHANNEL *stream, sample_t *outbuf, int channelspacing,
|
|||||||
|
|
||||||
/* block fully done */
|
/* block fully done */
|
||||||
if (channel_first_sample + samples_done == channel_block_samples) {
|
if (channel_first_sample + samples_done == channel_block_samples) {
|
||||||
stream->offset += 0x10 + frame_size;
|
stream->offset += head_size + frame_size;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1392,7 +1392,7 @@ void decode_vgmstream(VGMSTREAM* vgmstream, int samples_written, int samples_to_
|
|||||||
case coding_MTA2:
|
case coding_MTA2:
|
||||||
for (ch = 0; ch < vgmstream->channels; ch++) {
|
for (ch = 0; ch < vgmstream->channels; ch++) {
|
||||||
decode_mta2(&vgmstream->ch[ch], buffer+ch,
|
decode_mta2(&vgmstream->ch[ch], buffer+ch,
|
||||||
vgmstream->channels, vgmstream->samples_into_block, samples_to_do, ch);
|
vgmstream->channels, vgmstream->samples_into_block, samples_to_do, ch, vgmstream->codec_config);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case coding_MC3:
|
case coding_MC3:
|
||||||
|
@ -502,6 +502,7 @@ static const char* extension_list[] = {
|
|||||||
"ssd", //txth/reserved [Zack & Wiki (Wii)]
|
"ssd", //txth/reserved [Zack & Wiki (Wii)]
|
||||||
"ssm",
|
"ssm",
|
||||||
"sspr",
|
"sspr",
|
||||||
|
"ssp",
|
||||||
"sss",
|
"sss",
|
||||||
"ster",
|
"ster",
|
||||||
"sth",
|
"sth",
|
||||||
@ -1386,6 +1387,7 @@ static const meta_info meta_info_list[] = {
|
|||||||
{meta_WBK_NSLB, "Treyarch NSLB header"},
|
{meta_WBK_NSLB, "Treyarch NSLB header"},
|
||||||
{meta_DSP_APEX, "Koei Tecmo APEX header"},
|
{meta_DSP_APEX, "Koei Tecmo APEX header"},
|
||||||
{meta_MPEG, "MPEG header"},
|
{meta_MPEG, "MPEG header"},
|
||||||
|
{meta_SSPF, "Konami SSPF header"},
|
||||||
};
|
};
|
||||||
|
|
||||||
void get_vgmstream_coding_description(VGMSTREAM* vgmstream, char* out, size_t out_size) {
|
void get_vgmstream_coding_description(VGMSTREAM* vgmstream, char* out, size_t out_size) {
|
||||||
|
@ -264,6 +264,7 @@
|
|||||||
<ClCompile Include="meta\sqex_scd.c" />
|
<ClCompile Include="meta\sqex_scd.c" />
|
||||||
<ClCompile Include="meta\sqex_scd_sscf.c" />
|
<ClCompile Include="meta\sqex_scd_sscf.c" />
|
||||||
<ClCompile Include="meta\sqex_sead.c" />
|
<ClCompile Include="meta\sqex_sead.c" />
|
||||||
|
<ClCompile Include="meta\sspc.c" />
|
||||||
<ClCompile Include="meta\sspr.c" />
|
<ClCompile Include="meta\sspr.c" />
|
||||||
<ClCompile Include="meta\sthd.c" />
|
<ClCompile Include="meta\sthd.c" />
|
||||||
<ClCompile Include="meta\tun.c" />
|
<ClCompile Include="meta\tun.c" />
|
||||||
|
@ -1687,6 +1687,9 @@
|
|||||||
<ClCompile Include="meta\sqex_sead.c">
|
<ClCompile Include="meta\sqex_sead.c">
|
||||||
<Filter>meta\Source Files</Filter>
|
<Filter>meta\Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="meta\sspc.c">
|
||||||
|
<Filter>meta\Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
<ClCompile Include="meta\sspr.c">
|
<ClCompile Include="meta\sspr.c">
|
||||||
<Filter>meta\Source Files</Filter>
|
<Filter>meta\Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
@ -83,6 +83,7 @@ VGMSTREAM* init_vgmstream_aifc(STREAMFILE* sf) {
|
|||||||
/* checks */
|
/* checks */
|
||||||
if (!is_id32be(0x00,sf, "FORM"))
|
if (!is_id32be(0x00,sf, "FORM"))
|
||||||
goto fail;
|
goto fail;
|
||||||
|
VGM_LOG("1\n");
|
||||||
|
|
||||||
/* .aif: common (AIFF or AIFC), .aiff: common AIFF, .aifc: common AIFC
|
/* .aif: common (AIFF or AIFC), .aiff: common AIFF, .aifc: common AIFC
|
||||||
* .laif/laiff/laifc: for plugins
|
* .laif/laiff/laifc: for plugins
|
||||||
@ -95,18 +96,18 @@ VGMSTREAM* init_vgmstream_aifc(STREAMFILE* sf) {
|
|||||||
* .fda: Homeworld 2 (PC)
|
* .fda: Homeworld 2 (PC)
|
||||||
* .n64: Turok (N64) src
|
* .n64: Turok (N64) src
|
||||||
* .pcm: Road Rash (SAT)
|
* .pcm: Road Rash (SAT)
|
||||||
* .wav: SimCity 3000 (Mac)
|
* .wav: SimCity 3000 (Mac) (both AIFC and AIFF)
|
||||||
* .lwav: for media players that may confuse this format with the usual RIFF WAVE file.
|
* .lwav: for media players that may confuse this format with the usual RIFF WAVE file.
|
||||||
* .xa: SimCity 3000 (Mac)
|
* .xa: SimCity 3000 (Mac)
|
||||||
*/
|
*/
|
||||||
if (check_extensions(sf, "aif,laif,")) {
|
if (check_extensions(sf, "aif,laif,wav,lwav,")) {
|
||||||
is_aifc_ext = 1;
|
is_aifc_ext = 1;
|
||||||
is_aiff_ext = 1;
|
is_aiff_ext = 1;
|
||||||
}
|
}
|
||||||
else if (check_extensions(sf, "aifc,laifc,afc,cbd2,bgm,fda,n64,wav,lwav,xa")) {
|
else if (check_extensions(sf, "aifc,laifc,afc,cbd2,bgm,fda,n64,xa")) {
|
||||||
is_aifc_ext = 1;
|
is_aifc_ext = 1;
|
||||||
}
|
}
|
||||||
else if (check_extensions(sf, "aiff,laiff,acm,adp,ai,pcm,wav")) {
|
else if (check_extensions(sf, "aiff,laiff,acm,adp,ai,pcm")) {
|
||||||
is_aiff_ext = 1;
|
is_aiff_ext = 1;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -136,8 +137,10 @@ VGMSTREAM* init_vgmstream_aifc(STREAMFILE* sf) {
|
|||||||
aifx_size += 0x08; /* [Psychic Force Puzzle Taisen CD2 (PS1)] */
|
aifx_size += 0x08; /* [Psychic Force Puzzle Taisen CD2 (PS1)] */
|
||||||
}
|
}
|
||||||
|
|
||||||
if (aifx_size + 0x08 != file_size)
|
if (aifx_size + 0x08 != file_size) {
|
||||||
|
vgm_logi("AIFF: wrong reported size %x + 0x8 vs file size %x\n", aifx_size, file_size);
|
||||||
goto fail;
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* read through chunks to verify format and find metadata */
|
/* read through chunks to verify format and find metadata */
|
||||||
|
@ -235,11 +235,15 @@ static int parse_header(STREAMFILE* sf, eacs_header* ea, uint32_t offset) {
|
|||||||
ea->num_samples = read_s32(offset+0x0c, sf);
|
ea->num_samples = read_s32(offset+0x0c, sf);
|
||||||
ea->loop_start = read_s32(offset+0x10, sf);
|
ea->loop_start = read_s32(offset+0x10, sf);
|
||||||
ea->loop_end = read_s32(offset+0x14, sf) + ea->loop_start; /* loop length */
|
ea->loop_end = read_s32(offset+0x14, sf) + ea->loop_start; /* loop length */
|
||||||
ea->data_offset = read_s32(offset+0x18, sf); /* 0 when blocked */
|
ea->data_offset = read_s32(offset+0x18, sf); /* 0 when blocked, usually */
|
||||||
/* 0x1c: pan/volume/etc? (0x7F)
|
/* 0x1c: pan/volume/etc? (0x7F)
|
||||||
* rest may be padding/garbage */
|
* rest may be padding/garbage */
|
||||||
//VGM_ASSERT(ea->type != 0, "EA EACS: unknown type %i\n", ea->type);
|
//VGM_ASSERT(ea->type != 0, "EA EACS: unknown type %i\n", ea->type);
|
||||||
|
|
||||||
|
/* blocked should set 0 but in rare cases points to data start [NBA Live 95 (MS-DOS)] */
|
||||||
|
if (!ea->is_bank)
|
||||||
|
ea->data_offset = 0;
|
||||||
|
|
||||||
if (ea->codec == EA_CODEC_IMA)
|
if (ea->codec == EA_CODEC_IMA)
|
||||||
ea->codec_config = get_ea_1snh_ima_version(sf, 0x00, ea);
|
ea->codec_config = get_ea_1snh_ima_version(sf, 0x00, ea);
|
||||||
/* EACS banks with empty values exist but will be rejected later */
|
/* EACS banks with empty values exist but will be rejected later */
|
||||||
|
@ -46,7 +46,7 @@ VGMSTREAM* init_vgmstream_hca_subkey(STREAMFILE* sf, uint16_t subkey) {
|
|||||||
uint8_t keybuf[0x08+0x02];
|
uint8_t keybuf[0x08+0x02];
|
||||||
size_t keysize;
|
size_t keysize;
|
||||||
|
|
||||||
keysize = read_key_file(keybuf, 0x08+0x04, sf);
|
keysize = read_key_file(keybuf, sizeof(keybuf), sf);
|
||||||
if (keysize == 0x08) { /* standard */
|
if (keysize == 0x08) { /* standard */
|
||||||
keycode = get_u64be(keybuf+0x00);
|
keycode = get_u64be(keybuf+0x00);
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@ typedef VGMSTREAM* (*init_vgmstream_t)(STREAMFILE* sf);
|
|||||||
|
|
||||||
VGMSTREAM* init_vgmstream_silence(int channels, int sample_rate, int32_t num_samples);
|
VGMSTREAM* init_vgmstream_silence(int channels, int sample_rate, int32_t num_samples);
|
||||||
VGMSTREAM* init_vgmstream_silence_container(int total_subsongs);
|
VGMSTREAM* init_vgmstream_silence_container(int total_subsongs);
|
||||||
|
VGMSTREAM* init_vgmstream_silence_base(VGMSTREAM* vgmstream);
|
||||||
|
|
||||||
|
|
||||||
VGMSTREAM* init_vgmstream_adx(STREAMFILE* sf);
|
VGMSTREAM* init_vgmstream_adx(STREAMFILE* sf);
|
||||||
@ -654,6 +655,7 @@ VGMSTREAM* init_vgmstream_opus_prototype(STREAMFILE* sf);
|
|||||||
VGMSTREAM* init_vgmstream_opus_opusnx(STREAMFILE* sf);
|
VGMSTREAM* init_vgmstream_opus_opusnx(STREAMFILE* sf);
|
||||||
VGMSTREAM* init_vgmstream_opus_nsopus(STREAMFILE* sf);
|
VGMSTREAM* init_vgmstream_opus_nsopus(STREAMFILE* sf);
|
||||||
VGMSTREAM* init_vgmstream_opus_sqex(STREAMFILE* sf);
|
VGMSTREAM* init_vgmstream_opus_sqex(STREAMFILE* sf);
|
||||||
|
VGMSTREAM* init_vgmstream_opus_rsnd(STREAMFILE* sf);
|
||||||
|
|
||||||
VGMSTREAM * init_vgmstream_raw_al(STREAMFILE * streamFile);
|
VGMSTREAM * init_vgmstream_raw_al(STREAMFILE * streamFile);
|
||||||
|
|
||||||
@ -976,4 +978,6 @@ VGMSTREAM* init_vgmstream_ubi_ckd_cwav(STREAMFILE* sf);
|
|||||||
|
|
||||||
VGMSTREAM* init_vgmstream_mpeg(STREAMFILE* sf);
|
VGMSTREAM* init_vgmstream_mpeg(STREAMFILE* sf);
|
||||||
|
|
||||||
|
VGMSTREAM* init_vgmstream_sspf(STREAMFILE* sf);
|
||||||
|
|
||||||
#endif /*_META_H*/
|
#endif /*_META_H*/
|
||||||
|
@ -511,3 +511,32 @@ VGMSTREAM* init_vgmstream_opus_sqex(STREAMFILE* sf) {
|
|||||||
fail:
|
fail:
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Idea Factory(?) variation [Birushana: Ichijuu no Kaze (Switch)] */
|
||||||
|
VGMSTREAM* init_vgmstream_opus_rsnd(STREAMFILE* sf) {
|
||||||
|
off_t offset = 0;
|
||||||
|
int num_samples = 0, loop_start = 0, loop_end = 0, loop_flag;
|
||||||
|
|
||||||
|
/* checks */
|
||||||
|
if (!is_id32be(0x00, sf,"RSND"))
|
||||||
|
goto fail;
|
||||||
|
if (!check_extensions(sf, "rsnd"))
|
||||||
|
goto fail;
|
||||||
|
/* 0x04: 00? (16b)*/
|
||||||
|
/* 0x06: 00? (8b)*/
|
||||||
|
loop_flag = read_u8(0x07, sf);
|
||||||
|
if (loop_flag) { /* not really needed as both will be 0 */
|
||||||
|
loop_start = read_s32le(0x08, sf);
|
||||||
|
loop_end = read_s32le(0x0c, sf);
|
||||||
|
}
|
||||||
|
offset = read_u32le(0x10, sf); /* always 0x40 */
|
||||||
|
/* 0x14: offset again? */
|
||||||
|
/* 0x18+: null? (unknown numbers in bgm050) */
|
||||||
|
num_samples = 0; /* not loop_end as it isn't set when looping is disabled */
|
||||||
|
|
||||||
|
|
||||||
|
return init_vgmstream_opus(sf, meta_OPUS, offset, num_samples, loop_start, loop_end);
|
||||||
|
fail:
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
#include "meta.h"
|
#include "meta.h"
|
||||||
|
|
||||||
/* silent stream - mainly for engines that need them or dummy subsongs */
|
/* silent stream - mainly for engines that need them or dummy subsongs */
|
||||||
VGMSTREAM* init_vgmstream_silence(int channels, int sample_rate, int32_t num_samples) {
|
static VGMSTREAM* init_vgmstream_silence_internal(int channels, int sample_rate, int32_t num_samples, uint32_t channel_layout) {
|
||||||
VGMSTREAM* vgmstream = NULL;
|
VGMSTREAM* vgmstream = NULL;
|
||||||
|
|
||||||
if (channels <= 0)
|
if (channels <= 0)
|
||||||
@ -18,6 +18,7 @@ VGMSTREAM* init_vgmstream_silence(int channels, int sample_rate, int32_t num_sam
|
|||||||
vgmstream->meta_type = meta_SILENCE;
|
vgmstream->meta_type = meta_SILENCE;
|
||||||
vgmstream->sample_rate = sample_rate;
|
vgmstream->sample_rate = sample_rate;
|
||||||
vgmstream->num_samples = num_samples;
|
vgmstream->num_samples = num_samples;
|
||||||
|
vgmstream->channel_layout = channel_layout;
|
||||||
|
|
||||||
vgmstream->coding_type = coding_SILENCE;
|
vgmstream->coding_type = coding_SILENCE;
|
||||||
vgmstream->layout_type = layout_none;
|
vgmstream->layout_type = layout_none;
|
||||||
@ -28,11 +29,15 @@ fail:
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
VGMSTREAM* init_vgmstream_silence(int channels, int sample_rate, int32_t num_samples) {
|
||||||
|
return init_vgmstream_silence_internal(channels, sample_rate, num_samples, 0);
|
||||||
|
}
|
||||||
|
|
||||||
/* silent stream - for containers that have dummy streams but it's a hassle to detect/filter out */
|
/* silent stream - for containers that have dummy streams but it's a hassle to detect/filter out */
|
||||||
VGMSTREAM* init_vgmstream_silence_container(int total_subsongs) {
|
VGMSTREAM* init_vgmstream_silence_container(int total_subsongs) {
|
||||||
VGMSTREAM* vgmstream = NULL;
|
VGMSTREAM* vgmstream = NULL;
|
||||||
|
|
||||||
vgmstream = init_vgmstream_silence(0, 0, 0);
|
vgmstream = init_vgmstream_silence_internal(0, 0, 0, 0);
|
||||||
if (!vgmstream) goto fail;
|
if (!vgmstream) goto fail;
|
||||||
|
|
||||||
vgmstream->num_streams = total_subsongs;
|
vgmstream->num_streams = total_subsongs;
|
||||||
@ -43,3 +48,14 @@ fail:
|
|||||||
close_vgmstream(vgmstream);
|
close_vgmstream(vgmstream);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
VGMSTREAM* init_vgmstream_silence_base(VGMSTREAM* v) {
|
||||||
|
if (!v)
|
||||||
|
return init_vgmstream_silence_internal(0, 0, 0, 0);
|
||||||
|
|
||||||
|
return init_vgmstream_silence_internal(
|
||||||
|
v->channels,
|
||||||
|
v->sample_rate,
|
||||||
|
0, //v->num_samples ?
|
||||||
|
v->channel_layout);
|
||||||
|
}
|
120
src/meta/sspf.c
Normal file
120
src/meta/sspf.c
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
#include "meta.h"
|
||||||
|
|
||||||
|
|
||||||
|
/* SSPF - Konami/KCET banks [Metal Gear Solid 4 (PS3)] */
|
||||||
|
VGMSTREAM* init_vgmstream_sspf(STREAMFILE* sf) {
|
||||||
|
VGMSTREAM* vgmstream = NULL;
|
||||||
|
off_t start_offset;
|
||||||
|
int loop_flag, channels, sample_rate;
|
||||||
|
int32_t num_samples, loop_start;
|
||||||
|
int total_subsongs, target_subsong = sf->stream_index;
|
||||||
|
uint32_t file_size, pad_size, offset, bwav_offset, iwav_offset, ssw2_offset, stream_size;
|
||||||
|
uint32_t codec;
|
||||||
|
|
||||||
|
|
||||||
|
/* checks */
|
||||||
|
if (!is_id32be(0x00,sf, "SSPF"))
|
||||||
|
goto fail;
|
||||||
|
if (!check_extensions(sf, "ssp"))
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
/* extra check to ignore .spc, that are a RAM pack of .ssp with a ~0x800 table at the end */
|
||||||
|
file_size = read_u32be(0x08, sf) + 0x08; /* without padding */
|
||||||
|
pad_size = 0;
|
||||||
|
if (file_size % 0x800) /* add padding */
|
||||||
|
pad_size = 0x800 - (file_size % 0x800);
|
||||||
|
if (file_size != get_streamfile_size(sf) && file_size + pad_size != get_streamfile_size(sf))
|
||||||
|
goto fail;
|
||||||
|
/* 0x0c: "loadBank"? (always 2? MTA2 is always 1) */
|
||||||
|
|
||||||
|
/* read chunks (fixed order) */
|
||||||
|
bwav_offset = read_u32be(0x04, sf) + 0x08;
|
||||||
|
if (!is_id32be(bwav_offset,sf, "BWAV"))
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
iwav_offset = read_u32be(bwav_offset + 0x04, sf) + 0x08 + bwav_offset;
|
||||||
|
|
||||||
|
if (!is_id32be(iwav_offset,sf, "IWAV"))
|
||||||
|
goto fail;
|
||||||
|
/* past IWAV are some more chunks then padding (variable? some are defined in debug structs only, not seen) */
|
||||||
|
|
||||||
|
total_subsongs = read_u32be(iwav_offset + 0x08,sf);
|
||||||
|
if (target_subsong == 0) target_subsong = 1;
|
||||||
|
if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail;
|
||||||
|
|
||||||
|
offset = iwav_offset + 0x10 + (target_subsong - 1) * 0x20;
|
||||||
|
|
||||||
|
/* IWAV entry supposedly contains more info but seems only offset and some ID at 0x14, rest is 0 */
|
||||||
|
ssw2_offset = read_u32be(offset + 0x00,sf) + bwav_offset;
|
||||||
|
if (is_id32be(ssw2_offset,sf, "SSWF")) {
|
||||||
|
/*
|
||||||
|
04 kType (always 0x01)
|
||||||
|
05 nChannels
|
||||||
|
06 freq
|
||||||
|
08 lpStart
|
||||||
|
0C nSamples
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* data is some unknown codec that seems to be ADPCM header + byte (simplified MTA2 with only 1 group?) */
|
||||||
|
vgm_logi("SSPF: unsupported SSWF variant at %x\n", ssw2_offset);
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
else if (is_id32be(ssw2_offset,sf, "SSW2")) {
|
||||||
|
stream_size = read_u32be(ssw2_offset + 0x04,sf);
|
||||||
|
/* 08 version? (always 0) */
|
||||||
|
num_samples = read_s32be(ssw2_offset + 0x0c,sf);
|
||||||
|
codec = read_u32be(ssw2_offset + 0x10,sf); /* kType (always 0x21) */
|
||||||
|
if (read_u32be(ssw2_offset + 0x10,sf) != 0x21)
|
||||||
|
goto fail;
|
||||||
|
if (read_u8(ssw2_offset + 0x14,sf) != 0x08) /* nBlocks? */
|
||||||
|
goto fail;
|
||||||
|
if (read_u8(ssw2_offset + 0x15,sf) != 0x01) /* nChannels? */
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
channels = 1;
|
||||||
|
sample_rate = read_u16be(ssw2_offset + 0x16,sf);
|
||||||
|
loop_start = read_s32be(ssw2_offset + 0x18,sf);
|
||||||
|
/* 0x1c: lpStartAddr (0xFFFFFFFF is none) */
|
||||||
|
|
||||||
|
loop_flag = loop_start != 0x7FFFFFFF;
|
||||||
|
start_offset = ssw2_offset + 0x20;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
vgm_logi("SSPF: unknown variant at %x\n", ssw2_offset);
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* build the VGMSTREAM */
|
||||||
|
vgmstream = allocate_vgmstream(channels, loop_flag);
|
||||||
|
if (!vgmstream) goto fail;
|
||||||
|
|
||||||
|
vgmstream->meta_type = meta_SSPF;
|
||||||
|
vgmstream->sample_rate = sample_rate;
|
||||||
|
vgmstream->num_samples = num_samples;
|
||||||
|
vgmstream->loop_start_sample = loop_start;
|
||||||
|
vgmstream->loop_end_sample = num_samples;
|
||||||
|
|
||||||
|
vgmstream->num_streams = total_subsongs;
|
||||||
|
vgmstream->stream_size = stream_size;
|
||||||
|
|
||||||
|
switch (codec) {
|
||||||
|
case 0x21:
|
||||||
|
vgmstream->coding_type = coding_MTA2;
|
||||||
|
vgmstream->codec_config = 1;
|
||||||
|
vgmstream->layout_type = layout_none;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
vgm_logi("SSPF: unknown codec %x\n", codec);
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!vgmstream_open_stream(vgmstream, sf, start_offset))
|
||||||
|
goto fail;
|
||||||
|
return vgmstream;
|
||||||
|
|
||||||
|
fail:
|
||||||
|
close_vgmstream(vgmstream);
|
||||||
|
return NULL;
|
||||||
|
}
|
@ -218,15 +218,12 @@ static void clean_txtp(txtp_header* txtp, int fail) {
|
|||||||
|
|
||||||
static int parse_silents(txtp_header* txtp) {
|
static int parse_silents(txtp_header* txtp) {
|
||||||
int i;
|
int i;
|
||||||
int channels = 0;
|
VGMSTREAM* v_base = NULL;
|
||||||
int sample_rate = 0;
|
|
||||||
int32_t num_samples = 0;
|
|
||||||
|
|
||||||
/* silents use same channels as close files */
|
/* silents use same channels as close files */
|
||||||
for (i = 0; i < txtp->vgmstream_count; i++) {
|
for (i = 0; i < txtp->vgmstream_count; i++) {
|
||||||
if (!txtp->entry[i].silent) {
|
if (!txtp->entry[i].silent) {
|
||||||
channels = txtp->vgmstream[i]->channels;
|
v_base = txtp->vgmstream[i];
|
||||||
sample_rate = txtp->vgmstream[i]->sample_rate;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -236,7 +233,7 @@ static int parse_silents(txtp_header* txtp) {
|
|||||||
if (!txtp->entry[i].silent)
|
if (!txtp->entry[i].silent)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
txtp->vgmstream[i] = init_vgmstream_silence(channels, sample_rate, num_samples);
|
txtp->vgmstream[i] = init_vgmstream_silence_base(v_base);
|
||||||
if (!txtp->vgmstream[i]) goto fail;
|
if (!txtp->vgmstream[i]) goto fail;
|
||||||
|
|
||||||
apply_settings(txtp->vgmstream[i], &txtp->entry[i]);
|
apply_settings(txtp->vgmstream[i], &txtp->entry[i]);
|
||||||
|
56
src/mixing.c
56
src/mixing.c
@ -1283,6 +1283,56 @@ void mixing_macro_downmix(VGMSTREAM* vgmstream, int max /*, mapping_t output_map
|
|||||||
|
|
||||||
/* ******************************************************************* */
|
/* ******************************************************************* */
|
||||||
|
|
||||||
|
static int fix_layered_channel_layout(VGMSTREAM* vgmstream) {
|
||||||
|
int i;
|
||||||
|
mixing_data* data = vgmstream->mixing_data;
|
||||||
|
layered_layout_data* layout_data;
|
||||||
|
uint32_t prev_cl;
|
||||||
|
|
||||||
|
if (vgmstream->channel_layout || vgmstream->layout_type != layout_layered)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
layout_data = vgmstream->layout_data;
|
||||||
|
|
||||||
|
/* mainly layer-v (in cases of layers-within-layers should cascade) */
|
||||||
|
if (data->output_channels != layout_data->layers[0]->channels)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* check all layers share layout (implicitly works as a channel check, if not 0) */
|
||||||
|
prev_cl = layout_data->layers[0]->channel_layout;
|
||||||
|
if (prev_cl == 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
for (i = 1; i < layout_data->layer_count; i++) {
|
||||||
|
uint32_t layer_cl = layout_data->layers[i]->channel_layout;
|
||||||
|
if (prev_cl != layer_cl)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
prev_cl = layer_cl;
|
||||||
|
}
|
||||||
|
|
||||||
|
vgmstream->channel_layout = prev_cl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* channel layout + down/upmixing = ?, salvage what we can */
|
||||||
|
static void fix_channel_layout(VGMSTREAM* vgmstream) {
|
||||||
|
mixing_data* data = vgmstream->mixing_data;
|
||||||
|
|
||||||
|
if (fix_layered_channel_layout(vgmstream))
|
||||||
|
goto done;
|
||||||
|
|
||||||
|
/* segments should share channel layout automatically */
|
||||||
|
|
||||||
|
/* a bit wonky but eh... */
|
||||||
|
if (vgmstream->channel_layout && vgmstream->channels != data->output_channels) {
|
||||||
|
vgmstream->channel_layout = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
done:
|
||||||
|
((VGMSTREAM*)vgmstream->start_vgmstream)->channel_layout = vgmstream->channel_layout;
|
||||||
|
}
|
||||||
|
|
||||||
void mixing_setup(VGMSTREAM* vgmstream, int32_t max_sample_count) {
|
void mixing_setup(VGMSTREAM* vgmstream, int32_t max_sample_count) {
|
||||||
mixing_data *data = vgmstream->mixing_data;
|
mixing_data *data = vgmstream->mixing_data;
|
||||||
float *mixbuf_re = NULL;
|
float *mixbuf_re = NULL;
|
||||||
@ -1300,11 +1350,7 @@ void mixing_setup(VGMSTREAM * vgmstream, int32_t max_sample_count) {
|
|||||||
data->mixbuf = mixbuf_re;
|
data->mixbuf = mixbuf_re;
|
||||||
data->mixing_on = 1;
|
data->mixing_on = 1;
|
||||||
|
|
||||||
/* a bit wonky but eh... */
|
fix_channel_layout(vgmstream);
|
||||||
if (vgmstream->channel_layout && vgmstream->channels != data->output_channels) {
|
|
||||||
vgmstream->channel_layout = 0;
|
|
||||||
((VGMSTREAM*)vgmstream->start_vgmstream)->channel_layout = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* since data exists on its own memory and pointer is already set
|
/* since data exists on its own memory and pointer is already set
|
||||||
* there is no need to propagate to start_vgmstream */
|
* there is no need to propagate to start_vgmstream */
|
||||||
|
@ -519,6 +519,8 @@ VGMSTREAM* (*init_vgmstream_functions[])(STREAMFILE* sf) = {
|
|||||||
init_vgmstream_wbk_nslb,
|
init_vgmstream_wbk_nslb,
|
||||||
init_vgmstream_dsp_apex,
|
init_vgmstream_dsp_apex,
|
||||||
init_vgmstream_ubi_ckd_cwav,
|
init_vgmstream_ubi_ckd_cwav,
|
||||||
|
init_vgmstream_sspf,
|
||||||
|
init_vgmstream_opus_rsnd,
|
||||||
|
|
||||||
/* lower priority metas (no clean header identity, somewhat ambiguous, or need extension/companion file to identify) */
|
/* lower priority metas (no clean header identity, somewhat ambiguous, or need extension/companion file to identify) */
|
||||||
init_vgmstream_mpeg,
|
init_vgmstream_mpeg,
|
||||||
|
@ -754,6 +754,7 @@ typedef enum {
|
|||||||
meta_WBK_NSLB,
|
meta_WBK_NSLB,
|
||||||
meta_DSP_APEX,
|
meta_DSP_APEX,
|
||||||
meta_MPEG,
|
meta_MPEG,
|
||||||
|
meta_SSPF,
|
||||||
|
|
||||||
} meta_t;
|
} meta_t;
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user