Updated VGMStream to r1745-33-gbb2a6624

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
swiftingly
Christopher Snowhill 2022-06-20 16:22:07 -07:00
parent 3153159658
commit 9dbe0a0d3e
9 changed files with 391 additions and 290 deletions

View File

@ -336,7 +336,7 @@ VGMSTREAM* allocate_segmented_vgmstream(segmented_layout_data* data, int loop_fl
if (!vgmstream) goto fail;
vgmstream->meta_type = data->segments[0]->meta_type;
vgmstream->sample_rate = data->segments[0]->sample_rate;
vgmstream->sample_rate = sample_rate;
vgmstream->num_samples = num_samples;
vgmstream->loop_start_sample = loop_start;
vgmstream->loop_end_sample = loop_end;

View File

@ -122,7 +122,7 @@ VGMSTREAM* init_vgmstream_ea_swvr(STREAMFILE* sf) {
goto fail;
coding = coding_PCM8_U_int;
channels = 1;
sample_rate = 16000; /* assumed */
sample_rate = 22050; /* assumed */
}
else {
//todo

View File

@ -86,6 +86,9 @@ static const uint8_t key_bbf[] = { 0x71,0x6A,0x76,0x6B,0x65,0x6F,0x71,0x6B,0x72,
/* Fall Guys (PC)-update */ //"p@4_ih*srN:UJk&8"
static const uint8_t key_fgs[] = { 0x70,0x40,0x34,0x5F,0x69,0x68,0x2A,0x73,0x72,0x4E,0x3A,0x55,0x4A,0x6B,0x26,0x38 };
/* Achilles: Legends Untold (PC) */ //"Achilles_0_15_DpG"
static const uint8_t key_alu[] = { 0x41,0x63,0x68,0x69,0x6C,0x6C,0x65,0x73,0x5F,0x30,0x5F,0x31,0x35,0x5F,0x44,0x70,0x47 };
// Unknown:
// - Battle: Los Angeles
// - Guitar Hero: Warriors of Rock, DJ hero FSB
@ -160,6 +163,7 @@ static const fsbkey_info fsbkey_list[] = {
{ 1,0, sizeof(key_wrb),key_wrb },// FSB5
{ 0,0, sizeof(key_bbf),key_bbf },// FSB4
{ 1,0, sizeof(key_fgs),key_fgs },// FSB5
{ 1,0, sizeof(key_alu),key_alu },// FSB5
};
static const int fsbkey_list_count = sizeof(fsbkey_list) / sizeof(fsbkey_list[0]);

View File

@ -1,6 +1,7 @@
#include "meta.h"
#include "../layout/layout.h"
#include "../coding/coding.h"
#include "../util/endianness.h"
#include "mul_streamfile.h"
typedef enum { PSX, DSP, IMA, XMA1, FSB4 } mul_codec;
@ -16,15 +17,15 @@ VGMSTREAM* init_vgmstream_mul(STREAMFILE* sf) {
int loop_flag, channel_count, sample_rate, num_samples, loop_start;
int big_endian;
mul_codec codec;
uint32_t (*read_u32)(off_t,STREAMFILE*) = NULL;
read_u32_t read_u32;
read_f32_t read_f32;
/* checks */
/* .mul: found in the exe, used by the bigfile extractor (Gibbed.TombRaider)
/* .mul: found in the exe/reversed names, used by the bigfile extractor (Gibbed.TombRaider)
* (some files have companion .mus/sam files but seem to be sequences/control stuff)
* .(extensionless): filenames as found in the bigfile
* .emff: fake extension ('Eidos Music File Format') */
if (!check_extensions(sf, "mul,,emff"))
if (!check_extensions(sf, "mul,emff"))
goto fail;
if (read_u32be(0x10,sf) != 0 ||
read_u32be(0x14,sf) != 0 ||
@ -34,6 +35,7 @@ VGMSTREAM* init_vgmstream_mul(STREAMFILE* sf) {
big_endian = guess_endianness32bit(0x00, sf);
read_u32 = big_endian ? read_u32be : read_u32le;
read_f32 = big_endian ? read_f32be : read_f32le;
sample_rate = read_u32(0x00,sf);
loop_start = read_u32(0x04,sf);
@ -46,13 +48,15 @@ VGMSTREAM* init_vgmstream_mul(STREAMFILE* sf) {
/* 0x28: loop offset within audio data (not file offset) */
/* 0x2c: some value related to loop? */
/* 0x34: id? */
/* 0x38+: channel config until ~0x100? (multiple 0x3F800000 / 1.0f depending on the number of channels) */
/* 0x38+: channel config until ~0x100? (multiple 1.0f depending on the number of channels) */
/* test known "version" (some float) later versions start from 0x24 instead of 0x20 */
if (!(read_u32(0x38,sf) == 0x3F800000 || /* common */
read_u32(0x38,sf) == 0x4530F000 || /* Avengers */
read_u32(0x3c,sf) == 0x3F800000)) /* Tomb Raider Underworld */
goto fail;
/* extra tests just in case (1.0=common, varies but goes around ~2800.0) */
{
float check1 = read_f32(0x38,sf);
float check2 = read_f32(0x3c,sf);
if (!(check1 >= 1.0 && check1 <= 3000.0) && check2 != 1.0)
goto fail;
}
loop_flag = (loop_start >= 0); /* 0xFFFFFFFF when not looping */
@ -115,13 +119,20 @@ fail:
return NULL;
}
/* find first block with header info (probably hardcoded) */
static off_t get_start_offset(STREAMFILE* sf) {
uint32_t test1, test2;
/* find first block with header info */
if (read_u32be(0x0800,sf) != 0 || read_u32be(0x0804,sf) != 0) /* earlier games */
/* earlier games */
test1 = read_u32be(0x0800,sf);
test2 = read_u32be(0x0804,sf);
if ((test1 != 0 && test1 != 0xFFFFFFFF) || (test2 != 0 && test2 != 0xFFFFFFFF))
return 0x800;
if (read_u32be(0x2000,sf) != 0 || read_u32be(0x2004,sf) != 0) /* later games */
/* later games */
test1 = read_u32be(0x2000,sf);
test2 = read_u32be(0x2004,sf);
if ((test1 != 0 && test1 != 0xFFFFFFFF) || (test2 != 0 && test2 != 0xFFFFFFFF))
return 0x2000;
return 0;
@ -167,13 +178,16 @@ static int guess_codec(STREAMFILE* sf, off_t start_offset, int big_endian, int c
uint32_t block_size = read_u32(offset+0x04, sf);
uint32_t data_size = read_u32(offset+0x10, sf);
if (block_type == 0xFFFFFFFF || block_size == 0xFFFFFFFF || data_size == 0xFFFFFFFF)
return 0;
if (block_type != 0x00) {
offset += 0x10 + block_size;
continue; /* not audio */
}
/* test FSB4 header */
if (read_u32be(offset + 0x10, sf) == 0x46534234 || read_u32be(offset + 0x20, sf) == 0x46534234) {
if (is_id32be(offset + 0x10, sf, "FSB4") || is_id32be(offset + 0x20, sf, "FSB4")) {
*p_codec = FSB4;
return 1;
}

View File

@ -452,6 +452,9 @@ VGMSTREAM* init_vgmstream_riff(STREAMFILE* sf) {
riff_size + 0x08 + 0x08 == file_size || riff_size + 0x08 + 0x09 == file_size ||
riff_size + 0x08 - 0x3E == file_size || riff_size + 0x08 - 0x02 == file_size))
ignore_riff_size = 1; /* [Cross Gate (PC)] (last info LIST chunk has wrong size) */
else if (codec == 0xFFFE && riff_size + 0x08 + 0x40 == file_size)
file_size -= 0x40; /* [Megami no Etsubo (PSP)] (has extra padding in all files) */
}
/* check for truncated RIFF */

View File

@ -1,239 +1,241 @@
#include "meta.h"
#include "../coding/coding.h"
/* SXD - Sony/SCE's SNDX lib format (cousin of SGXD) [Gravity Rush, Freedom Wars, Soul Sacrifice PSV] */
VGMSTREAM * init_vgmstream_sxd(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
STREAMFILE *streamHeader = NULL, *streamExternal = NULL, *streamData = NULL, *streamHead = NULL, *streamBody = NULL;
off_t start_offset, chunk_offset, first_offset = 0x60, name_offset = 0;
size_t chunk_size, stream_size = 0;
int is_dual, is_external;
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;
/* checks */
/* .sxd: header+data (SXDF)
* .sxd1: header (SXDF) + .sxd2 = data (SXDS)
* .sxd3: sxd1 + sxd2 pasted together (found in some PS4 games, ex. Fate Extella)*/
if (!check_extensions(streamFile,"sxd,sxd2,sxd3"))
goto fail;
/* setup head/body variations */
if (check_extensions(streamFile,"sxd2")) {
/* sxd1+sxd2: open sxd1 as header */
streamHead = open_streamfile_by_ext(streamFile, "sxd1");
if (!streamHead) goto fail;
streamHeader = streamHead;
streamExternal = streamFile;
is_dual = 1;
}
else if (check_extensions(streamFile,"sxd3")) {
/* sxd3: make subfiles for head and body to simplify parsing */
off_t sxd1_offset = 0x00;
size_t sxd1_size = read_32bitLE(0x08, streamFile);
off_t sxd2_offset = sxd1_size;
size_t sxd2_size = get_streamfile_size(streamFile) - sxd1_size;
streamHead = setup_subfile_streamfile(streamFile, sxd1_offset, sxd1_size, "sxd1");
if (!streamHead) goto fail;
streamBody = setup_subfile_streamfile(streamFile, sxd2_offset, sxd2_size, "sxd2");
if (!streamBody) goto fail;
streamHeader = streamHead;
streamExternal = streamBody;
is_dual = 1;
}
else {
/* sxd: use the current file as header */
streamHeader = streamFile;
streamExternal = NULL;
is_dual = 0;
}
if (streamHeader && read_32bitBE(0x00,streamHeader) != 0x53584446) /* "SXDF" */
goto fail;
if (streamExternal && read_32bitBE(0x00,streamExternal) != 0x53584453) /* "SXDS" */
goto fail;
/* 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)) /* "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 */
{
off_t table_offset, header_offset, stream_offset;
/* get target offset using table of relative offsets within WAVE */
table_offset = chunk_offset + 0x08 + 4*(target_subsong-1);
header_offset = table_offset + read_32bitLE(table_offset,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) */
num_samples = read_32bitLE(header_offset+0x14,streamHeader);
loop_start_sample = read_32bitLE(header_offset+0x18,streamHeader);
loop_end_sample = read_32bitLE(header_offset+0x1c,streamHeader);
stream_size = read_32bitLE(header_offset+0x20,streamHeader);
stream_offset = read_32bitLE(header_offset+0x24,streamHeader);
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;
if (!(flags & 1))
goto fail;
while (extra_offset < max_offset) {
uint32_t tag = read_32bitBE(extra_offset, streamHeader);
if (tag == 0x0A010000 || tag == 0x0A010600) {
at9_config_data = read_32bitLE(extra_offset+0x04,streamHeader); /* yes, LE */
break;
}
extra_offset += 0x04;
}
if (!at9_config_data)
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 {
start_offset = header_offset+0x24 + stream_offset; /* from current entry offset if internal */
}
}
/* get stream name (NAME is tied to REQD/cues, and SFX cues repeat WAVEs, but should work ok for streams) */
if (is_dual && find_chunk_le(streamHeader, 0x4E414D45,first_offset,0, &chunk_offset,NULL)) { /* "NAME" */
/* table: relative offset (32b) + hash? (32b) + cue index (32b) */
int i;
int num_entries = read_16bitLE(chunk_offset+0x04,streamHeader); /* can be bigger than streams */
for (i = 0; i < num_entries; i++) {
uint32_t index = (uint32_t)read_32bitLE(chunk_offset+0x08 + 0x08 + i*0x0c,streamHeader);
if (index+1 == target_subsong) {
name_offset = chunk_offset+0x08 + 0x00 + i*0x0c + read_32bitLE(chunk_offset+0x08 + 0x00 + i*0x0c,streamHeader);
break;
}
}
}
if (is_external && !is_dual) {
VGM_LOG("SXD: found single sxd with external data\n");
goto fail;
}
/* even dual files may have some non-external streams */
if (is_external) {
streamData = streamExternal;
} else {
streamData = streamHeader;
}
if (start_offset > get_streamfile_size(streamData)) {
VGM_LOG("SXD: wrong location?\n");
goto fail;
}
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channels,loop_flag);
if (!vgmstream) goto fail;
vgmstream->sample_rate = sample_rate;
vgmstream->num_samples = num_samples;
vgmstream->loop_start_sample = loop_start_sample;
vgmstream->loop_end_sample = loop_end_sample;
vgmstream->num_streams = total_subsongs;
vgmstream->stream_size = stream_size;
vgmstream->meta_type = meta_SXD;
if (name_offset)
read_string(vgmstream->stream_name,STREAM_NAME_SIZE, name_offset,streamHeader);
switch (codec) {
case 0x20: /* PS-ADPCM [Hot Shots Golf: World Invitational (Vita) sfx] */
vgmstream->coding_type = coding_PSX;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = 0x10;
break;
case 0x21: /* HEVAG [Gravity Rush (Vita) sfx] */
vgmstream->coding_type = coding_HEVAG;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = 0x10;
break;
#ifdef VGM_USE_ATRAC9
case 0x42: { /* ATRAC9 [Soul Sacrifice (Vita), Freedom Wars (Vita), Gravity Rush 2 (PS4)] */
atrac9_config cfg = {0};
cfg.channels = vgmstream->channels;
cfg.config_data = at9_config_data;
vgmstream->codec_data = init_atrac9(&cfg);
if (!vgmstream->codec_data) goto fail;
vgmstream->coding_type = coding_ATRAC9;
vgmstream->layout_type = layout_none;
break;
}
#endif
//case 0x28: /* dummy codec? (found with 0 samples) [Hot Shots Golf: World Invitational (Vita) sfx] */
default:
VGM_LOG("SXD: unknown codec 0x%x\n", codec);
goto fail;
}
/* open the file for reading */
if (!vgmstream_open_stream(vgmstream,streamData,start_offset))
goto fail;
if (streamHead) close_streamfile(streamHead);
if (streamBody) close_streamfile(streamBody);
return vgmstream;
fail:
if (streamHead) close_streamfile(streamHead);
if (streamBody) close_streamfile(streamBody);
close_vgmstream(vgmstream);
return NULL;
}
#include "meta.h"
#include "../coding/coding.h"
/* SXD - Sony/SCE's SNDX lib format (cousin of SGXD) [Gravity Rush, Freedom Wars, Soul Sacrifice PSV] */
VGMSTREAM* init_vgmstream_sxd(STREAMFILE* sf) {
VGMSTREAM* vgmstream = NULL;
STREAMFILE* sf_sxd1 = NULL, *sf_sxd2 = NULL, *sf_data = NULL, *sf_h = NULL, *sf_b = NULL;
off_t start_offset, chunk_offset, first_offset = 0x60, name_offset = 0;
size_t chunk_size, stream_size = 0;
int is_dual, is_external;
int loop_flag, channels, codec, sample_rate;
int32_t num_samples, loop_start_sample, loop_end_sample;
uint32_t flags, at9_config_data = 0;
int total_subsongs, target_subsong = sf->stream_index;
/* checks */
/* .sxd: header+data (SXDF)
* .sxd1: header (SXDF) + .sxd2 = data (SXDS)
* .sxd3: sxd1 + sxd2 pasted together (found in some PS4 games, ex. Fate Extella)*/
if (!check_extensions(sf,"sxd,sxd2,sxd3"))
goto fail;
/* setup head/body variations */
if (check_extensions(sf,"sxd2")) {
/* sxd1+sxd2: open sxd1 as header */
sf_h = open_streamfile_by_ext(sf, "sxd1");
if (!sf_h) goto fail;
sf_sxd1 = sf_h;
sf_sxd2 = sf;
is_dual = 1;
}
else if (check_extensions(sf,"sxd3")) {
/* sxd3: make subfiles for head and body to simplify parsing */
off_t sxd1_offset = 0x00;
size_t sxd1_size = read_u32le(0x08, sf);
off_t sxd2_offset = sxd1_size;
size_t sxd2_size = get_streamfile_size(sf) - sxd1_size;
sf_h = setup_subfile_streamfile(sf, sxd1_offset, sxd1_size, "sxd1");
if (!sf_h) goto fail;
sf_b = setup_subfile_streamfile(sf, sxd2_offset, sxd2_size, "sxd2");
if (!sf_b) goto fail;
sf_sxd1 = sf_h;
sf_sxd2 = sf_b;
is_dual = 1;
}
else {
/* sxd: use the current file as header */
sf_sxd1 = sf;
sf_sxd2 = NULL;
is_dual = 0;
}
if (sf_sxd1 && !is_id32be(0x00, sf_sxd1, "SXDF"))
goto fail;
if (sf_sxd2 && !is_id32be(0x00, sf_sxd2, "SXDS"))
goto fail;
/* typical chunks: NAME, WAVE and many control chunks (SXDs don't need to contain any sound data) */
if (!find_chunk_le(sf_sxd1, get_id32be("WAVE"),first_offset,0, &chunk_offset,&chunk_size))
goto fail;
/* check multi-streams (usually only in SFX containers) */
total_subsongs = read_s32le(chunk_offset + 0x04,sf_sxd1);
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 */
{
uint32_t table_offset, header_offset, stream_offset;
/* get target offset using table of relative offsets within WAVE */
table_offset = chunk_offset + 0x08 + 4 * (target_subsong - 1);
header_offset = table_offset + read_32bitLE(table_offset,sf_sxd1);
flags = read_u32le(header_offset+0x00,sf_sxd1);
codec = read_u8 (header_offset+0x04,sf_sxd1);
channels = read_u8 (header_offset+0x05,sf_sxd1);
/* 0x06(2): unknown, rarely 0xFF */
sample_rate = read_s32le(header_offset+0x08,sf_sxd1);
/* 0x0c(4): unknown size? (0x4000/0x3999/0x3333/etc, not related to extra data) */
/* 0x10(4): ? + volume? + pan? (can be 0 for music) */
num_samples = read_s32le(header_offset+0x14,sf_sxd1);
loop_start_sample = read_s32le(header_offset+0x18,sf_sxd1);
loop_end_sample = read_s32le(header_offset+0x1c,sf_sxd1);
stream_size = read_u32le(header_offset+0x20,sf_sxd1);
stream_offset = read_u32le(header_offset+0x24,sf_sxd1);
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;
if (!(flags & 1))
goto fail;
while (extra_offset < max_offset) {
uint32_t tag = read_u32be(extra_offset, sf_sxd1);
if (tag == 0x0A010000 || tag == 0x0A010600) {
at9_config_data = read_u32le(extra_offset+0x04, sf_sxd1); /* yes, LE */
break;
}
extra_offset += 0x04;
}
if (!at9_config_data)
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 {
start_offset = header_offset + 0x24 + stream_offset; /* from current entry offset if internal */
}
}
/* get stream name (NAME is tied to REQD/cues, and SFX cues repeat WAVEs, but should work ok for streams) */
if (is_dual && find_chunk_le(sf_sxd1, get_id32be("NAME"),first_offset,0, &chunk_offset,NULL)) {
/* table: relative offset (32b) + hash? (32b) + cue index (32b) */
int i;
int num_entries = read_s16le(chunk_offset + 0x04, sf_sxd1); /* can be bigger than streams */
for (i = 0; i < num_entries; i++) {
uint32_t index = read_u32le(chunk_offset + 0x08 + 0x08 + i * 0x0c,sf_sxd1);
if (index+1 == target_subsong) {
name_offset = chunk_offset + 0x08 + 0x00 + i*0x0c + read_u32le(chunk_offset + 0x08 + 0x00 + i * 0x0c, sf_sxd1);
break;
}
}
}
if (is_external && !is_dual) {
VGM_LOG("SXD: found single sxd with external data\n");
goto fail;
}
/* even dual files may have some non-external streams */
if (is_external) {
sf_data = sf_sxd2;
} else {
sf_data = sf_sxd1;
}
if (start_offset > get_streamfile_size(sf_data)) {
VGM_LOG("SXD: wrong location?\n");
goto fail;
}
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channels, loop_flag);
if (!vgmstream) goto fail;
vgmstream->meta_type = meta_SXD;
vgmstream->sample_rate = sample_rate;
vgmstream->num_samples = num_samples;
vgmstream->loop_start_sample = loop_start_sample;
vgmstream->loop_end_sample = loop_end_sample;
vgmstream->num_streams = total_subsongs;
vgmstream->stream_size = stream_size;
if (name_offset)
read_string(vgmstream->stream_name,STREAM_NAME_SIZE, name_offset,sf_sxd1);
switch (codec) {
case 0x20: /* PS-ADPCM [Hot Shots Golf: World Invitational (Vita) sfx] */
vgmstream->coding_type = coding_PSX;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = 0x10;
break;
case 0x21: /* HEVAG [Gravity Rush (Vita) sfx] */
vgmstream->coding_type = coding_HEVAG;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = 0x10;
break;
#ifdef VGM_USE_ATRAC9
case 0x42: { /* ATRAC9 [Soul Sacrifice (Vita), Freedom Wars (Vita), Gravity Rush 2 (PS4)] */
atrac9_config cfg = {0};
cfg.channels = vgmstream->channels;
cfg.config_data = at9_config_data;
vgmstream->codec_data = init_atrac9(&cfg);
if (!vgmstream->codec_data) goto fail;
vgmstream->coding_type = coding_ATRAC9;
vgmstream->layout_type = layout_none;
break;
}
#endif
//case 0x28: /* dummy codec? (found with 0 samples) [Hot Shots Golf: World Invitational (Vita) sfx] */
default:
VGM_LOG("SXD: unknown codec 0x%x\n", codec);
goto fail;
}
/* open the file for reading */
if (!vgmstream_open_stream(vgmstream, sf_data, start_offset))
goto fail;
if (sf_h) close_streamfile(sf_h);
if (sf_b) close_streamfile(sf_b);
return vgmstream;
fail:
if (sf_h) close_streamfile(sf_h);
if (sf_b) close_streamfile(sf_b);
close_vgmstream(vgmstream);
return NULL;
}

View File

@ -290,7 +290,7 @@ static int parse_entries(txtp_header* txtp, STREAMFILE* sf) {
else
temp_sf = open_streamfile_by_filename(sf, filename); /* from current path */
if (!temp_sf) {
VGM_LOG("TXTP: cannot open streamfile for %s\n", filename);
vgm_logi("TXTP: cannot open %s\n", filename);
goto fail;
}
temp_sf->stream_index = txtp->entry[i].subsong;
@ -298,7 +298,7 @@ static int parse_entries(txtp_header* txtp, STREAMFILE* sf) {
txtp->vgmstream[i] = init_vgmstream_from_STREAMFILE(temp_sf);
close_streamfile(temp_sf);
if (!txtp->vgmstream[i]) {
VGM_LOG("TXTP: cannot open vgmstream for %s#%i\n", filename, txtp->entry[i].subsong);
vgm_logi("TXTP: cannot parse %s#%i\n", filename, txtp->entry[i].subsong);
goto fail;
}

View File

@ -2,11 +2,13 @@
#include "meta.h"
#include "../layout/layout.h"
#include "../coding/coding.h"
#include "../util/endianness.h"
#include "ubi_sb_streamfile.h"
#define SB_MAX_LAYER_COUNT 16 /* arbitrary max */
#define SB_MAX_CHAIN_COUNT 256 /* +150 exist in Tonic Trouble */
#define SB_MAX_SUBSONGS 128000 /* arbitrary max to detect incorrect reads */
#define LAYER_HIJACK_GRAW_X360 1
#define LAYER_HIJACK_SCPT_PS2 2
@ -130,24 +132,26 @@ typedef struct {
int is_ps2_bnm;
int is_blk;
int has_numbered_banks;
int header_init;
STREAMFILE* sf_header;
uint32_t version; /* 16b+16b major+minor version */
uint32_t version_empty; /* map sbX versions are empty */
/* events (often share header_id/type with some descriptors,
* but may exist without headers or header exist without them) */
size_t section1_num;
uint32_t section1_num;
off_t section1_offset;
/* descriptors, audio header or other config types */
size_t section2_num;
uint32_t section2_num;
off_t section2_offset;
/* internal streams table, referenced by each header */
size_t section3_num;
uint32_t section3_num;
off_t section3_offset;
/* section with sounds in some map versions */
size_t section4_num;
uint32_t section4_num;
off_t section4_offset;
/* extra table, config for certain types (DSP coefs, external resources, layer headers, etc) */
size_t sectionX_size;
uint32_t sectionX_size;
off_t sectionX_offset;
/* sound bank size */
size_t bank_size;
@ -210,6 +214,7 @@ static VGMSTREAM* init_vgmstream_ubi_sb_header(ubi_sb_header* sb, STREAMFILE* sf
static VGMSTREAM *init_vgmstream_ubi_sb_silence(ubi_sb_header *sb);
static int config_sb_platform(ubi_sb_header* sb, STREAMFILE* sf);
static int config_sb_version(ubi_sb_header* sb, STREAMFILE* sf);
static int init_sb_header(ubi_sb_header* sb, STREAMFILE* sf);
/* .SBx - banks from Ubisoft's DARE (Digital Audio Rendering Engine) engine games in ~2000-2008+ */
@ -249,32 +254,9 @@ VGMSTREAM* init_vgmstream_ubi_sb(STREAMFILE* sf) {
if (!config_sb_version(&sb, sf))
goto fail;
if (!init_sb_header(&sb, sf))
goto fail;
if (sb.version <= 0x0000000B) {
sb.section1_num = read_32bit(0x04, sf);
sb.section2_num = read_32bit(0x0c, sf);
sb.section3_num = read_32bit(0x14, sf);
sb.sectionX_size = read_32bit(0x1c, sf);
sb.section1_offset = 0x20;
} else if (sb.version <= 0x000A0000) {
sb.section1_num = read_32bit(0x04, sf);
sb.section2_num = read_32bit(0x08, sf);
sb.section3_num = read_32bit(0x0c, sf);
sb.sectionX_size = read_32bit(0x10, sf);
sb.flag1 = read_32bit(0x14, sf);
sb.section1_offset = 0x18;
} else {
sb.section1_num = read_32bit(0x04, sf);
sb.section2_num = read_32bit(0x08, sf);
sb.section3_num = read_32bit(0x0c, sf);
sb.sectionX_size = read_32bit(0x10, sf);
sb.flag1 = read_32bit(0x14, sf);
sb.flag2 = read_32bit(0x18, sf);
sb.section1_offset = 0x1c;
}
if (sb.cfg.is_padded_section1_offset)
sb.section1_offset = align_size_to_block(sb.section1_offset, 0x10);
@ -2798,6 +2780,102 @@ static int check_project_file(STREAMFILE *sf_header, const char *name, int has_l
return 0;
}
/* Each entry in section1/2 has a type of 16b+16b group+sound identifier. May start from 0 (rarely) but
* always are low-ish numbers, so can be used to a point to detect if entries are correct with some entry_size. */
static int test_version_sb_entry(ubi_sb_header* sb, STREAMFILE* sf, uint32_t offset, int count, uint32_t entry_size) {
read_u32_t read_u32 = sb->big_endian ? read_u32be : read_u32le;
uint32_t prev_group = 0;
int i;
prev_group = 0;
for (i = 0; i < count; i++) {
uint32_t curr = read_u32(offset, sf);
uint16_t group, sound;
if (i > 1 && curr == 0)
return 0;
/* max seen in ~0x0200 */
group = (curr >> 16) & 0xFFFF;
sound = (curr >> 0) & 0xFFFF;
if (group > 0x1000 || sound > 0x1000)
return 0;
/* sounds aren't always ordered, but seems groups are */
if (prev_group && group < prev_group)
return 0;
prev_group = group;
offset += entry_size;
}
return 1;
}
/* Checks if matches entry sizes, for cases where same ID is reused. Only for SB fow now. */
static int test_version_sb(ubi_sb_header* sb, STREAMFILE* sf, uint32_t section1_size_entry, uint32_t section2_size_entry) {
uint32_t offset;
if (!init_sb_header(sb, sf))
return 0;
if (sb->section2_num == 0) /* no waves = no point to detect */
return 0;
offset = sb->section1_offset;
if (!test_version_sb_entry(sb, sf, offset, sb->section1_num, section1_size_entry))
return 0;
offset = sb->section1_offset + sb->section1_num * section1_size_entry;
if (!test_version_sb_entry(sb, sf, offset, sb->section2_num, section2_size_entry))
return 0;
return 1;
}
static int init_sb_header(ubi_sb_header* sb, STREAMFILE* sf) {
read_u32_t read_u32 = sb->big_endian ? read_u32be : read_u32le;
if (sb->header_init)
return 1;
if (sb->version <= 0x0000000B) {
sb->section1_num = read_u32(0x04, sf);
sb->section2_num = read_u32(0x0c, sf);
sb->section3_num = read_u32(0x14, sf);
sb->sectionX_size = read_u32(0x1c, sf);
sb->section1_offset = 0x20;
}
else if (sb->version <= 0x000A0000) {
sb->section1_num = read_u32(0x04, sf);
sb->section2_num = read_u32(0x08, sf);
sb->section3_num = read_u32(0x0c, sf);
sb->sectionX_size = read_u32(0x10, sf);
sb->flag1 = read_u32(0x14, sf);
sb->section1_offset = 0x18;
}
else {
sb->section1_num = read_u32(0x04, sf);
sb->section2_num = read_u32(0x08, sf);
sb->section3_num = read_u32(0x0c, sf);
sb->sectionX_size = read_u32(0x10, sf);
sb->flag1 = read_u32(0x14, sf);
sb->flag2 = read_u32(0x18, sf);
sb->section1_offset = 0x1c;
}
if (sb->section1_num > SB_MAX_SUBSONGS || sb->section2_num > SB_MAX_SUBSONGS || sb->section3_num > SB_MAX_SUBSONGS)
return 0;
sb->header_init = 1;
return 1;
}
static int config_sb_version(ubi_sb_header* sb, STREAMFILE* sf) {
int is_ttse_pc = 0;
int is_bia_ps2 = 0, is_biadd_psp = 0;
@ -3478,9 +3556,9 @@ static int config_sb_version(ubi_sb_header* sb, STREAMFILE* sf) {
return 1;
}
/* two configs with same id; use project file as identifier */
/* two configs with same id; try to autodetect or use project file as identifier */
if (sb->version == 0x00120006 && sb->platform == UBI_PC) {
if (check_project_file(sf, "gamesnd_myst4.sp0", 1)) {
if (test_version_sb(sb, sf, 0x6c, 0xa4) || check_project_file(sf, "gamesnd_myst4.sp0", 1)) {
is_myst4_pc = 1;
}
}

View File

@ -528,7 +528,6 @@ VGMSTREAM* (*init_vgmstream_functions[])(STREAMFILE* sf) = {
init_vgmstream_bw_riff_mp3,
/* lower priority metas (no clean header identity, somewhat ambiguous, or need extension/companion file to identify) */
init_vgmstream_mpeg,
init_vgmstream_agsc,
init_vgmstream_dtk,
init_vgmstream_rsf,
@ -543,6 +542,7 @@ VGMSTREAM* (*init_vgmstream_functions[])(STREAMFILE* sf) = {
/* lowest priority metas (should go after all metas, and TXTH should go before raw formats) */
init_vgmstream_txth, /* proper parsers should supersede TXTH, once added */
init_vgmstream_mpeg, /* semi-raw MP3 */
init_vgmstream_encrypted, /* encrypted stuff */
init_vgmstream_btsnd, /* semi-headerless */
init_vgmstream_raw_int, /* .int raw PCM */