Merge pull request #1592 from EdnessP/master

Sony BNK: 0x23 codec fixes and ZLSD support
This commit is contained in:
bnnm 2024-09-11 15:24:46 +02:00 committed by GitHub
commit 61b84a683e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -3,7 +3,7 @@
#include "../coding/coding.h" #include "../coding/coding.h"
#include "../util/endianness.h" #include "../util/endianness.h"
typedef enum { NONE, DUMMY, PSX, PCM16, MPEG, ATRAC9, HEVAG, RIFF_ATRAC9 } bnk_codec; typedef enum { NONE, DUMMY, EXTERNAL, PSX, PCM16, MPEG, ATRAC9, HEVAG, RIFF_ATRAC9, XVAG_ATRAC9 } bnk_codec;
typedef struct { typedef struct {
bnk_codec codec; bnk_codec codec;
@ -47,9 +47,6 @@ typedef struct {
uint32_t start_offset; uint32_t start_offset;
uint32_t stream_offset; uint32_t stream_offset;
uint32_t bank_name_offset;
uint32_t stream_name_offset;
uint32_t stream_name_size;
uint32_t stream_size; uint32_t stream_size;
uint32_t interleave; uint32_t interleave;
@ -69,10 +66,10 @@ VGMSTREAM* init_vgmstream_bnk_sony(STREAMFILE* sf) {
bnk_header_t h = {0}; bnk_header_t h = {0};
/* checks */ /* checks */
if (!parse_bnk_v3(sf, &h))
return NULL;
if (!check_extensions(sf, "bnk")) if (!check_extensions(sf, "bnk"))
return NULL; return NULL;
if (!parse_bnk_v3(sf, &h))
return NULL;
/* build the VGMSTREAM */ /* build the VGMSTREAM */
@ -85,15 +82,6 @@ VGMSTREAM* init_vgmstream_bnk_sony(STREAMFILE* sf) {
vgmstream->meta_type = meta_BNK_SONY; vgmstream->meta_type = meta_BNK_SONY;
if (h.stream_name_size >= STREAM_NAME_SIZE || h.stream_name_size <= 0)
h.stream_name_size = STREAM_NAME_SIZE;
/* replace this with reading into the buffer ASAP when processing tables? */
if (h.bank_name_offset)
read_string(h.bank_name, h.stream_name_size, h.bank_name_offset, sf);
if (h.stream_name_offset)
read_string(h.stream_name, h.stream_name_size, h.stream_name_offset, sf);
if (h.stream_name[0]) { if (h.stream_name[0]) {
get_streamfile_basename(sf, file_name, STREAM_NAME_SIZE); get_streamfile_basename(sf, file_name, STREAM_NAME_SIZE);
if (h.bank_name[0] && strcmp(file_name, h.bank_name) != 0) if (h.bank_name[0] && strcmp(file_name, h.bank_name) != 0)
@ -115,6 +103,39 @@ VGMSTREAM* init_vgmstream_bnk_sony(STREAMFILE* sf) {
return temp_vs; return temp_vs;
} }
case EXTERNAL: {
VGMSTREAM* temp_vs = NULL;
STREAMFILE* temp_sf = NULL;
/* try with both stream_name and bank_name/stream_name? */
temp_sf = open_streamfile_by_filename(sf, h.stream_name);
if (!temp_sf) { /* create dummy stream if it can't be found */
temp_vs = init_vgmstream_silence_container(h.total_subsongs);
if (!temp_vs) goto fail;
temp_vs->meta_type = vgmstream->meta_type;
snprintf(temp_vs->stream_name, STREAM_NAME_SIZE, "%s [not found]", vgmstream->stream_name);
close_vgmstream(vgmstream);
return temp_vs;
}
/* are external streams always xvag? it shouldn't be hardcoded like this, but... */
/* and at that point does this also need to be put behind #ifdef VGM_USE_ATRAC9? */
/* known BNK v12 externals use XVAG MPEG but it functions differently in general */
temp_vs = init_vgmstream_xvag(temp_sf);
close_streamfile(temp_sf);
if (!temp_vs) goto fail;
temp_vs->num_streams = vgmstream->num_streams;
temp_vs->meta_type = vgmstream->meta_type;
strcpy(temp_vs->stream_name, vgmstream->stream_name);
close_vgmstream(vgmstream);
return temp_vs;
}
#ifdef VGM_USE_ATRAC9 #ifdef VGM_USE_ATRAC9
case ATRAC9: { case ATRAC9: {
atrac9_config cfg = {0}; atrac9_config cfg = {0};
@ -154,6 +175,30 @@ VGMSTREAM* init_vgmstream_bnk_sony(STREAMFILE* sf) {
close_vgmstream(vgmstream); close_vgmstream(vgmstream);
return temp_vs; return temp_vs;
} }
case XVAG_ATRAC9: {
VGMSTREAM* temp_vs = NULL;
STREAMFILE* temp_sf = NULL;
temp_sf = setup_subfile_streamfile(sf, h.start_offset, h.stream_size, "xvag");
if (!temp_sf) goto fail;
temp_sf->stream_index = 1;
temp_vs = init_vgmstream_xvag(temp_sf);
close_streamfile(temp_sf);
if (!temp_vs) goto fail;
/* maybe also a separate warning/fail if XVAG returns more than 1 subsong? */
temp_vs->num_streams = vgmstream->num_streams;
//temp_vs->stream_size = vgmstream->stream_size;
temp_vs->meta_type = vgmstream->meta_type;
strcpy(temp_vs->stream_name, vgmstream->stream_name);
close_vgmstream(vgmstream);
return temp_vs;
}
#endif #endif
#ifdef VGM_USE_MPEG #ifdef VGM_USE_MPEG
case MPEG: { case MPEG: {
@ -396,9 +441,10 @@ static bool process_tables(STREAMFILE* sf, bnk_header_t* h) {
h->table2_suboffset = 0x00; h->table2_suboffset = 0x00;
break; break;
/* later version have a few more tables (some optional) and work slightly differently (header is part of wave) */ /* later versions have a few more tables (some optional) and work slightly differently (header is part of wave) */
case 0x1a: /* Demon's Souls (PS5) */ case 0x1a: /* Demon's Souls (PS5) */
case 0x23: { /* The Last of Us (PC) */ case 0x23: { /* The Last of Us (PC) */
uint32_t bank_name_offset = h->sblk_offset + (h->sblk_version <= 0x1a ? 0x1c : 0x20);
uint32_t tables_offset = h->sblk_offset + (h->sblk_version <= 0x1a ? 0x120 : 0x128); uint32_t tables_offset = h->sblk_offset + (h->sblk_version <= 0x1a ? 0x120 : 0x128);
uint32_t counts_offset = tables_offset + (h->sblk_version <= 0x1a ? 0x98 : 0xb0); uint32_t counts_offset = tables_offset + (h->sblk_version <= 0x1a ? 0x98 : 0xb0);
@ -408,6 +454,8 @@ static bool process_tables(STREAMFILE* sf, bnk_header_t* h) {
//h->sounds_entries = read_u16(counts_offset+0x00,sf); //h->sounds_entries = read_u16(counts_offset+0x00,sf);
//h->grains_entries = read_u16(counts_offset+0x02,sf); //h->grains_entries = read_u16(counts_offset+0x02,sf);
h->stream_entries = read_u16(counts_offset+0x06,sf); h->stream_entries = read_u16(counts_offset+0x06,sf);
read_string(h->bank_name, STREAM_NAME_SIZE, bank_name_offset, sf);
break; break;
} }
@ -493,7 +541,7 @@ static bool process_headers(STREAMFILE* sf, bnk_header_t* h) {
//;VGM_LOG("BNK: subsongs %i, table2_entry=%x, table3_entry=%x\n", h->total_subsongs, h->table2_entry_offset, h->table3_entry_offset); //;VGM_LOG("BNK: subsongs %i, table2_entry=%x, table3_entry=%x\n", h->total_subsongs, h->table2_entry_offset, h->table3_entry_offset);
if (h->target_subsong < 0 || h->target_subsong > h->total_subsongs || h->total_subsongs < 1) if (!h->zlsd_offset && (h->target_subsong < 0 || h->target_subsong > h->total_subsongs || h->total_subsongs < 1))
goto fail; goto fail;
/* this means some subsongs repeat streams, that can happen in some sfx banks, whatevs */ /* this means some subsongs repeat streams, that can happen in some sfx banks, whatevs */
if (h->total_subsongs != h->stream_entries) { if (h->total_subsongs != h->stream_entries) {
@ -503,6 +551,10 @@ static bool process_headers(STREAMFILE* sf, bnk_header_t* h) {
//;VGM_LOG("BNK: header entry at %x\n", h->table3_offset + h->table3_entry_offset); //;VGM_LOG("BNK: header entry at %x\n", h->table3_offset + h->table3_entry_offset);
/* is currently working on ZLSD streams */
if (h->zlsd_offset && h->target_subsong > h->total_subsongs)
return true;
sndh_offset = h->table3_offset + h->table3_entry_offset; sndh_offset = h->table3_offset + h->table3_entry_offset;
/* parse sounds */ /* parse sounds */
@ -607,10 +659,15 @@ static bool process_names(STREAMFILE* sf, bnk_header_t* h) {
if (h->table4_offset <= h->sblk_offset) if (h->table4_offset <= h->sblk_offset)
return true; return true;
/* is currently working on ZLSD streams */
if (h->zlsd_offset && h->target_subsong > h->total_subsongs)
return true;
int i; int i;
int table4_entry_id = -1; int table4_entry_id = -1;
uint32_t table4_entry_idx, table4_entries_offset, table4_names_offset; uint32_t table4_entry_idx, table4_entries_offset, table4_names_offset;
uint32_t entry_offset, entry_count; uint32_t entry_offset, entry_count;
uint32_t stream_name_offset;
switch (h->sblk_version) { switch (h->sblk_version) {
case 0x03: case 0x03:
@ -637,30 +694,30 @@ static bool process_names(STREAMFILE* sf, bnk_header_t* h) {
* and using that as the index for the chunk offsets * and using that as the index for the chunk offsets
* name_sect_offset + (chunk_idx[result] * 0x14); * name_sect_offset + (chunk_idx[result] * 0x14);
*/ */
if (read_u8(h->table4_offset, sf)) read_string(h->bank_name, STREAM_NAME_SIZE, h->table4_offset, sf);
h->bank_name_offset = h->table4_offset;
table4_entries_offset = h->table4_offset + 0x18; table4_entries_offset = h->table4_offset + 0x18;
table4_names_offset = h->table4_offset + read_u32(h->table4_offset + 0x08, sf); table4_names_offset = h->table4_offset + read_u32(h->table4_offset + 0x08, sf);
for (i = 0; i < 32; i++) { for (i = 0; i < 32; i++) {
table4_entry_idx = read_u16(table4_entries_offset + (i * 2), sf); table4_entry_idx = read_u16(table4_entries_offset + (i * 2), sf);
h->stream_name_offset = table4_names_offset + (table4_entry_idx * 0x14); stream_name_offset = table4_names_offset + (table4_entry_idx * 0x14);
/* searches the chunk until it finds the target name/index, or breaks at empty name */ /* searches the chunk until it finds the target name/index, or breaks at empty name */
while (read_u8(h->stream_name_offset, sf)) { while (read_u8(stream_name_offset, sf)) {
/* in case it goes somewhere out of bounds unexpectedly */ /* in case it goes somewhere out of bounds unexpectedly */
if (((read_u8(h->stream_name_offset + 0x00, sf) + read_u8(h->stream_name_offset + 0x04, sf) + if (((read_u8(stream_name_offset + 0x00, sf) + read_u8(stream_name_offset + 0x04, sf) +
read_u8(h->stream_name_offset + 0x08, sf) + read_u8(h->stream_name_offset + 0x0C, sf)) & 0x1F) != i) read_u8(stream_name_offset + 0x08, sf) + read_u8(stream_name_offset + 0x0C, sf)) & 0x1F) != i)
goto fail; goto fail;
if (read_u16(h->stream_name_offset + 0x10, sf) == table4_entry_id) if (read_u16(stream_name_offset + 0x10, sf) == table4_entry_id) {
read_string(h->stream_name, STREAM_NAME_SIZE, stream_name_offset, sf);
goto loop_break; /* to break out of the for+while loop simultaneously */ goto loop_break; /* to break out of the for+while loop simultaneously */
//break; //break;
h->stream_name_offset += 0x14; }
stream_name_offset += 0x14;
} }
} }
//goto fail; /* didn't find any valid index? */ //goto fail; /* didn't find any valid index? */
h->stream_name_offset = 0; loop_break:
loop_break:
break; break;
case 0x04: case 0x04:
@ -687,15 +744,15 @@ static bool process_names(STREAMFILE* sf, bnk_header_t* h) {
* 0x08: ? (2x int16) * 0x08: ? (2x int16)
* 0x0C: section index (int16) * 0x0C: section index (int16)
*/ */
if (read_u8(h->table4_offset, sf)) read_string(h->bank_name, STREAM_NAME_SIZE, h->table4_offset, sf);
h->bank_name_offset = h->table4_offset;
table4_entries_offset = h->table4_offset + read_u32(h->table4_offset + 0x08, sf); table4_entries_offset = h->table4_offset + read_u32(h->table4_offset + 0x08, sf);
table4_names_offset = h->table4_offset + read_u32(h->table4_offset + 0x0C, sf); table4_names_offset = h->table4_offset + read_u32(h->table4_offset + 0x0C, sf);
for (i = 0; i < h->sounds_entries; i++) { for (i = 0; i < h->sounds_entries; i++) {
if (read_u16(table4_entries_offset + (i * 0x10) + 0x0C, sf) == table4_entry_id) { if (read_u16(table4_entries_offset + (i * 0x10) + 0x0C, sf) == table4_entry_id) {
h->stream_name_offset = table4_names_offset + read_u32(table4_entries_offset + (i * 0x10), sf); stream_name_offset = table4_names_offset + read_u32(table4_entries_offset + (i * 0x10), sf);
read_string(h->stream_name, STREAM_NAME_SIZE, stream_name_offset, sf);
break; break;
} }
} }
@ -726,8 +783,7 @@ static bool process_names(STREAMFILE* sf, bnk_header_t* h) {
/* 0x0c: table4 size */ /* 0x0c: table4 size */
/* variable: entries */ /* variable: entries */
/* variable: names (null terminated) */ /* variable: names (null terminated) */
if (read_u8(h->table4_offset, sf)) read_string(h->bank_name, STREAM_NAME_SIZE, h->table4_offset, sf);
h->bank_name_offset = h->table4_offset;
table4_entries_offset = h->table4_offset + read_u32(h->table4_offset + 0x08, sf); table4_entries_offset = h->table4_offset + read_u32(h->table4_offset + 0x08, sf);
table4_names_offset = table4_entries_offset + (0x10 * h->sounds_entries); table4_names_offset = table4_entries_offset + (0x10 * h->sounds_entries);
@ -737,7 +793,8 @@ static bool process_names(STREAMFILE* sf, bnk_header_t* h) {
for (i = 0; i < h->sounds_entries; i++) { for (i = 0; i < h->sounds_entries; i++) {
int entry_id = read_u16(table4_entries_offset + (i * 0x10) + 0x0c, sf); int entry_id = read_u16(table4_entries_offset + (i * 0x10) + 0x0c, sf);
if (entry_id == table4_entry_id) { if (entry_id == table4_entry_id) {
h->stream_name_offset = table4_names_offset + read_u32(table4_entries_offset + (i * 0x10) + 0x00, sf); stream_name_offset = table4_names_offset + read_u32(table4_entries_offset + (i * 0x10) + 0x00, sf);
read_string(h->stream_name, STREAM_NAME_SIZE, stream_name_offset, sf);
break; break;
} }
} }
@ -761,8 +818,13 @@ static bool process_data(STREAMFILE* sf, bnk_header_t* h) {
read_s32_t read_s32 = h->big_endian ? read_s32be : read_s32le; read_s32_t read_s32 = h->big_endian ? read_s32be : read_s32le;
read_u64_t read_u64 = h->big_endian ? read_u64be : read_u64le; read_u64_t read_u64 = h->big_endian ? read_u64be : read_u64le;
/* is currently working on ZLSD streams */
if (h->zlsd_offset && h->target_subsong > h->total_subsongs)
return true;
int subtype, loop_length; int subtype, loop_length;
uint32_t extradata_size = 0, postdata_size = 0; uint32_t extradata_size = 0, postdata_size = 0;
uint32_t stream_name_size, stream_name_offset;
h->start_offset = h->data_offset + h->stream_offset; h->start_offset = h->data_offset + h->stream_offset;
uint32_t info_offset = h->start_offset; uint32_t info_offset = h->start_offset;
@ -907,7 +969,7 @@ static bool process_data(STREAMFILE* sf, bnk_header_t* h) {
case 0x0c: case 0x0c:
/* has two different variants under the same version - one for PS3 and another for PS4 */ /* has two different variants under the same version - one for PS3 and another for PS4 */
subtype = read_u32(h->start_offset + 0x00,sf); /* might be u16 at 0x02 instead? (implied by PS4's subtypes) */ subtype = read_u16(h->start_offset + 0x02, sf);
if (read_u32(h->start_offset + 0x04, sf) != 0x01) { /* type? */ if (read_u32(h->start_offset + 0x04, sf) != 0x01) { /* type? */
VGM_LOG("BNK: unknown subtype\n"); VGM_LOG("BNK: unknown subtype\n");
goto fail; goto fail;
@ -958,9 +1020,7 @@ static bool process_data(STREAMFILE* sf, bnk_header_t* h) {
} }
else { else {
switch (subtype) { /* PS4 */ switch (subtype) { /* PS4 */
/* if subtype is u16 @ 0x02, then 0x00 is PCM and 0x01 is VAG */ case 0x00: /* PCM */
case 0x00: /* PCM mono? */
case 0x01: /* PCM stereo? */
/* 0x10: null? */ /* 0x10: null? */
h->channels = read_u32(h->start_offset + 0x14, sf); h->channels = read_u32(h->start_offset + 0x14, sf);
h->interleave = 0x02; h->interleave = 0x02;
@ -972,7 +1032,7 @@ static bool process_data(STREAMFILE* sf, bnk_header_t* h) {
h->codec = PCM16; h->codec = PCM16;
break; break;
case 0x10000: /* PS-ADPCM (HEVAG?) */ case 0x01: /* PS-ADPCM (HEVAG?) */
/* 0x10: num samples */ /* 0x10: num samples */
h->channels = read_u32(h->start_offset + 0x14, sf); h->channels = read_u32(h->start_offset + 0x14, sf);
h->interleave = 0x10; h->interleave = 0x10;
@ -1065,30 +1125,61 @@ static bool process_data(STREAMFILE* sf, bnk_header_t* h) {
} }
/* pre-info */ /* pre-info */
h->stream_name_size = read_u64(info_offset+0x00,sf); stream_name_size = read_u64(info_offset+0x00,sf);
h->stream_name_offset = info_offset + 0x08; stream_name_offset = info_offset + 0x08;
info_offset += h->stream_name_size + 0x08; info_offset += stream_name_size + 0x08;
h->stream_size = read_u64(info_offset + 0x00,sf); /* after this offset */ h->stream_size = read_u64(info_offset + 0x00,sf); /* after this offset */
h->stream_size += 0x08 + h->stream_name_size + 0x08; h->stream_size += 0x08 + stream_name_size + 0x08;
/* 0x08: max block/etc size? (0x00010000/00030000) */ /* 0x08: 0/1 for PCM (Mono/Stereo?), 0/1/2/3 for ATRAC9 (channels/2)? */
/* 0x0c: always 1? */ subtype = read_u16(info_offset + 0x0a, sf);
extradata_size = read_u64(info_offset + 0x10,sf) + 0x08 + h->stream_name_size + 0x18; /* 0x0c: always 1 - using this to detect whether it's an SBlk or ZLSD/exteral sound for now */
extradata_size = read_u64(info_offset + 0x10,sf) + 0x08 + stream_name_size + 0x18;
if (stream_name_size >= STREAM_NAME_SIZE || stream_name_size <= 0)
stream_name_size = STREAM_NAME_SIZE;
read_string(h->stream_name, stream_name_size, stream_name_offset, sf);
if (read_u32(info_offset + 0x0c, sf) != 0x01) {
h->channels = 1;
h->codec = EXTERNAL;
break;
}
info_offset += 0x18; info_offset += 0x18;
/* actual stream info */ /* actual stream info */
/* 0x00: extradata size (without pre-info, also above) */ switch (subtype) {
h->atrac9_info = read_u32be(info_offset+0x04,sf); case 0x00: /* PCM */
h->num_samples = read_s32(info_offset+0x08,sf); h->num_samples = read_s32(info_offset + 0x00, sf);
h->channels = read_u32(info_offset+0x0c,sf); h->channels = read_u32(info_offset + 0x04, sf);
h->loop_start = read_s32(info_offset+0x10,sf); /* 0x08: loop flag? (always -1) */
h->loop_end = read_s32(info_offset+0x14,sf);
/* 0x18: loop flag (0=loop, -1=no) */ h->codec = PCM16;
/* rest: null */ break;
/* should be split, but 0x1A has no other known codecs yet */
case 0x01: /* ATRAC9 (0x23) */
case 0x03: /* ATRAC9 (0x1A) */
/* 0x00: extradata size (without pre-info, also above) */
h->atrac9_info = read_u32be(info_offset + 0x04, sf);
h->num_samples = read_s32(info_offset + 0x08, sf);
h->channels = read_u32(info_offset + 0x0c, sf);
h->loop_start = read_s32(info_offset + 0x10, sf);
h->loop_end = read_s32(info_offset + 0x14, sf);
/* 0x18: loop flag (0=loop, -1=no) */
/* rest: null */
h->codec = RIFF_ATRAC9;
break;
default:
vgm_logi("BNK: unknown subtype %x (report)\n", subtype);
goto fail;
}
/* no sample rate (probably fixed to 48000/system's, but seen in RIFF) */ /* no sample rate (probably fixed to 48000/system's, but seen in RIFF) */
h->sample_rate = 48000; h->sample_rate = 48000;
h->codec = RIFF_ATRAC9; /* unsure how other codecs would work */
break; break;
default: default:
@ -1106,36 +1197,75 @@ fail:
return false; return false;
} }
/* zlsd part: parse external stream prefetch data */
/* zlsd part: parse extra footer (vox?) data */
static bool process_zlsd(STREAMFILE* sf, bnk_header_t* h) { static bool process_zlsd(STREAMFILE* sf, bnk_header_t* h) {
if (!h->zlsd_offset) if (!h->zlsd_offset)
return true; return true;
/* TODO: ZLSD contains FNV1-32 hashes of the SBlk external streams,
* but with the way it's all currently set up, it isn't as simple to
* map appropriate hashes to existing SBlk streams. So for now these
* won't have a "proper" stream name visible.
*/
int zlsd_subsongs, target_subsong;
uint32_t zlsd_table_offset, zlsd_table_entry_offset, stream_offset, stream_name_hash;
read_u32_t read_u32 = h->big_endian ? read_u32be : read_u32le; read_u32_t read_u32 = h->big_endian ? read_u32be : read_u32le;
if (read_u32(h->zlsd_offset+0x00,sf) != get_id32be("DSLZ")) if (read_u32(h->zlsd_offset + 0x00, sf) != get_id32be("DSLZ"))
return false; return false;
/* 0x04: version? (1) */ /* 0x04: version? (1) */
int zlsd_count = read_u32(h->zlsd_offset+0x08,sf); zlsd_subsongs = read_u32(h->zlsd_offset + 0x08, sf);
/* 0x0c: start */ /* 0x0c: start (most of the time) */
/* 0x10: start if 64-bit zlsd_subsongs? seen in SBlk 0x1A */
zlsd_table_offset = read_u32(h->zlsd_offset + 0x0C, sf);
/* rest: null */ /* rest: null */
if (zlsd_count) { /* files can have both SBlk+ZLSD streams */
vgm_logi("BNK: unsupported ZLSD subsongs found\n"); if (zlsd_subsongs < 1) {
goto fail; if (h->total_subsongs < 1)
goto fail;
return true;
} }
/* per entry (for v23) if (!zlsd_table_offset)
* 00: crc (not referenced elsewhere) goto fail; /* 64-bit entries count? */
/* per entry (for SBlk v0x23)
* 00: fnv1-32 hash of the stream name
* 04: stream offset (from this offset) * 04: stream offset (from this offset)
* 08: null (part of offset?) * 08: null (part of offset?)
* 0c: stream size * 0c: stream size
* 10: offset/size? * 10: offset/size?
* 14: null */ * 14/18: null */
/* known streams are standard XVAG (no subsongs) */ /* known streams are standard XVAG (no subsongs) */
/* target_subsong is negative if it's working on SBlk streams */
target_subsong = h->target_subsong - h->total_subsongs - 1;
h->total_subsongs += zlsd_subsongs;
if (h->target_subsong < 0 || h->target_subsong > h->total_subsongs)
goto fail;
if (target_subsong < 0)
return true;
zlsd_table_entry_offset = h->zlsd_offset + zlsd_table_offset + target_subsong * 0x18;
h->start_offset = zlsd_table_entry_offset + 0x04 + read_u32(zlsd_table_entry_offset + 0x04, sf);
h->stream_size = read_u32(zlsd_table_entry_offset + 0x0C, sf);
stream_name_hash = read_u32(zlsd_table_entry_offset + 0x00, sf);
/* should be a switch case, but no other formats known yet */
if (!is_id32be(h->start_offset, sf, "XVAG")) {
vgm_logi("BNK: unsupported ZLSD subfile found (report)\n");
goto fail;
}
snprintf(h->stream_name, STREAM_NAME_SIZE, "%u [pre]", stream_name_hash);
h->channels = 1; /* dummy, real channels will be retrieved from xvag/riff */
h->codec = XVAG_ATRAC9;
return true; return true;
fail: fail:
return false; return false;
@ -1145,11 +1275,11 @@ fail:
/* parse SCREAM bnk (usually SFX but also used for music) */ /* parse SCREAM bnk (usually SFX but also used for music) */
static bool parse_bnk_v3(STREAMFILE* sf, bnk_header_t* h) { static bool parse_bnk_v3(STREAMFILE* sf, bnk_header_t* h) {
/* bnk/SCREAM tool version (v2 is a bit different, not seen v1) */ /* bnk/SCREAM tool version (v2 is a bit different, not seen v1) */
if (read_u32be(0x00,sf) == 0x03) { /* PS3 */ if (read_u32be(0x00,sf) == 0x03) { /* PS3 */
h->big_endian = 1; h->big_endian = 1;
} }
else if (read_u32le(0x00,sf) == 0x03) { /* PS2/PSP/Vita/PS4 */ else if (read_u32le(0x00,sf) == 0x03) { /* PS2/PSP/Vita/PS4/PS5 */
h->big_endian = 0; h->big_endian = 0;
} }
else { else {
@ -1163,7 +1293,7 @@ static bool parse_bnk_v3(STREAMFILE* sf, bnk_header_t* h) {
return false; return false;
/* in theory a bank can contain multiple blocks but only those are used */ /* in theory a bank can contain multiple blocks but only those are used */
/* section sizes don't include padding (sometimes aligned to 0x10/0x800) */ /* file is sometimes aligned to 0x10/0x800, so this can't be used for total size checks */
h->sblk_offset = read_u32(0x08,sf); h->sblk_offset = read_u32(0x08,sf);
//h->sblk_size = read_u32(0x0c,sf); //h->sblk_size = read_u32(0x0c,sf);
h->data_offset = read_u32(0x10,sf); h->data_offset = read_u32(0x10,sf);
@ -1195,12 +1325,12 @@ static bool parse_bnk_v3(STREAMFILE* sf, bnk_header_t* h) {
* - 0x10: block number * - 0x10: block number
* - 0x11: padding * - 0x11: padding
* version >= v0x1a: * version >= v0x1a:
* - 0x0c: hash (0x10) * - 0x0c: uuid (0x10)
* - 0x1c: filename (0x100?) * - 0x1c: bank name (0x100?)
* version ~= v0x23: * version ~= v0x23:
* - 0x0c: null (depends on flags? v1a=0x05, v23=0x07) * - 0x0c: null (depends on flags? v1a=0x05, v23=0x07)
* - 0x10: hash (0x10) * - 0x10: uuid (0x10)
* - 0x20: filename (0x100?) * - 0x20: bank name (0x100?)
*/ */
//;VGM_LOG("BNK: h->sblk_offset=%lx, h->data_offset=%lx, h->sblk_version %x\n", h->sblk_offset, h->data_offset, h->sblk_version); //;VGM_LOG("BNK: h->sblk_offset=%lx, h->data_offset=%lx, h->sblk_version %x\n", h->sblk_offset, h->data_offset, h->sblk_version);
//TODO handle, in rare cases may contain subsongs (unsure how are referenced but has its own number) //TODO handle, in rare cases may contain subsongs (unsure how are referenced but has its own number)