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; if (!vgmstream) goto fail;
vgmstream->meta_type = data->segments[0]->meta_type; 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->num_samples = num_samples;
vgmstream->loop_start_sample = loop_start; vgmstream->loop_start_sample = loop_start;
vgmstream->loop_end_sample = loop_end; vgmstream->loop_end_sample = loop_end;

View File

@ -122,7 +122,7 @@ VGMSTREAM* init_vgmstream_ea_swvr(STREAMFILE* sf) {
goto fail; goto fail;
coding = coding_PCM8_U_int; coding = coding_PCM8_U_int;
channels = 1; channels = 1;
sample_rate = 16000; /* assumed */ sample_rate = 22050; /* assumed */
} }
else { else {
//todo //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" /* 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 }; 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: // Unknown:
// - Battle: Los Angeles // - Battle: Los Angeles
// - Guitar Hero: Warriors of Rock, DJ hero FSB // - 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 { 1,0, sizeof(key_wrb),key_wrb },// FSB5
{ 0,0, sizeof(key_bbf),key_bbf },// FSB4 { 0,0, sizeof(key_bbf),key_bbf },// FSB4
{ 1,0, sizeof(key_fgs),key_fgs },// FSB5 { 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]); static const int fsbkey_list_count = sizeof(fsbkey_list) / sizeof(fsbkey_list[0]);

View File

@ -1,6 +1,7 @@
#include "meta.h" #include "meta.h"
#include "../layout/layout.h" #include "../layout/layout.h"
#include "../coding/coding.h" #include "../coding/coding.h"
#include "../util/endianness.h"
#include "mul_streamfile.h" #include "mul_streamfile.h"
typedef enum { PSX, DSP, IMA, XMA1, FSB4 } mul_codec; 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 loop_flag, channel_count, sample_rate, num_samples, loop_start;
int big_endian; int big_endian;
mul_codec codec; mul_codec codec;
uint32_t (*read_u32)(off_t,STREAMFILE*) = NULL; read_u32_t read_u32;
read_f32_t read_f32;
/* checks */ /* 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) * (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') */ * .emff: fake extension ('Eidos Music File Format') */
if (!check_extensions(sf, "mul,,emff")) if (!check_extensions(sf, "mul,emff"))
goto fail; goto fail;
if (read_u32be(0x10,sf) != 0 || if (read_u32be(0x10,sf) != 0 ||
read_u32be(0x14,sf) != 0 || read_u32be(0x14,sf) != 0 ||
@ -34,6 +35,7 @@ VGMSTREAM* init_vgmstream_mul(STREAMFILE* sf) {
big_endian = guess_endianness32bit(0x00, sf); big_endian = guess_endianness32bit(0x00, sf);
read_u32 = big_endian ? read_u32be : read_u32le; read_u32 = big_endian ? read_u32be : read_u32le;
read_f32 = big_endian ? read_f32be : read_f32le;
sample_rate = read_u32(0x00,sf); sample_rate = read_u32(0x00,sf);
loop_start = read_u32(0x04,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) */ /* 0x28: loop offset within audio data (not file offset) */
/* 0x2c: some value related to loop? */ /* 0x2c: some value related to loop? */
/* 0x34: id? */ /* 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 */ /* extra tests just in case (1.0=common, varies but goes around ~2800.0) */
if (!(read_u32(0x38,sf) == 0x3F800000 || /* common */ {
read_u32(0x38,sf) == 0x4530F000 || /* Avengers */ float check1 = read_f32(0x38,sf);
read_u32(0x3c,sf) == 0x3F800000)) /* Tomb Raider Underworld */ float check2 = read_f32(0x3c,sf);
if (!(check1 >= 1.0 && check1 <= 3000.0) && check2 != 1.0)
goto fail; goto fail;
}
loop_flag = (loop_start >= 0); /* 0xFFFFFFFF when not looping */ loop_flag = (loop_start >= 0); /* 0xFFFFFFFF when not looping */
@ -115,13 +119,20 @@ fail:
return NULL; return NULL;
} }
/* find first block with header info (probably hardcoded) */
static off_t get_start_offset(STREAMFILE* sf) { static off_t get_start_offset(STREAMFILE* sf) {
uint32_t test1, test2;
/* find first block with header info */ /* earlier games */
if (read_u32be(0x0800,sf) != 0 || read_u32be(0x0804,sf) != 0) /* earlier games */ test1 = read_u32be(0x0800,sf);
test2 = read_u32be(0x0804,sf);
if ((test1 != 0 && test1 != 0xFFFFFFFF) || (test2 != 0 && test2 != 0xFFFFFFFF))
return 0x800; 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 0x2000;
return 0; 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 block_size = read_u32(offset+0x04, sf);
uint32_t data_size = read_u32(offset+0x10, 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) { if (block_type != 0x00) {
offset += 0x10 + block_size; offset += 0x10 + block_size;
continue; /* not audio */ continue; /* not audio */
} }
/* test FSB4 header */ /* 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; *p_codec = FSB4;
return 1; 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 + 0x08 == file_size || riff_size + 0x08 + 0x09 == file_size ||
riff_size + 0x08 - 0x3E == file_size || riff_size + 0x08 - 0x02 == 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) */ 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 */ /* check for truncated RIFF */

View File

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

View File

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

View File

@ -2,11 +2,13 @@
#include "meta.h" #include "meta.h"
#include "../layout/layout.h" #include "../layout/layout.h"
#include "../coding/coding.h" #include "../coding/coding.h"
#include "../util/endianness.h"
#include "ubi_sb_streamfile.h" #include "ubi_sb_streamfile.h"
#define SB_MAX_LAYER_COUNT 16 /* arbitrary max */ #define SB_MAX_LAYER_COUNT 16 /* arbitrary max */
#define SB_MAX_CHAIN_COUNT 256 /* +150 exist in Tonic Trouble */ #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_GRAW_X360 1
#define LAYER_HIJACK_SCPT_PS2 2 #define LAYER_HIJACK_SCPT_PS2 2
@ -130,24 +132,26 @@ typedef struct {
int is_ps2_bnm; int is_ps2_bnm;
int is_blk; int is_blk;
int has_numbered_banks; int has_numbered_banks;
int header_init;
STREAMFILE* sf_header; STREAMFILE* sf_header;
uint32_t version; /* 16b+16b major+minor version */ uint32_t version; /* 16b+16b major+minor version */
uint32_t version_empty; /* map sbX versions are empty */ uint32_t version_empty; /* map sbX versions are empty */
/* events (often share header_id/type with some descriptors, /* events (often share header_id/type with some descriptors,
* but may exist without headers or header exist without them) */ * but may exist without headers or header exist without them) */
size_t section1_num; uint32_t section1_num;
off_t section1_offset; off_t section1_offset;
/* descriptors, audio header or other config types */ /* descriptors, audio header or other config types */
size_t section2_num; uint32_t section2_num;
off_t section2_offset; off_t section2_offset;
/* internal streams table, referenced by each header */ /* internal streams table, referenced by each header */
size_t section3_num; uint32_t section3_num;
off_t section3_offset; off_t section3_offset;
/* section with sounds in some map versions */ /* section with sounds in some map versions */
size_t section4_num; uint32_t section4_num;
off_t section4_offset; off_t section4_offset;
/* extra table, config for certain types (DSP coefs, external resources, layer headers, etc) */ /* 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; off_t sectionX_offset;
/* sound bank size */ /* sound bank size */
size_t 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 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_platform(ubi_sb_header* sb, STREAMFILE* sf);
static int config_sb_version(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+ */ /* .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)) if (!config_sb_version(&sb, sf))
goto fail; 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) if (sb.cfg.is_padded_section1_offset)
sb.section1_offset = align_size_to_block(sb.section1_offset, 0x10); 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; 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) { static int config_sb_version(ubi_sb_header* sb, STREAMFILE* sf) {
int is_ttse_pc = 0; int is_ttse_pc = 0;
int is_bia_ps2 = 0, is_biadd_psp = 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; 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 (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; is_myst4_pc = 1;
} }
} }

View File

@ -528,7 +528,6 @@ VGMSTREAM* (*init_vgmstream_functions[])(STREAMFILE* sf) = {
init_vgmstream_bw_riff_mp3, init_vgmstream_bw_riff_mp3,
/* 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_agsc, init_vgmstream_agsc,
init_vgmstream_dtk, init_vgmstream_dtk,
init_vgmstream_rsf, 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) */ /* 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_txth, /* proper parsers should supersede TXTH, once added */
init_vgmstream_mpeg, /* semi-raw MP3 */
init_vgmstream_encrypted, /* encrypted stuff */ init_vgmstream_encrypted, /* encrypted stuff */
init_vgmstream_btsnd, /* semi-headerless */ init_vgmstream_btsnd, /* semi-headerless */
init_vgmstream_raw_int, /* .int raw PCM */ init_vgmstream_raw_int, /* .int raw PCM */