Updated VGMStream to r1702-76-g00bdb165

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
CQTexperiment
Christopher Snowhill 2022-02-21 16:44:18 -08:00
parent d70e1da126
commit 9fd85f6670
11 changed files with 151 additions and 93 deletions

View File

@ -48,7 +48,7 @@ size_t ms_ima_bytes_to_samples(size_t bytes, int block_align, int channels);
size_t xbox_ima_bytes_to_samples(size_t bytes, int channels);
size_t dat4_ima_bytes_to_samples(size_t bytes, int channels);
size_t apple_ima4_bytes_to_samples(size_t bytes, int channels);
int xbox_check_format(STREAMFILE* sf, uint32_t offset, uint32_t max, int channels);
/* ngc_dsp_decoder */
void decode_ngc_dsp(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
@ -430,6 +430,15 @@ void seek_vorbis_custom(VGMSTREAM* vgmstream, int32_t num_sample);
void free_vorbis_custom(vorbis_custom_codec_data* data);
#endif
typedef struct {
int version;
int layer;
int bit_rate;
int sample_rate;
int frame_samples;
int frame_size; /* bytes */
int channels;
} mpeg_frame_info;
#ifdef VGM_USE_MPEG
/* mpeg_decoder */
@ -482,16 +491,6 @@ void free_mpeg(mpeg_codec_data* data);
int mpeg_get_sample_rate(mpeg_codec_data* data);
long mpeg_bytes_to_samples(long bytes, const mpeg_codec_data* data);
typedef struct {
int version;
int layer;
int bit_rate;
int sample_rate;
int frame_samples;
int frame_size; /* bytes */
int channels;
} mpeg_frame_info;
int mpeg_get_frame_info(STREAMFILE* sf, off_t offset, mpeg_frame_info* info);
#endif

View File

@ -1326,3 +1326,27 @@ size_t apple_ima4_bytes_to_samples(size_t bytes, int channels) {
return (bytes / block_align) * (block_align - 0x02*channels) * 2 / channels
+ ((bytes % block_align) ? ((bytes % block_align) - 0x02*channels) * 2 / channels : 0);
}
/* test XBOX-ADPCM frames for correctness */
int xbox_check_format(STREAMFILE* sf, uint32_t offset, uint32_t max, int channels) {
off_t max_offset = offset + max;
int ch;
if (max_offset > get_streamfile_size(sf))
max_offset = get_streamfile_size(sf);
if (!channels)
return 0;
while (offset < max_offset) {
for (ch = 0; ch < channels; ch++) {
uint16_t step = read_u16le(offset + 0x04 * ch + 0x02,sf);
if (step > 88)
return 0;
}
offset += 0x24 * channels;
}
return 1;
}

View File

@ -192,7 +192,7 @@ void block_update_ea_schl(off_t block_offset, VGMSTREAM * vgmstream) {
for (i = 0; i < vgmstream->channels; i++) {
//vgmstream->ch[i].adpcm_history1_32 = read_16bit(vgmstream->ch[i].offset+0x00,streamFile);
//vgmstream->ch[i].adpcm_history3_32 = read_16bit(vgmstream->ch[i].offset+0x02,streamFile);
vgmstream->ch[i].offset += 4;
vgmstream->ch[i].offset += 0x04;
}
}

View File

@ -285,48 +285,49 @@ fail:
/* EA SBR/SBS - used in older 7th gen games for storing SFX */
VGMSTREAM* init_vgmstream_ea_sbr(STREAMFILE* sf) {
uint32_t i, num_sounds, type_desc;
uint16_t num_metas, meta_type;
off_t table_offset, types_offset, entry_offset, metas_offset, data_offset, snr_offset, sns_offset;
STREAMFILE *sbsFile = NULL;
uint32_t num_sounds, sound_id, type_desc, num_items, item_type,
table_offset, types_offset, entry_offset, items_offset, data_offset, snr_offset, sns_offset;
uint32_t i;
STREAMFILE *sf_sbs = NULL;
VGMSTREAM* vgmstream = NULL;
int target_stream = sf->stream_index;
if (!check_extensions(sf, "sbr"))
goto fail;
if (read_32bitBE(0x00, sf) != 0x53424B52) /* "SBKR" */
if (read_u32be(0x00, sf) != 0x53424B52) /* "SBKR" */
goto fail;
/* SBR files are always big endian */
num_sounds = read_32bitBE(0x1c, sf);
table_offset = read_32bitBE(0x24, sf);
types_offset = read_32bitBE(0x28, sf);
num_sounds = read_u32be(0x1c, sf);
table_offset = read_u32be(0x24, sf);
types_offset = read_u32be(0x28, sf);
if (target_stream == 0) target_stream = 1;
if (target_stream < 0 || num_sounds == 0 || target_stream > num_sounds)
goto fail;
entry_offset = table_offset + 0x0a * (target_stream - 1);
num_metas = read_16bitBE(entry_offset + 0x04, sf);
metas_offset = read_32bitBE(entry_offset + 0x06, sf);
sound_id = read_u32be(entry_offset + 0x00, sf);
num_items = read_u16be(entry_offset + 0x04, sf);
items_offset = read_u32be(entry_offset + 0x06, sf);
snr_offset = 0;
sns_offset = 0;
for (i = 0; i < num_metas; i++) {
entry_offset = metas_offset + 0x06 * i;
meta_type = read_16bitBE(entry_offset + 0x00, sf);
data_offset = read_32bitBE(entry_offset + 0x02, sf);
for (i = 0; i < num_items; i++) {
entry_offset = items_offset + 0x06 * i;
item_type = read_u16be(entry_offset + 0x00, sf);
data_offset = read_u32be(entry_offset + 0x02, sf);
type_desc = read_32bitBE(types_offset + 0x06 * meta_type, sf);
type_desc = read_u32be(types_offset + 0x06 * item_type, sf);
switch (type_desc) {
case 0x534E5231: /* "SNR1" */
snr_offset = data_offset;
break;
case 0x534E5331: /* "SNS1" */
sns_offset = read_32bitBE(data_offset, sf);
sns_offset = read_u32be(data_offset, sf);
break;
default:
break;
@ -336,43 +337,40 @@ VGMSTREAM* init_vgmstream_ea_sbr(STREAMFILE* sf) {
if (snr_offset == 0 && sns_offset == 0)
goto fail;
if (snr_offset == 0) {
/* SPS file */
sbsFile = open_streamfile_by_ext(sf, "sbs");
if (!sbsFile)
goto fail;
if (read_32bitBE(0x00, sbsFile) != 0x53424B53) /* "SBKS" */
goto fail;
vgmstream = init_vgmstream_eaaudiocore_header(sbsFile, NULL, sns_offset, 0x00, meta_EA_SPS, 0);
if (!vgmstream)
goto fail;
} else if (sns_offset == 0) {
if (sns_offset == 0) {
/* RAM asset */
vgmstream = init_vgmstream_eaaudiocore_header(sf, NULL, snr_offset, 0x00, meta_EA_SNR_SNS, 0);
if (!vgmstream)
goto fail;
meta_t meta_type = (read_u8(snr_offset, sf) == 0x48) ? meta_EA_SPS : meta_EA_SNR_SNS;
vgmstream = init_vgmstream_eaaudiocore_header(sf, NULL, snr_offset, 0x00, meta_type, 0);
if (!vgmstream) goto fail;
} else {
/* streamed asset */
sbsFile = open_streamfile_by_ext(sf, "sbs");
if (!sbsFile)
sf_sbs = open_streamfile_by_ext(sf, "sbs");
if (!sf_sbs) goto fail;
if (read_u32be(0x00, sf_sbs) != 0x53424B53) /* "SBKS" */
goto fail;
if (read_32bitBE(0x00, sbsFile) != 0x53424B53) /* "SBKS" */
goto fail;
if (read_u8(sns_offset, sf_sbs) == 0x48) {
/* SPS */
vgmstream = init_vgmstream_eaaudiocore_header(sf_sbs, NULL, sns_offset, 0x00, meta_EA_SPS, 0);
if (!vgmstream) goto fail;
} else {
/* SNR/SNS */
if (snr_offset == 0)
goto fail;
vgmstream = init_vgmstream_eaaudiocore_header(sf, sbsFile, snr_offset, sns_offset, meta_EA_SNR_SNS, 0);
if (!vgmstream)
goto fail;
vgmstream = init_vgmstream_eaaudiocore_header(sf, sf_sbs, snr_offset, sns_offset, meta_EA_SNR_SNS, 0);
if (!vgmstream) goto fail;
}
}
snprintf(vgmstream->stream_name, STREAM_NAME_SIZE, "%08x", sound_id);
vgmstream->num_streams = num_sounds;
close_streamfile(sbsFile);
close_streamfile(sf_sbs);
return vgmstream;
fail:
close_streamfile(sbsFile);
close_streamfile(sf_sbs);
return NULL;
}
@ -813,7 +811,7 @@ fail:
VGMSTREAM* init_vgmstream_ea_sbr_harmony(STREAMFILE* sf) {
uint64_t base_offset, sound_offset, offset, prev_offset;
uint32_t chunk_id, data_offset, table_offset, dset_offset, set_values, set_sounds, sound_table_offset;
int32_t flag;
int16_t flag;
uint16_t num_dsets;
uint8_t set_type, offset_size;
uint32_t i, j;
@ -1090,7 +1088,7 @@ static VGMSTREAM* init_vgmstream_eaaudiocore_header(STREAMFILE* sf_head, STREAMF
/* common channel configs are mono/stereo/quad/5.1/7.1 (from debug strings), while others are quite rare
* [Battlefield 4 (X360)-EAXMA: 3/5/7ch, Army of Two: The Devil's Cartel (PS3)-EALayer3v2P: 11ch] */
eaac.channels = eaac.channel_config + 1;
/* EA 6ch channel mapping is L C R BL BR LFE, but may use stereo layers for dynamic music
/* EA 6ch channel mapping is FL FC FR BL BR LFE, but may use stereo layers for dynamic music
* instead, so we can't re-map automatically (use TXTP) */
/* V0: SNR+SNS, V1: SPH+SPS (no apparent differences, other than block flags) */
@ -1111,8 +1109,8 @@ static VGMSTREAM* init_vgmstream_eaaudiocore_header(STREAMFILE* sf_head, STREAMF
goto fail;
}
if (eaac.version == EAAC_VERSION_V1 && eaac.type != EAAC_TYPE_STREAM) {
/* should never happen */
if (eaac.version == EAAC_VERSION_V1 && eaac.type == EAAC_TYPE_GIGASAMPLE) {
/* probably does not exist */
VGM_LOG("EA EAAC: bad stream type for version %x\n", eaac.version);
goto fail;
}

View File

@ -1483,7 +1483,7 @@ static VGMSTREAM* init_vgmstream_ea_variable_header(STREAMFILE* sf, ea_header* e
} else if (vgmstream->coding_type == coding_NGC_DSP && vgmstream->channels > 1 && ea->offsets[0] == ea->offsets[1]) {
/* pcstream+gcadpcm with sx.exe v2, not in flag_value, probably a bug (even with this parts of the wave are off) */
int interleave = (ea->num_samples / 14 * 8); /* full interleave */
for (i = 0; i < ea->channels; i++) {
for (i = 0; i < vgmstream->channels; i++) {
vgmstream->ch[i].offset = ea->offsets[0] + interleave*i;
}
} else if (ea->platform == EA_PLATFORM_PS2 && (ea->flag_value & 0x100)) {

View File

@ -112,15 +112,18 @@ VGMSTREAM* init_vgmstream_fsb(STREAMFILE* sf) {
fsb_header fsb = {0};
/* checks
* .fsb: standard
* .bnk: Hard Corps Uprising (PS3) */
if ( !check_extensions(sf, "fsb,bnk") )
/* checks */
if ((read_u32be(0x00,sf) & 0xFFFFFF00) != get_id32be("FSB\0"))
goto fail;
/* check header */
fsb.id = read_32bitBE(0x00,sf);
if (fsb.id == 0x46534231) { /* "FSB1" (somewhat different from other fsbs) */
/* .fsb: standard
* .bnk: Hard Corps Uprising (PS3)
* .sfx: Geon Cube (Wii) */
if ( !check_extensions(sf, "fsb,bnk,sfx") )
goto fail;
fsb.id = read_u32be(0x00,sf);
if (fsb.id == get_id32be("FSB1")) {
fsb.meta_type = meta_FSB1;
fsb.base_header_size = 0x10;
fsb.sample_header_min = 0x40;
@ -132,7 +135,8 @@ VGMSTREAM* init_vgmstream_fsb(STREAMFILE* sf) {
fsb.version = 0;
fsb.flags = 0;
if (fsb.total_subsongs > 1) goto fail;
if (fsb.total_subsongs > 1)
goto fail;
/* sample header (first stream only, not sure if there are multi-FSB1) */
{
@ -159,16 +163,16 @@ VGMSTREAM* init_vgmstream_fsb(STREAMFILE* sf) {
fsb.stream_offset = fsb.base_header_size + fsb.sample_headers_size;
}
}
else { /* other FSBs (common/extended format) */
if (fsb.id == 0x46534232) { /* "FSB2" */
else {
if (fsb.id == get_id32be("FSB2")) {
fsb.meta_type = meta_FSB2;
fsb.base_header_size = 0x10;
fsb.sample_header_min = 0x40; /* guessed */
} else if (fsb.id == 0x46534233) { /* "FSB3" */
} else if (fsb.id == get_id32be("FSB3")) {
fsb.meta_type = meta_FSB3;
fsb.base_header_size = 0x18;
fsb.sample_header_min = 0x40;
} else if (fsb.id == 0x46534234) { /* "FSB4" */
} else if (fsb.id == get_id32be("FSB4")) {
fsb.meta_type = meta_FSB4;
fsb.base_header_size = 0x30;
fsb.sample_header_min = 0x50;
@ -183,7 +187,7 @@ VGMSTREAM* init_vgmstream_fsb(STREAMFILE* sf) {
if (fsb.base_header_size > 0x10) {
fsb.version = read_32bitLE(0x10,sf);
fsb.flags = read_32bitLE(0x14,sf);
/* FSB4: 0x18(8):hash 0x20(10):guid */
/* FSB4: 0x18(8):hash, 0x20(10):guid */
} else {
fsb.version = 0;
fsb.flags = 0;

View File

@ -858,6 +858,8 @@ static const hcakey_info hcakey_list[] = {
{0x0a5d0fc8cc5c4502}, // Sng018
{0x198ea1a17416050b}, // Sng019
{0x2aa3b8abad207a1e}, // Sng020
{0x08ad2fe12c79bca9}, // Sng022
{0x18488992b1632ef5}, // Sng023
{0x1175edbbacc1fc18}, // Sng024
{0x0e14d06d7f7a6c8c}, // Sng025
{0x33d98a3a9f9bfdef}, // Sng026
@ -880,6 +882,9 @@ static const hcakey_info hcakey_list[] = {
// Shaman King: Funbari Chronicle (Android)
{1620612098671}, // 0000017954022A6F
// Heaven Burns Red (Android)
{7355875239102698567}, // 6615518E8ECED447
};
#endif/*_HCA_KEYS_H_*/

View File

@ -1,7 +1,6 @@
#include "meta.h"
#include "../coding/coding.h"
typedef struct {
int loop_flag;
int32_t loop_start;
@ -15,10 +14,10 @@ typedef struct {
/* KTAC - Koei Tecmo custom AAC [Kin'iro no Corda 3 (Vita), Shingeki no Kyojin: Shichi kara no Dasshutsu (3DS), Dynasty Warriors (PS4)] */
VGMSTREAM* init_vgmstream_ktac(STREAMFILE* sf) {
#ifdef VGM_USE_FFMPEG
VGMSTREAM* vgmstream = NULL;
ktac_header_t ktac = {0};
/* checks */
/* .ktac: header id */
if (!check_extensions(sf,"ktac"))
@ -53,7 +52,6 @@ VGMSTREAM* init_vgmstream_ktac(STREAMFILE* sf) {
if (ktac.type == 1)
goto fail;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(ktac.mp4.channels, ktac.loop_flag);
if (!vgmstream) goto fail;
@ -66,19 +64,14 @@ VGMSTREAM* init_vgmstream_ktac(STREAMFILE* sf) {
/* KTAC uses AAC, but not type found in .aac (that has headered frames, like mp3) but raw
* packets + frame size table (similar to .mp4/m4a). We set config for FFmpeg's fake M4A header */
#ifdef VGM_USE_FFMPEG
{
vgmstream->codec_data = init_ffmpeg_mp4_custom_std(sf, &ktac.mp4);
if (!vgmstream->codec_data) goto fail;
vgmstream->coding_type = coding_FFmpeg;
vgmstream->layout_type = layout_none;
}
#else
goto fail;
#endif
vgmstream->codec_data = init_ffmpeg_mp4_custom_std(sf, &ktac.mp4);
if (!vgmstream->codec_data) goto fail;
vgmstream->coding_type = coding_FFmpeg;
vgmstream->layout_type = layout_none;
return vgmstream;
fail:
close_vgmstream(vgmstream);
#endif
return NULL;
}

View File

@ -1,5 +1,6 @@
#include "meta.h"
#include "../coding/coding.h"
#include "../util/endianness.h"
typedef enum { MFX, MFX_BANK, SFX_BANK, SBNK, FORM } musx_form;
typedef enum { PSX, DSP, XBOX, IMA, DAT, NGCA, PCM } musx_codec;
@ -187,16 +188,24 @@ static int parse_musx_stream(STREAMFILE* sf, musx_header* musx) {
musx->platform = get_id32be("GC02"); /* (fake) */
}
else {
int channels = musx->channels;
off_t offset = musx->stream_offset;
size_t max = 0x5000;
if (max > musx->stream_size)
max = musx->stream_size;
if (!channels)
channels = 2;
/* since engine seems to hardcode codecs no apparent way to detect in some cases
* [Sphinx and the Cursed Mummy (multi), Buffy the Vampire Slayer: Chaos Bleeds (multi)] */
if (ps_check_format(sf, offset, max)) {
musx->platform = get_id32be("PS2_");
} else {
} else if (xbox_check_format(sf, offset, max, channels)) {
musx->platform = get_id32be("XB02"); /* (fake) */
}
else {
musx->platform = get_id32be("PC02"); /* (fake) */
}
}
}
@ -267,6 +276,12 @@ static int parse_musx_stream(STREAMFILE* sf, musx_header* musx) {
musx->codec = DAT;
break;
case 0x50433032: /* "PC02" */
default_channels = 2;
default_sample_rate = 32000;
musx->codec = IMA;
break;
default:
VGM_LOG("MUSX: unknown platform %x\n", musx->platform);
goto fail;
@ -307,7 +322,7 @@ static int parse_musx_stream(STREAMFILE* sf, musx_header* musx) {
}
/* other types (0x0a, 0x09) look like section/end markers, 0x06/07 only seems to exist once */
if (type == 0x06 || type == 0x07) {
if (type == 0x06 || type == 0x07) { /* loop / goto */
musx->loop_start = offset2;
musx->loop_end = offset1;
musx->loop_flag = 1;
@ -361,6 +376,8 @@ fail:
return 0;
}
//TODO: check possible info here:
// https://sphinxandthecursedmummy.fandom.com/wiki/SFX
static int parse_musx(STREAMFILE* sf, musx_header* musx) {
int32_t (*read_s32)(off_t,STREAMFILE*) = NULL;
uint32_t (*read_u32)(off_t,STREAMFILE*) = NULL;
@ -378,7 +395,7 @@ static int parse_musx(STREAMFILE* sf, musx_header* musx) {
case 1: /* Athens 2004 (PS2) */
musx->platform = 0; /* guess later */
musx->tables_offset = 0x10;
musx->big_endian = guess_endianness32bit(0x10, sf);
musx->big_endian = guess_endian32(0x10, sf);
musx->is_old = 1;
break;
@ -670,7 +687,7 @@ static int parse_musx(STREAMFILE* sf, musx_header* musx) {
data_offset = read_u32(musx->tables_offset+0x3c, sf);
target_offset = head_offset + (target_subsong - 1) * 0x1c;
;VGM_LOG("MUSX: ho=%lx, do=%lx, to=%lx\n", head_offset, data_offset, target_offset);
//;VGM_LOG("MUSX: ho=%lx, do=%lx, to=%lx\n", head_offset, data_offset, target_offset);
/* 0x00: subfile ID */
musx->num_samples = read_s32(target_offset + 0x04, sf);

View File

@ -18,12 +18,12 @@ VGMSTREAM* init_vgmstream_mic_koei(STREAMFILE* sf) {
if (start_offset != 0x800) goto fail;
sample_rate = read_u32le(0x04,sf);
channels = read_u32le(0x08,sf);
if (channels > 2) goto fail;
if (channels > 4) goto fail; /* 1/2/4 are known */
interleave = read_u32le(0x0c,sf);
if (interleave != 0x10) goto fail;
loop_end = read_32bitLE(0x10,sf);
loop_start = read_32bitLE(0x14,sf);
loop_end = read_s32le(0x10,sf);
loop_start = read_s32le(0x14,sf);
if (read_u32le(0x18,sf) != 0) goto fail;
if (read_u32le(0x1c,sf) != 0) goto fail;

View File

@ -1,5 +1,6 @@
#include "meta.h"
#include "../coding/coding.h"
//#include <ctype.h>
/* .WBK - seen in some Treyarch games [Spider-Man 2, Ultimate Spider-Man, Call of Duty 2: Big Red One] */
VGMSTREAM* init_vgmstream_wbk(STREAMFILE* sf) {
@ -154,6 +155,19 @@ fail:
return NULL;
}
/* Ultimate Spider-Man string hashing algorithm, for reference */
#if 0
static uint32_t wbk_hasher(const char* input) {
uint32_t hash = 0;
for (const char* ch = input; *ch; ch++) {
hash += hash*32 + tolower(*ch);
}
return hash;
}
#endif
/* .WBK - evolution of the above Treyarch bank format [Call of Duty 3] */
VGMSTREAM* init_vgmstream_wbk_nslb(STREAMFILE* sf) {
VGMSTREAM* vgmstream = NULL;
@ -291,14 +305,18 @@ VGMSTREAM* init_vgmstream_wbk_nslb(STREAMFILE* sf) {
off_t riff_fmt_offset, riff_data_offset;
size_t bytes, riff_fmt_size, riff_data_size;
sound_offset += 0x0c;
sound_size -= 0x0c;
/* find "fmt" chunk */
if (!find_chunk_riff_le(sf, 0x666d7420, sound_offset + 0x0c, sound_size - 0x0c, &riff_fmt_offset, &riff_fmt_size))
if (!find_chunk_riff_le(sf, 0x666d7420, sound_offset, sound_size, &riff_fmt_offset, &riff_fmt_size))
goto fail;
/* find "data" chunk */
if (!find_chunk_riff_le(sf, 0x64617461, sound_offset + 0x0c, sound_size - 0x0c, &riff_data_offset, &riff_data_size))
if (!find_chunk_riff_le(sf, 0x64617461, sound_offset, sound_size, &riff_data_offset, &riff_data_size))
goto fail;
sound_offset = riff_data_offset;
bytes = ffmpeg_make_riff_xma_from_fmt_chunk(buf, 0x100, riff_fmt_offset, riff_fmt_size, riff_data_size, sf, 0);
vgmstream->codec_data = init_ffmpeg_header_offset(sf, buf, bytes, riff_data_offset, riff_data_size);