Updated VGMStream to r1050-1417-g60615e6d.

CQTexperiment
Christopher Snowhill 2018-07-19 20:19:14 -07:00
parent b3eb934a38
commit c1efdb0a6e
7 changed files with 309 additions and 52 deletions

View File

@ -19,6 +19,7 @@ static const char* extension_list[] = {
"aa3", //FFmpeg, not parsed (ATRAC3/ATRAC3PLUS/MP3/LPCM/WMA)
"aaap",
"aax",
"abk",
//"ac3", //FFmpeg, not parsed //common?
"ace", //fake, for tri-Ace's formats (to be removed)
"acm",
@ -145,6 +146,7 @@ static const char* extension_list[] = {
"h4m",
"hca",
"hdr",
"hgc1",
"his",
"hlwav",

View File

@ -23,9 +23,10 @@ void block_update_ea_schl(off_t block_offset, VGMSTREAM * vgmstream) {
{
uint32_t block_id = read_32bitBE(block_offset+0x00,streamFile);
block_size = read_32bitLE(block_offset+0x04,streamFile);
if (block_size > 0x00F00000) /* size is always LE, except in early SAT/MAC */
block_size = read_32bitBE(block_offset+0x04,streamFile);
if (guess_endianness32bit(block_offset + 0x04,streamFile)) /* size is always LE, except in early SS/MAC */
block_size = read_32bitBE(block_offset + 0x04,streamFile);
else
block_size = read_32bitLE(block_offset + 0x04,streamFile);
switch(block_id) {
case 0x5343446C: /* "SCDl" */

View File

@ -89,7 +89,7 @@ VGMSTREAM * init_vgmstream_ea_snu(STREAMFILE *streamFile) {
* 0x0c(4): some sub-offset? (0x20, found when @0x01 is set) */
/* use start_offset as endianness flag */
if ((uint32_t)read_32bitLE(0x08,streamFile) > 0x0000FFFF) {
if (guess_endianness32bit(0x08,streamFile)) {
read_32bit = read_32bitBE;
} else {
read_32bit = read_32bitLE;

View File

@ -72,20 +72,16 @@ typedef struct {
int codec_version;
} ea_header;
static VGMSTREAM * parse_schl_block(STREAMFILE *streamFile, off_t offset, int total_streams);
static VGMSTREAM * parse_bnk_header(STREAMFILE *streamFile, off_t offset, int target_stream, int total_streams);
static int parse_variable_header(STREAMFILE* streamFile, ea_header* ea, off_t begin_offset, int max_length);
static uint32_t read_patch(STREAMFILE* streamFile, off_t* offset);
static int get_ea_stream_total_samples(STREAMFILE* streamFile, off_t start_offset, VGMSTREAM* vgmstream);
static off_t get_ea_stream_mpeg_start_offset(STREAMFILE* streamFile, off_t start_offset, const ea_header* ea);
static VGMSTREAM * init_vgmstream_ea_variable_header(STREAMFILE *streamFile, ea_header *ea, off_t start_offset, int is_bnk, int total_streams);
/* EA SCHl with variable header - from EA games (roughly 1997~2010); generated by EA Canada's sx.exe/Sound eXchange */
VGMSTREAM * init_vgmstream_ea_schl(STREAMFILE *streamFile) {
off_t start_offset, header_offset;
size_t header_size;
ea_header ea = {0};
/* check extension; exts don't seem enforced by EA's tools, but usually:
* STR/ASF/MUS ~early, EAM ~mid, SNG/AUD ~late, rest uncommon/one game (ex. STRM: MySims Kingdom Wii) */
if (!check_extensions(streamFile,"str,asf,mus,eam,sng,aud,sx,strm,xa,xsf,exa,stm,ast,trj,trm"))
@ -105,19 +101,7 @@ VGMSTREAM * init_vgmstream_ea_schl(STREAMFILE *streamFile) {
/* Stream is divided into blocks/chunks: SCHl=audio header, SCCl=count of SCDl, SCDl=data xN, SCLl=loop end, SCEl=end.
* Video uses picture blocks (MVhd/MV0K/etc) and sometimes multiaudio blocks (SHxx/SCxx/SDxx/SExx where xx=language=EN/FR/GE/IT/SP/RU/JA).
* The number/size is affected by: block rate setting, sample rate, channels, CPU location (SPU/main/DSP/others), etc */
header_size = read_32bitLE(0x04,streamFile);
if (header_size > 0x00F00000) /* size is always LE, except in early SS/MAC */
header_size = read_32bitBE(0x04,streamFile);
header_offset = 0x08;
if (!parse_variable_header(streamFile,&ea, 0x08, header_size - header_offset))
goto fail;
start_offset = header_size; /* starts in "SCCl" (skipped in block layout) or very rarely "SCDl" and maybe movie blocks */
/* rest is common */
return init_vgmstream_ea_variable_header(streamFile, &ea, start_offset, 0, 1);
return parse_schl_block(streamFile, 0x00, 0);
fail:
return NULL;
@ -125,14 +109,7 @@ fail:
/* EA BNK with variable header - from EA games SFXs; also created by sx.exe */
VGMSTREAM * init_vgmstream_ea_bnk(STREAMFILE *streamFile) {
off_t start_offset, header_offset, offset, table_offset;
size_t header_size;
ea_header ea = {0};
int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL;
int16_t (*read_16bit)(off_t,STREAMFILE*) = NULL;
int i, bnk_version;
int total_streams, target_stream = streamFile->stream_index;
off_t offset;
/* check extension */
/* .bnk: sfx, .sdt: speech, .mus: streams/jingles (rare) */
@ -148,8 +125,243 @@ VGMSTREAM * init_vgmstream_ea_bnk(STREAMFILE *streamFile) {
else
goto fail;
return parse_bnk_header(streamFile, offset, streamFile->stream_index, 0);
fail:
return NULL;
}
/* EA ABK - contains embedded BNK file or references streams in AST file */
VGMSTREAM * init_vgmstream_ea_abk(STREAMFILE *streamFile) {
int bnk_target_stream, is_dupe, total_sounds = 0, target_stream = streamFile->stream_index;
off_t bnk_offset, header_table_offset, base_offset, value_offset, table_offset, entry_offset, target_entry_offset, schl_offset;
uint32_t i, j, k, num_sounds, total_sound_tables;
uint16_t num_tables;
uint8_t version, sound_type, num_entries;
off_t sound_table_offsets[0x2000];
STREAMFILE * astData = NULL;
VGMSTREAM * vgmstream;
int32_t (*read_32bit)(off_t,STREAMFILE*);
int16_t (*read_16bit)(off_t,STREAMFILE*);
/* check extension */
if (!check_extensions(streamFile, "abk"))
goto fail;
if (read_32bitBE(0x00, streamFile) != 0x41424B43) /* "ABKC" */
goto fail;
version = read_8bit(0x06, streamFile);
if (version > 0x01)
goto fail;
/* use table offset to check endianness */
if (guess_endianness32bit(0x1C,streamFile)) {
read_32bit = read_32bitBE;
read_16bit = read_16bitBE;
} else {
read_32bit = read_32bitLE;
read_16bit = read_16bitLE;
}
if (target_stream == 0) target_stream = 1;
if (target_stream < 0) goto fail;
num_tables = read_16bit(0x0A, streamFile);
header_table_offset = read_32bit(0x1C, streamFile);
bnk_offset = read_32bit(0x20, streamFile);
target_entry_offset = 0;
total_sound_tables = 0;
for (i = 0; i < num_tables; i++) {
num_entries = read_8bit(header_table_offset + 0x24, streamFile);
base_offset = read_32bit(header_table_offset + 0x2C, streamFile);
for (j = 0; j < num_entries; j++) {
value_offset = read_32bit(header_table_offset + 0x3C + 0x04 * j, streamFile);
table_offset = read_32bit(base_offset + value_offset + 0x04, streamFile);
/* For some reason, there are duplicate entries pointing at the same sound tables */
is_dupe = 0;
for (k = 0; k < total_sound_tables; k++)
{
if (table_offset==sound_table_offsets[k])
{
is_dupe = 1;
break;
}
}
if (is_dupe) continue;
sound_table_offsets[total_sound_tables++] = table_offset;
num_sounds = read_32bit(table_offset, streamFile);
for (k = 0; k < num_sounds; k++) {
entry_offset = table_offset + 0x04 + 0x0C * k;
sound_type = read_8bit(entry_offset, streamFile);
/* some of these dummies pointing at sound 0 in BNK */
if (sound_type == 0x00 && read_32bit(entry_offset + 0x04, streamFile) == 0)
continue;
total_sounds++;
if (target_stream == total_sounds)
target_entry_offset = entry_offset;
}
}
header_table_offset += 0x3C + num_entries * 0x04;
}
if (target_entry_offset == 0) goto fail;
/* 0x00: type (0x00 - normal, 0x01 - streamed, 0x02 - streamed and prefetched(?) */
/* 0x01: ??? */
/* 0x04: index for normal sounds, offset for streamed sounds */
/* 0x08: offset for prefetched sounds */
sound_type = read_8bit(target_entry_offset, streamFile);
switch (sound_type) {
case 0x00:
if (!bnk_offset) goto fail;
if (read_32bitBE(bnk_offset, streamFile) != 0x424E4B6C && /* "BNKl" */
read_32bitBE(bnk_offset,streamFile) != 0x424E4B62) /* BNKb */
goto fail;
bnk_target_stream = read_32bit(target_entry_offset + 0x04, streamFile) + 1;
vgmstream = parse_bnk_header(streamFile, bnk_offset, bnk_target_stream, total_sounds);
if (!vgmstream) goto fail;
break;
case 0x01:
case 0x02:
astData = open_streamfile_by_ext(streamFile, "ast");
if (!astData) goto fail;
if (sound_type == 0x01)
schl_offset = read_32bit(target_entry_offset + 0x04, streamFile);
else
schl_offset = read_32bit(target_entry_offset + 0x08, streamFile);
if (read_32bitBE(schl_offset, astData) != 0x5343486C) /* "SCHl */
goto fail;
vgmstream = parse_schl_block(astData, schl_offset, total_sounds);
if (!vgmstream) goto fail;
break;
default:
goto fail;
break;
}
close_streamfile(astData);
return vgmstream;
fail:
close_streamfile(astData);
return NULL;
}
/* EA HDR/DAT combo - frequently used for storing speech */
VGMSTREAM * init_vgmstream_ea_hdr(STREAMFILE *streamFile) {
int target_stream = streamFile->stream_index;
uint8_t userdata_size, total_sounds;
size_t total_size;
off_t schl_offset;
STREAMFILE *datFile = NULL, *sthFile = NULL;
VGMSTREAM *vgmstream;
int32_t (*read_32bit)(off_t,STREAMFILE*);
int16_t (*read_16bit)(off_t,STREAMFILE*);
/* No nice way to validate these so we do what we can */
sthFile = open_streamfile_by_ext(streamFile, "sth");
if (sthFile) goto fail; /* newer version using SNR/SNS */
datFile = open_streamfile_by_ext(streamFile, "dat");
if (!datFile) goto fail;
/* 0x00: hash */
/* 0x02: sub-ID (used for different police voices in NFS games) */
/* 0x04: userdata size (low nibble) */
/* 0x05: number of files */
/* 0x06: ??? */
/* 0x08: combined size of all sounds without padding divided by 0x0100 */
/* 0x0C: table start */
userdata_size = read_8bit(0x04, streamFile) & 0x0F;
total_sounds = read_8bit(0x05, streamFile);
if (target_stream == 0) target_stream = 1;
if (target_stream < 0 || total_sounds == 0 || target_stream > total_sounds) goto fail;
if (guess_endianness16bit(0x08,streamFile)) {
read_32bit = read_32bitBE;
read_16bit = read_16bitBE;
} else {
read_32bit = read_32bitLE;
read_16bit = read_16bitLE;
}
total_size = (size_t)read_16bit(0x08, streamFile) * 0x0100;
if (total_size > get_streamfile_size(datFile)) goto fail;
/* offsets are always big endian */
schl_offset = (off_t)read_16bitBE(0x0C + (0x02+userdata_size) * (target_stream-1), streamFile) * 0x0100;
if (read_32bitBE(schl_offset, datFile) != 0x5343486C) /* "SCHl */
goto fail;
vgmstream = parse_schl_block(datFile, schl_offset, total_sounds);
if (!vgmstream) goto fail;
close_streamfile(datFile);
return vgmstream;
fail:
close_streamfile(datFile);
close_streamfile(sthFile);
return NULL;
}
/* EA SCHl with variable header - from EA games (roughly 1997~2010); generated by EA Canada's sx.exe/Sound eXchange */
static VGMSTREAM * parse_schl_block(STREAMFILE *streamFile, off_t offset, int total_streams) {
off_t start_offset, header_offset;
size_t header_size;
ea_header ea = { 0 };
if (guess_endianness32bit(offset + 0x04, streamFile)) /* size is always LE, except in early SS/MAC */
header_size = read_32bitBE(offset + 0x04, streamFile);
else
header_size = read_32bitLE(offset + 0x04, streamFile);
header_offset = offset + 0x08;
if (!parse_variable_header(streamFile, &ea, header_offset, header_size))
goto fail;
start_offset = offset + header_size; /* starts in "SCCl" (skipped in block layout) or very rarely "SCDl" and maybe movie blocks */
/* rest is common */
return init_vgmstream_ea_variable_header(streamFile, &ea, start_offset, 0, total_streams);
fail:
return NULL;
}
/* EA BNK with variable header - from EA games SFXs; also created by sx.exe */
static VGMSTREAM * parse_bnk_header(STREAMFILE *streamFile, off_t offset, int target_stream, int total_streams) {
off_t header_offset, start_offset, test_offset, table_offset;
size_t header_size;
ea_header ea = {0};
int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL;
int16_t (*read_16bit)(off_t,STREAMFILE*) = NULL;
int i, bnk_version;
int total_bnk_sounds, real_bnk_sounds = 0;
/* use header size as endianness flag */
if ((uint32_t)read_32bitLE(0x08,streamFile) > 0x000F0000) { /* todo not very accurate */
if (guess_endianness32bit(offset + 0x08,streamFile)) {
read_32bit = read_32bitBE;
read_16bit = read_16bitBE;
} else {
@ -158,28 +370,20 @@ VGMSTREAM * init_vgmstream_ea_bnk(STREAMFILE *streamFile) {
}
bnk_version = read_8bit(offset + 0x04,streamFile);
total_streams = read_16bit(offset + 0x06,streamFile);
/* check multi-streams */
if (target_stream == 0) target_stream = 1;
if (target_stream < 0 || target_stream > total_streams || total_streams < 1) goto fail;
total_bnk_sounds = read_16bit(offset + 0x06,streamFile);
/* check multi-streams */
switch(bnk_version) {
case 0x02: /* early (Need For Speed PC, Fifa 98 SS) */
table_offset = 0x0c;
header_size = read_32bit(offset + 0x08,streamFile); /* full size */
header_offset = offset + table_offset + 0x04*(target_stream-1) + read_32bit(offset + table_offset + 0x04*(target_stream-1),streamFile);
break;
case 0x04: /* mid (last used in PSX banks) */
case 0x05: /* late (generated by sx.exe ~v2+) */
/* 0x08: header/file size, 0x0C: file size/null, 0x10: always null */
table_offset = 0x14;
if (read_32bit(offset + table_offset,streamFile) == 0x00)
table_offset += 0x4; /* MOH Heroes 2 PSP has an extra empty field, not sure why */
header_size = get_streamfile_size(streamFile); /* unknown (header is variable and may have be garbage until data) */
header_offset = offset + table_offset + 0x04*(target_stream-1) + read_32bit(offset + table_offset + 0x04*(target_stream-1),streamFile);
break;
default:
@ -187,13 +391,37 @@ VGMSTREAM * init_vgmstream_ea_bnk(STREAMFILE *streamFile) {
goto fail;
}
if (target_stream == 0) target_stream = 1;
header_offset = 0;
for (i = 0; i < total_bnk_sounds; i++) {
/* some of these are dummies with zero offset */
test_offset = read_32bit(offset + table_offset + 0x04 * i, streamFile);
if (test_offset != 0) {
real_bnk_sounds++;
/* ABK points at absolute indexes, i.e. with dummies included */
if (total_streams != 0) {
if (target_stream - 1 == i)
header_offset = offset + table_offset + 0x04 * i + test_offset;
}
else {
/* Ignore dummy streams when opening standalone BNK files */
if (target_stream == real_bnk_sounds)
header_offset = offset + table_offset + 0x04 * i + test_offset;
}
}
}
if (target_stream < 0 || header_offset == 0 || real_bnk_sounds < 1) goto fail;
if (!parse_variable_header(streamFile,&ea, header_offset, header_size - header_offset))
goto fail;
/* fix absolute offsets so it works in next funcs */
if (offset) {
for (i = 0; i < ea.channels; i++) {
ea.coefs[i] += offset;
ea.offsets[i] += offset;
}
}
@ -202,13 +430,13 @@ VGMSTREAM * init_vgmstream_ea_bnk(STREAMFILE *streamFile) {
/* special case found in some tests (pcstream had hist, pcbnk no hist, no patch diffs)
* I think this works but what decides if hist is used or not a secret to everybody */
if (ea.codec2 == EA_CODEC2_EAXA && ea.codec1 == EA_CODEC1_NONE && ea.version == EA_VERSION_V1) {
if (ea.codec2 == EA_CODEC2_EAXA && ea.codec1 == EA_CODEC1_NONE && ea.version >= EA_VERSION_V1) {
ea.codec_version = 0;
}
/* rest is common */
return init_vgmstream_ea_variable_header(streamFile, &ea, start_offset, bnk_version, total_streams);
return init_vgmstream_ea_variable_header(streamFile, &ea, start_offset, bnk_version, total_streams ? total_streams : real_bnk_sounds);
fail:
return NULL;
@ -262,8 +490,7 @@ static VGMSTREAM * init_vgmstream_ea_variable_header(STREAMFILE *streamFile, ea_
vgmstream->layout_type = layout_blocked_ea_schl;
}
if (is_bnk)
vgmstream->num_streams = total_streams;
vgmstream->num_streams = total_streams;
/* EA usually implements their codecs in all platforms (PS2/WII do EAXA/MT/EALAYER3) and
* favors them over platform's natives (ex. EAXA vs VAG/DSP).
@ -418,16 +645,17 @@ static VGMSTREAM * init_vgmstream_ea_variable_header(STREAMFILE *streamFile, ea_
}
}
else {
/* fix num_samples for streams with multiple SCHl */
int total_samples = get_ea_stream_total_samples(streamFile, start_offset, vgmstream);
if (total_samples > vgmstream->num_samples)
vgmstream->num_samples = total_samples;
if (total_streams == 0) {
/* HACK: fix num_samples for streams with multiple SCHl. Need to eventually get rid of this */
int total_samples = get_ea_stream_total_samples(streamFile, start_offset, vgmstream);
if (total_samples > vgmstream->num_samples)
vgmstream->num_samples = total_samples;
}
/* setup first block to update offsets */
block_update_ea_schl(start_offset,vgmstream);
}
return vgmstream;
fail:
@ -521,7 +749,16 @@ static int parse_variable_header(STREAMFILE* streamFile, ea_header* ea, off_t be
case 0x1A: /* unknown and very rare, size 0 (BNK only) [SSX3 (PS2)] */
case 0x1B: /* unknown (movie only?) */
case 0x1C: /* initial envelope volume (BNK only) */
case 0x1D: /* unknown, rare [NASCAR 06 (Xbox)] */
case 0x1E:
case 0x1F:
case 0x20:
case 0x21:
case 0x22:
case 0x23:
case 0x24: /* master random detune range (BNK only) */
case 0x25: /* unknown */
case 0x26: /* unknown, rare [FIFA 07 (Xbox)] */
read_patch(streamFile, &offset);
break;
@ -609,6 +846,8 @@ static int parse_variable_header(STREAMFILE* streamFile, ea_header* ea, off_t be
case 0x8A: /* long padding (always 0x00000000) */
case 0x8C: /* flags (ex. play type = 01=static/02=dynamic | spatialize = 20=pan/etc) */
/* (ex. PS1 VAG=0, PS2 PCM/LAYER2=4, GC EAXA=4, 3DS DSP=512, Xbox EAXA=36, N64 BLK=05E800, N64 MT10=01588805E800) */
case 0x8D: /* unknown, rare [FIFA 07 (GC)] */
case 0x8E:
case 0x92: /* bytes per sample? */
case 0x98: /* embedded time stretch 1 (long data for who-knows-what) */
case 0x99: /* embedded time stretch 2 */
@ -628,6 +867,7 @@ static int parse_variable_header(STREAMFILE* streamFile, ea_header* ea, off_t be
default:
VGM_LOG("EA SCHl: unknown patch 0x%02x at 0x%04lx\n", patch_type, (offset-1));
goto fail;
break;
}
}

View File

@ -660,6 +660,8 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_ea_schl(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_ea_bnk(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_ea_abk(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_ea_hdr(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_ea_schl_fixed(STREAMFILE * streamFile);

View File

@ -171,6 +171,16 @@ static inline int8_t read_8bit(off_t offset, STREAMFILE * streamfile) {
return buf[0];
}
/* guess byte endianness from a given value, return true if big endian and false if little endian */
/* TODO: possibly improve */
static inline int guess_endianness16bit(off_t offset, STREAMFILE * streamfile) {
return ((uint16_t)read_16bitLE(offset,streamfile) > (uint16_t)read_16bitBE(offset,streamfile)) ? 1 : 0;
}
static inline int guess_endianness32bit(off_t offset, STREAMFILE * streamfile) {
return ((uint32_t)read_32bitLE(offset,streamfile) > (uint32_t)read_32bitBE(offset,streamfile)) ? 1 : 0;
}
/* various STREAMFILE helpers functions */
size_t get_streamfile_text_line(int dst_length, char * dst, off_t offset, STREAMFILE * streamfile, int *line_done_ptr);

View File

@ -358,6 +358,8 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = {
init_vgmstream_pc_xa30,
init_vgmstream_wii_04sw,
init_vgmstream_ea_bnk,
init_vgmstream_ea_abk,
init_vgmstream_ea_hdr,
init_vgmstream_ea_schl_fixed,
init_vgmstream_sk_aud,
init_vgmstream_stm,