Merge pull request #625 from NicknineTheEagle/ubi

Ubisoft formats
This commit is contained in:
NicknineTheEagle 2020-05-23 03:36:40 +03:00 committed by GitHub
commit d3a7d41065
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 355 additions and 105 deletions

2
.gitignore vendored
View File

@ -40,6 +40,8 @@ Release
/xmplay/Release
/xmplay/*.dll
/dependencies
/version.h
/version.mk
/**/test.zip
/**/foo_input_vgmstream.fb2k-component

View File

@ -106,6 +106,7 @@ static const char* extension_list[] = {
"bik2",
//"bin", //common
"bk2",
"blk",
"bmdx",
"bms",
"bnk",

View File

@ -254,6 +254,9 @@
<ClInclude Include="meta\zsnd_streamfile.h">
<Filter>meta\Header Files</Filter>
</ClInclude>
<ClInclude Include="coding\g7221_decoder_lib.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="formats.c">

View File

@ -264,7 +264,6 @@ fail:
/* EA BNK with variable header - from EA games SFXs; also created by sx.exe */
VGMSTREAM * init_vgmstream_ea_bnk(STREAMFILE* sf) {
off_t offset;
int target_stream = sf->stream_index;
/* check extension */
@ -276,15 +275,8 @@ VGMSTREAM * init_vgmstream_ea_bnk(STREAMFILE* sf) {
if (!check_extensions(sf,"bnk,sdt,mus,abk,ast"))
goto fail;
/* check header (doesn't use EA blocks, otherwise very similar to SCHl) */
if (read_32bitBE(0x100,sf) == EA_BNK_HEADER_LE)
offset = 0x100; /* Harry Potter and the Goblet of Fire (PS2) .mus have weird extra 0x100 bytes */
else
offset = 0x00;
if (target_stream == 0) target_stream = 1;
return parse_bnk_header(sf, offset, target_stream - 1, 0);
return parse_bnk_header(sf, 0x00, target_stream - 1, 0);
fail:
return NULL;

View File

@ -655,6 +655,7 @@ VGMSTREAM * init_vgmstream_naac(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_ubi_sb(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_ubi_sm(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_ubi_bnm(STREAMFILE * streamFile);
VGMSTREAM *init_vgmstream_ubi_blk(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_ezw(STREAMFILE * streamFile);

View File

@ -1735,7 +1735,7 @@ static int config_bao_version(ubi_bao_header * bao, STREAMFILE *streamFile) {
case 0x001F0008: /* Rayman Raving Rabbids: TV Party (Wii)-package */
case 0x001F0010: /* Prince of Persia 2008 (PC/PS3/X360)-atomic-forge, Far Cry 2 (PS3)-atomic-dunia? */
case 0x001F0011: /* Naruto: The Broken Bond (X360)-package */
case 0x0021000C: /* Splinter Cell: Conviction (X360)(E3 2009 Demo)-package */
case 0x0021000C: /* Splinter Cell: Conviction (E3 2009 Demo)(X360)-package */
case 0x0022000D: /* Just Dance (Wii)-package */
case 0x0022001B: /* Prince of Persia: The Forgotten Sands (Wii)-package */
config_bao_entry(bao, 0xA4, 0x28); /* PC/Wii: 0xA8 */

View File

@ -18,6 +18,7 @@ typedef struct {
size_t section2_entry_size;
size_t section3_entry_size;
size_t resource_name_size;
size_t blk_table_size;
off_t audio_extra_offset;
off_t audio_stream_size;
@ -26,6 +27,8 @@ typedef struct {
off_t audio_group_id;
off_t audio_external_flag;
off_t audio_loop_flag;
off_t audio_loc_flag;
off_t audio_stereo_flag;
off_t audio_num_samples;
off_t audio_num_samples2;
off_t audio_sample_rate;
@ -36,6 +39,8 @@ typedef struct {
int audio_external_and;
int audio_loop_and;
int audio_group_and;
int audio_loc_and;
int audio_stereo_and;
int num_codec_flags;
int audio_has_internal_names;
size_t audio_interleave;
@ -59,7 +64,7 @@ typedef struct {
off_t layer_stream_type;
off_t layer_num_samples;
size_t layer_entry_size;
size_t layer_hijack;
int layer_hijack;
off_t silence_duration_int;
off_t silence_duration_float;
@ -80,6 +85,7 @@ typedef struct {
typedef struct {
ubi_sb_platform platform;
int is_ps2_old;
int is_psp_old;
int big_endian;
int total_subsongs;
@ -92,12 +98,12 @@ typedef struct {
/* map base header info */
off_t map_start;
off_t map_num;
uint32_t map_num;
uint32_t map_type;
uint32_t map_zero;
off_t map_offset;
off_t map_size;
size_t map_size;
char map_name[0x28];
uint32_t map_unknown;
@ -105,24 +111,26 @@ typedef struct {
int is_bank;
int is_map;
int is_bnm;
int is_blk;
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;
size_t section1_offset;
off_t section1_offset;
/* descriptors, audio header or other config types */
size_t section2_num;
size_t section2_offset;
off_t section2_offset;
/* internal streams table, referenced by each header */
size_t section3_num;
size_t section3_offset;
off_t section3_offset;
/* section with sounds in some map versions */
size_t section4_num;
size_t section4_offset;
off_t section4_offset;
/* extra table, config for certain types (DSP coefs, external resources, layer headers, etc) */
size_t sectionX_size;
size_t sectionX_offset;
off_t sectionX_offset;
/* unknown, usually -1 but can be others (0/1/2/etc) */
int flag1;
int flag2;
@ -139,6 +147,8 @@ typedef struct {
size_t stream_size; /* size of the audio data */
uint32_t stream_type; /* rough codec value */
uint32_t group_id; /* internal id to reference in section3 */
int is_localized;
int is_stereo;
int loop_flag; /* stream loops (normally internal sfx, but also external music) */
int loop_start; /* usually 0 */
@ -419,6 +429,7 @@ static int parse_bnm_header(ubi_sb_header * sb, STREAMFILE *streamFile) {
/* PLATFORM DETECTION */
sb->platform = UBI_PC;
sb->big_endian = 0;
read_32bit = sb->big_endian ? read_32bitBE : read_32bitLE;
/* SB HEADER */
@ -454,22 +465,144 @@ static int is_bnm_other_bank(STREAMFILE *streamFile, int bank_number) {
return strcmp(current_name, bank_name) != 0;
}
#if 0
/* .BLK - maps in separate .blk chunks [Donald Duck: Goin' Quackers (PS2), The Jungle Book Rhythm N'Groove (PS2)] */
VGMSTREAM * init_vgmstream_ubi_blk(STREAMFILE *streamFile) {
VGMSTREAM* vgmstream = NULL;
STREAMFILE *resData = NULL, *streamTest = NULL;
ubi_sb_header sb = { 0 };
int32_t(*read_32bit)(off_t, STREAMFILE*) = NULL;
int target_subsong = streamFile->stream_index;
/* Somewhat equivalent to a v0x00000003 map:
* - HEADER.BLK: base map header (slightly different?) + submaps headers
* - EVT.BLK: section1 from all submaps
* - RES.BLK: section2 + sectionX from all submaps
* - MAPS.BLK, MAPLANG.BLK: section3 variation?
* - STREAMED.BLK, STRLANG.BLK: audio data
* - HEADER.BLK: base map header + submaps headers
* - EVT.BLK: section1
* - RES.BLK: section2 + sectionX
* - MAPS.BLK, MAPLANG.BLK: section3's for each map
* - STREAMED.BLK, STRLANG.BLK: streamed sounds
*
* Parsing may be be simplified with multifile_streamfiles?
*
* The format is different from SMx in that there's a single sec1 and sec2
* shared by all maps so we can't determine which sounds belong to which map.
* Meanwhile, RAM sounds are stored in MAP.BLK/MAPLANG.BLK, which are split into blocks,
* one per map, which contain all RAM sounds that should be loaded for a given map.
* 0x00: version
* 0x04: number of maps
* 0x08: number of events (EVT.BLK)
* 0x0c: number of resources (RES.BLK)
* 0x10: flags?
* 0x14: size of extra section in RES.BLK
* for each map:
* 0x00: total size of common RAM sounds
* 0x04: total size of localized RAM sounds
* 0x08: offset of common sound table in MAP.BLK
* 0x0c: offset of localized sound table in MAPLANG.BLK
* 0x10: map name (0x20 bytes)
*/
/* checks */
if (!check_extensions(streamFile, "blk"))
goto fail;
/* only known to be used on PS2 */
sb.platform = UBI_PS2;
sb.big_endian = 0;
read_32bit = sb.big_endian ? read_32bitBE : read_32bitLE;
/* must open HEADER.BLK */
sb.is_blk = 1;
sb.version = read_32bit(0x00, streamFile) & 0x7FFFFFFF;
if (read_32bit(0x00, streamFile) & 0x80000000) {
sb.cfg.blk_table_size = 0x2000;
} else {
sb.cfg.blk_table_size = 0x1800;
}
if (sb.version != 0x00000003)
goto fail;
if (!config_sb_version(&sb, streamFile))
goto fail;
sb.sf_header = streamFile;
sb.map_num = read_32bit(0x04, streamFile);
sb.section1_num = read_32bit(0x08, streamFile);
sb.section1_offset = 0;
sb.section2_num = read_32bit(0x0c, streamFile);
sb.section2_offset = 0;
sb.sectionX_offset = sb.section2_num * sb.cfg.section2_entry_size;
sb.sectionX_size = read_32bit(0x14, streamFile);
/* ugh... */
resData = open_streamfile_by_filename(streamFile, "RES.BLK");
streamTest = reopen_streamfile(resData, 0x100);
if (target_subsong == 0) target_subsong = 1;
if (!parse_sb(&sb, streamTest, target_subsong))
goto fail;
/* CREATE VGMSTREAM */
vgmstream = init_vgmstream_ubi_sb_header(&sb, streamTest, resData);
close_streamfile(resData);
close_streamfile(streamTest);
return vgmstream;
fail:
close_streamfile(resData);
close_streamfile(streamTest);
return NULL;
}
#endif
static int blk_parse_offsets(ubi_sb_header *sb) {
STREAMFILE *sndData = NULL;
uint32_t i;
int32_t(*read_32bit)(off_t, STREAMFILE *) = sb->big_endian ? read_32bitBE : read_32bitLE;
/* correct offsets */
if (sb->is_external) {
sb->stream_offset *= sb->cfg.audio_interleave;
} else {
/* find the first map block which has this sound */
sndData = open_streamfile_by_filename(sb->sf_header, sb->resource_name);
for (i = 0; i < sb->map_num; i++) {
uint32_t entry_offset, cmn_table_offset, loc_table_offset, table_offset;
entry_offset = 0x18 + i * sb->cfg.map_entry_size;
cmn_table_offset = read_32bit(entry_offset + 0x08, sb->sf_header);
loc_table_offset = read_32bit(entry_offset + 0x0c, sb->sf_header);
table_offset = sb->is_localized ? loc_table_offset : cmn_table_offset;
sb->stream_offset = read_32bit(table_offset + sb->header_index * 0x04, sndData);
if (sb->stream_offset != 0xFFFFFFFF) {
sb->stream_offset += table_offset + sb->cfg.blk_table_size;
//sb->stream_size -= 0x04;
break;
}
}
close_streamfile(sndData);
if (sb->stream_offset == 0xFFFFFFFF) {
VGM_LOG("No map block contains resource %08x (%d)\n", sb->header_id, sb->header_index);
return 0;
}
}
return 1;
}
static void blk_get_resource_name(ubi_sb_header *sb) {
if (sb->is_external) {
if (sb->is_localized) {
strcpy(sb->resource_name, "STRLANG.BLK");
} else {
strcpy(sb->resource_name, "../STREAMED.BLK");
}
} else {
if (sb->is_localized) {
strcpy(sb->resource_name, "MAPLANG.BLK");
} else {
strcpy(sb->resource_name, "../MAP.BLK");
}
}
}
/* ************************************************************************* */
@ -780,7 +913,7 @@ static VGMSTREAM * init_vgmstream_ubi_sb_audio(ubi_sb_header *sb, STREAMFILE *st
STREAMFILE *streamData = NULL;
/* open external stream if needed */
if (sb->is_external) {
if (sb->is_external || sb->is_blk) {
streamData = open_streamfile_by_filename(streamFile,sb->resource_name);
if (streamData == NULL) {
VGM_LOG("UBI SB: external stream '%s' not found\n", sb->resource_name);
@ -797,11 +930,11 @@ static VGMSTREAM * init_vgmstream_ubi_sb_audio(ubi_sb_header *sb, STREAMFILE *st
if (!vgmstream) goto fail;
if (sb->is_external && streamData) close_streamfile(streamData);
if (streamData != streamFile) close_streamfile(streamData);
return vgmstream;
fail:
if (sb->is_external && streamData) close_streamfile(streamData);
if (streamData != streamFile) close_streamfile(streamData);
close_vgmstream(vgmstream);
return NULL;
}
@ -814,8 +947,13 @@ static VGMSTREAM * init_vgmstream_ubi_sb_layer(ubi_sb_header *sb, STREAMFILE *st
size_t full_stream_size = sb->stream_size;
int i, total_channels = 0;
if (sb->is_ps2_old) {
/* no blocked layout yet, just open as normal file */
return init_vgmstream_ubi_sb_audio(sb, streamTest, streamFile);
}
/* open external stream if needed */
if (sb->is_external) {
if (sb->is_external || sb->is_blk) {
streamData = open_streamfile_by_filename(streamFile,sb->resource_name);
if (streamData == NULL) {
VGM_LOG("UBI SB: external stream '%s' not found\n", sb->resource_name);
@ -869,12 +1007,12 @@ static VGMSTREAM * init_vgmstream_ubi_sb_layer(ubi_sb_header *sb, STREAMFILE *st
vgmstream->layout_type = layout_layered;
vgmstream->layout_data = data;
if (sb->is_external && streamData) close_streamfile(streamData);
if (streamData != streamFile) close_streamfile(streamData);
return vgmstream;
fail:
close_streamfile(temp_streamFile);
if (sb->is_external && streamData) close_streamfile(streamData);
if (streamData != streamFile) close_streamfile(streamData);
if (vgmstream)
close_vgmstream(vgmstream);
else
@ -1120,6 +1258,9 @@ static void build_readable_name(char * buf, size_t buf_size, ubi_sb_header * sb)
else
grp_name = "bnm";
}
else if (sb->is_blk) {
grp_name = "blk";
}
else {
grp_name = "bank";
}
@ -1145,7 +1286,7 @@ static void build_readable_name(char * buf, size_t buf_size, ubi_sb_header * sb)
}
}
else {
if (sb->is_external || sb->cfg.audio_has_internal_names)
if (sb->is_external || sb->cfg.audio_has_internal_names || sb->is_blk)
res_name = sb->resource_name;
else
res_name = NULL;
@ -1167,6 +1308,75 @@ static void build_readable_name(char * buf, size_t buf_size, ubi_sb_header * sb)
}
}
static int parse_type_audio_ps2_old(ubi_sb_header *sb, off_t offset, STREAMFILE *streamFile) {
int32_t(*read_32bit)(off_t, STREAMFILE *) = sb->big_endian ? read_32bitBE : read_32bitLE;
sb->stream_size = read_32bit(offset + sb->cfg.audio_stream_size, streamFile);
sb->stream_offset = read_32bit(offset + sb->cfg.audio_stream_offset, streamFile);
sb->sample_rate = read_32bit(offset + sb->cfg.audio_sample_rate, streamFile);
if (sb->stream_size == 0) {
VGM_LOG("UBI SB: bad stream size\n");
goto fail;
}
sb->is_external = read_32bit(offset + sb->cfg.audio_external_flag, streamFile) & sb->cfg.audio_external_and;
sb->loop_flag = read_32bit(offset + sb->cfg.audio_loop_flag, streamFile) & sb->cfg.audio_loop_and;
sb->is_localized = read_32bit(offset + sb->cfg.audio_loc_flag, streamFile) & sb->cfg.audio_loc_and;
sb->is_stereo = read_32bit(offset + sb->cfg.audio_stereo_flag, streamFile) & sb->cfg.audio_stereo_and;
sb->num_samples = 0; /* calculate from size */
sb->channels = sb->is_stereo ? 2 : 1;
sb->stream_size *= sb->channels;
sb->group_id = 0; /* TODO: verify, flag could exist */
/* filenames are hardcoded */
if (sb->is_blk) {
blk_get_resource_name(sb);
} else if (sb->is_external) {
strcpy(sb->resource_name, sb->is_localized ? "STRM.LM1" : "STRM.SM1");
}
return 1;
fail:
return 0;
}
static int parse_type_layer_ps2_old(ubi_sb_header *sb, off_t offset, STREAMFILE *streamFile) {
int32_t(*read_32bit)(off_t, STREAMFILE *) = sb->big_endian ? read_32bitBE : read_32bitLE;
sb->layer_count = read_32bit(offset + sb->cfg.layer_layer_count, streamFile);
sb->stream_size = read_32bit(offset + sb->cfg.layer_stream_size, streamFile);
sb->stream_offset = read_32bit(offset + sb->cfg.layer_stream_offset, streamFile);
if (sb->stream_size == 0) {
VGM_LOG("UBI SB: bad stream size\n");
goto fail;
}
if (sb->layer_count > SB_MAX_LAYER_COUNT) {
VGM_LOG("Ubi SB: incorrect layer count\n");
goto fail;
}
/* much simpler than later iteration */
sb->sample_rate = 44100; /* fixed? */
sb->num_samples = 0; /* calculate from size */
sb->channels = sb->layer_count * 2; /* layers are always stereo */
sb->stream_size *= sb->channels;
/* filenames are hardcoded */
if (sb->is_blk) {
blk_get_resource_name(sb);
} else if (sb->is_external) {
strcpy(sb->resource_name, sb->is_localized ? "STRM.LM1" : "STRM.SM1");
}
return 1;
fail:
return 0;
}
static int parse_type_audio(ubi_sb_header * sb, off_t offset, STREAMFILE* streamFile) {
int32_t (*read_32bit)(off_t,STREAMFILE*) = sb->big_endian ? read_32bitBE : read_32bitLE;
int16_t (*read_16bit)(off_t,STREAMFILE*) = sb->big_endian ? read_16bitBE : read_16bitLE;
@ -1174,6 +1384,9 @@ static int parse_type_audio(ubi_sb_header * sb, off_t offset, STREAMFILE* stream
/* audio header */
sb->type = UBI_AUDIO;
if (sb->is_ps2_old)
return parse_type_audio_ps2_old(sb, offset, streamFile);
sb->extra_offset = read_32bit(offset + sb->cfg.audio_extra_offset, streamFile) + sb->sectionX_offset;
sb->stream_size = read_32bit(offset + sb->cfg.audio_stream_size, streamFile);
sb->stream_offset = read_32bit(offset + sb->cfg.audio_stream_offset, streamFile);
@ -1188,9 +1401,7 @@ static int parse_type_audio(ubi_sb_header * sb, off_t offset, STREAMFILE* stream
goto fail;
}
if (sb->cfg.audio_external_flag && sb->cfg.audio_external_and) {
sb->is_external = (read_32bit(offset + sb->cfg.audio_external_flag, streamFile) & sb->cfg.audio_external_and);
}
sb->is_external = read_32bit(offset + sb->cfg.audio_external_flag, streamFile) & sb->cfg.audio_external_and;
if (sb->cfg.audio_group_id && sb->cfg.audio_group_and) {
/* probably means "SW decoded" */
@ -1206,9 +1417,7 @@ static int parse_type_audio(ubi_sb_header * sb, off_t offset, STREAMFILE* stream
sb->group_id = (int)!(sb->stream_type & 0x01);
}
if (sb->cfg.audio_loop_flag && sb->cfg.audio_loop_and) {
sb->loop_flag = (read_32bit(offset + sb->cfg.audio_loop_flag, streamFile) & sb->cfg.audio_loop_and);
}
sb->loop_flag = read_32bit(offset + sb->cfg.audio_loop_flag, streamFile) & sb->cfg.audio_loop_and;
if (sb->loop_flag) {
sb->loop_start = read_32bit(offset + sb->cfg.audio_num_samples, streamFile);
@ -1256,8 +1465,8 @@ static int parse_type_sequence(ubi_sb_header * sb, off_t offset, STREAMFILE* str
/* sequence chain */
sb->type = UBI_SEQUENCE;
if (sb->cfg.sequence_entry_size == 0) {
VGM_LOG("Ubi SB: sequence entry size not configured at %x\n", (uint32_t)offset);
if (sb->cfg.sequence_sequence_count == 0) {
VGM_LOG("Ubi SB: sequence not configured at %x\n", (uint32_t)offset);
goto fail;
}
@ -1318,11 +1527,17 @@ static int parse_type_layer(ubi_sb_header * sb, off_t offset, STREAMFILE* stream
/* layer header */
sb->type = UBI_LAYER;
if (sb->cfg.layer_entry_size == 0) {
VGM_LOG("Ubi SB: layer entry size not configured at %x\n", (uint32_t)offset);
if (sb->cfg.layer_layer_count == 0) {
VGM_LOG("Ubi SB: layers not configured at %x\n", (uint32_t)offset);
goto fail;
}
/* all layers seem external */
sb->is_external = 1;
if (sb->is_ps2_old)
return parse_type_layer_ps2_old(sb, offset, streamFile);
sb->extra_offset = read_32bit(offset + sb->cfg.layer_extra_offset, streamFile) + sb->sectionX_offset;
sb->layer_count = read_32bit(offset + sb->cfg.layer_layer_count, streamFile);
sb->stream_size = read_32bit(offset + sb->cfg.layer_stream_size, streamFile);
@ -1340,17 +1555,17 @@ static int parse_type_layer(ubi_sb_header * sb, off_t offset, STREAMFILE* stream
/* get 1st layer header in extra table and validate all headers match */
table_offset = sb->extra_offset;
//sb->channels = (sb->cfg.layer_channels % 4) ? /* non-aligned offset is always 16b */
// (uint16_t)read_16bit(table_offset + sb->cfg.layer_channels, streamFile) :
// (uint32_t)read_32bit(table_offset + sb->cfg.layer_channels, streamFile);
sb->sample_rate = read_32bit(table_offset + sb->cfg.layer_sample_rate, streamFile);
sb->stream_type = read_32bit(table_offset + sb->cfg.layer_stream_type, streamFile);
sb->num_samples = read_32bit(table_offset + sb->cfg.layer_num_samples, streamFile);
//sb->channels = (sb->cfg.layer_channels % 4) ? /* non-aligned offset is always 16b */
// (uint16_t)read_16bit(table_offset + sb->cfg.layer_channels, streamFile) :
// (uint32_t)read_32bit(table_offset + sb->cfg.layer_channels, streamFile);
sb->sample_rate = read_32bit(table_offset + sb->cfg.layer_sample_rate, streamFile);
sb->stream_type = read_32bit(table_offset + sb->cfg.layer_stream_type, streamFile);
sb->num_samples = read_32bit(table_offset + sb->cfg.layer_num_samples, streamFile);
for (i = 0; i < sb->layer_count; i++) {
int channels = (sb->cfg.layer_channels % 4) ? /* non-aligned offset is always 16b */
(uint16_t)read_16bit(table_offset + sb->cfg.layer_channels, streamFile) :
(uint32_t)read_32bit(table_offset + sb->cfg.layer_channels, streamFile);
int channels = (sb->cfg.layer_channels % 4) ? /* non-aligned offset is always 16b */
(uint16_t)read_16bit(table_offset + sb->cfg.layer_channels, streamFile) :
(uint32_t)read_32bit(table_offset + sb->cfg.layer_channels, streamFile);
int sample_rate = read_32bit(table_offset + sb->cfg.layer_sample_rate, streamFile);
int stream_type = read_32bit(table_offset + sb->cfg.layer_stream_type, streamFile);
int num_samples = read_32bit(table_offset + sb->cfg.layer_num_samples, streamFile);
@ -1372,13 +1587,10 @@ static int parse_type_layer(ubi_sb_header * sb, off_t offset, STREAMFILE* stream
table_offset += sb->cfg.layer_entry_size;
}
/* all layers seem external */
sb->is_external = 1;
/* external stream name can be found in the header (first versions) or the sectionX table (later versions) */
if (sb->cfg.layer_stream_name) {
read_string(sb->resource_name, sb->cfg.resource_name_size, offset + sb->cfg.layer_stream_name, streamFile);
} else {
} else if (sb->cfg.layer_extra_name) {
sb->cfg.layer_stream_name = read_32bit(offset + sb->cfg.layer_extra_name, streamFile);
if (sb->cfg.layer_stream_name != 0xFFFFFFFF)
read_string(sb->resource_name, sb->cfg.resource_name_size, sb->sectionX_offset + sb->cfg.layer_stream_name, streamFile);
@ -1473,11 +1685,17 @@ static int parse_stream_codec(ubi_sb_header * sb) {
if (sb->type == UBI_SEQUENCE)
return 1;
if (sb->is_ps2_old) {
/* early PS2 games don't support different codecs, it's always PSX ADPCM */
sb->codec = RAW_PSX;
return 1;
}
/* in early versions, this is a bitfield with either 1 or 2 rightmost bits being flags */
sb->stream_type >>= sb->cfg.num_codec_flags;
if (sb->cfg.default_codec_for_group0 && sb->type == UBI_AUDIO && sb->group_id == 0) {
/* early Xbox games contain garbage in stream_type field in this case, it seems that 0x00 is assumed */
/* some Xbox games contain garbage in stream_type field in this case, it seems that 0x00 is assumed */
sb->stream_type = 0x00;
}
@ -1560,6 +1778,9 @@ static int parse_stream_codec(ubi_sb_header * sb) {
case 0x02:
switch (sb->platform) {
case UBI_PC:
sb->codec = UBI_ADPCM;
break;
case UBI_PS3:
sb->codec = RAW_PSX; /* PS3 */
break;
@ -1567,8 +1788,8 @@ static int parse_stream_codec(ubi_sb_header * sb) {
sb->codec = UBI_IMA_SCE;
break;
default:
sb->codec = UBI_ADPCM;
break;
VGM_LOG("UBI SB: unknown codec for stream_type %02x\n", sb->stream_type);
goto fail;
}
break;
@ -1604,7 +1825,7 @@ static int parse_stream_codec(ubi_sb_header * sb) {
break;
case 0x08:
sb->codec = FMT_AT3;
sb->codec = FMT_AT3; /* PS3 */
break;
default:
@ -1626,6 +1847,9 @@ static int parse_offsets(ubi_sb_header * sb, STREAMFILE *streamFile) {
if (sb->type == UBI_SEQUENCE)
return 1;
if (sb->is_blk)
return blk_parse_offsets(sb);
VGM_ASSERT(!sb->is_map && sb->section3_num > 2, "UBI SB: section3 > 2 found\n");
if (sb->is_external)
@ -1661,7 +1885,7 @@ static int parse_offsets(ubi_sb_header * sb, STREAMFILE *streamFile) {
uint32_t table2_num = read_32bit(offset + 0x10, streamFile);
for (j = 0; j < table_num; j++) {
int index = read_32bit(table_offset + 0x08 * j + 0x00, streamFile) & 0x0000FFFF;
int index = read_32bit(table_offset + 0x08 * j + 0x00, streamFile) & 0x7FFFFFFF;
if (index == sb->header_index) {
sb->stream_offset = read_32bit(table_offset + 0x08 * j + 0x04, streamFile);
@ -1680,8 +1904,7 @@ static int parse_offsets(ubi_sb_header * sb, STREAMFILE *streamFile) {
if (sb->stream_offset)
break;
}
}
else {
} else {
/* banks store internal sounds after all headers and adjusted by the group table, find the matching entry */
off_t sounds_offset = sb->section3_offset + sb->cfg.section3_entry_size*sb->section3_num;
@ -1899,6 +2122,17 @@ static void config_sb_audio_fb(ubi_sb_header * sb, off_t flag_bits, int external
sb->cfg.audio_group_and = group_and;
sb->cfg.audio_loop_and = loop_and;
}
static void config_sb_audio_fb_ps2_old(ubi_sb_header* sb, off_t flag_bits, int external_and, int loop_and, int loc_and, int stereo_and) {
/* audio header with bit flags */
sb->cfg.audio_external_flag = flag_bits;
sb->cfg.audio_loop_flag = flag_bits;
sb->cfg.audio_loc_flag = flag_bits;
sb->cfg.audio_stereo_flag = flag_bits;
sb->cfg.audio_external_and = external_and;
sb->cfg.audio_loop_and = loop_and;
sb->cfg.audio_loc_and = loc_and;
sb->cfg.audio_stereo_and = stereo_and;
}
static void config_sb_audio_hs(ubi_sb_header * sb, off_t channels, off_t sample_rate, off_t num_samples, off_t num_samples2, off_t stream_name, off_t stream_type) {
/* audio header with stream name */
sb->cfg.audio_channels = channels;
@ -1928,6 +2162,10 @@ static void config_sb_sequence(ubi_sb_header * sb, off_t sequence_count, off_t e
sb->cfg.sequence_sequence_loop = sequence_count - 0x0c;
sb->cfg.sequence_sequence_single= sequence_count - 0x08;
}
if (sb->is_blk) {
sb->cfg.sequence_sequence_loop = sequence_count - 0x14;
sb->cfg.sequence_sequence_single= sequence_count - 0x0c;
}
}
static void config_sb_layer_hs(ubi_sb_header * sb, off_t layer_count, off_t stream_size, off_t stream_offset, off_t stream_name) {
/* layer headers with stream name */
@ -2085,8 +2323,11 @@ static int config_sb_version(ubi_sb_header * sb, STREAMFILE *streamFile) {
sb->cfg.map_version = 3;
sb->cfg.map_entry_size = (sb->cfg.map_version < 2) ? 0x30 : 0x34;
if (sb->is_blk) {
sb->cfg.map_entry_size = 0x30;
}
if (sb->version == 0x00000000 || sb->version == 0x00000200) {
if (sb->version == 0x00000000 || sb->version == 0x00000200 || sb->is_blk) {
sb->cfg.audio_stream_size = 0x0c;
sb->cfg.audio_stream_offset = 0x10;
//sb->cfg.audio_extra_offset = 0x10;
@ -2179,7 +2420,7 @@ static int config_sb_version(ubi_sb_header * sb, STREAMFILE *streamFile) {
}
/* Tonic Trouble Special Edition (1999)(PC)-bnm */
/* Tonic Trouble Special Edition (1998)(PC)-bnm */
if (sb->version == 0x00000000 && sb->platform == UBI_PC && is_ttse_pc) {
config_sb_entry(sb, 0x20, 0x5c);
@ -2237,6 +2478,38 @@ static int config_sb_version(ubi_sb_header * sb, STREAMFILE *streamFile) {
return 1;
}
/* Donald Duck: Goin' Quackers (2000)(PS2)-blk */
/* The Jungle Book Rhythm N'Groove (PS2)-blk */
if (sb->version == 0x00000003 && sb->platform == UBI_PS2 && sb->is_blk) {
config_sb_entry(sb, 0x20, 0x40);
config_sb_audio_fb_ps2_old(sb, 0x18, (1 << 4), (1 << 5), (1 << 6), (1 << 7));
config_sb_audio_hs(sb, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00);
sb->cfg.audio_interleave = 0x800;
sb->is_ps2_old = 1; /* yikes */
config_sb_sequence(sb, 0x2c, 0x18); /* this is normal enough */
config_sb_layer_hs(sb, 0x1c, 0x0c, 0x10, 0x00);
return 1;
}
/* Batman: Vengeance (2001)(PS2)-map */
/* Disney's Tarzan: Untamed (2001)(PS2)-map */
if (sb->version == 0x00000003 && sb->platform == UBI_PS2) {
config_sb_entry(sb, 0x30, 0x3c);
config_sb_audio_fb_ps2_old(sb, 0x1c, (1 << 4), (1 << 5), (1 << 6), (1 << 7));
config_sb_audio_hs(sb, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00);
sb->cfg.audio_interleave = 0x800;
sb->is_ps2_old = 1;
config_sb_sequence(sb, 0x2c, 0x18);
config_sb_layer_hs(sb, 0x20, 0x0c, 0x14, 0x00);
return 1;
}
/* Disney's Tarzan: Untamed (2001)(GC)-map */
/* Batman: Vengeance (2001)(GC)-map */
/* Donald Duck: Goin' Quackers (2002)(GC)-map */
@ -2253,26 +2526,6 @@ static int config_sb_version(ubi_sb_header * sb, STREAMFILE *streamFile) {
return 1;
}
#if 0
//todo too weird
/* Batman: Vengeance (2001)(PS2)-map */
/* Disney's Tarzan: Untamed (2001)(PS2)-map */
if (sb->version == 0x00000003 && sb->platform == UBI_PS2) {
config_sb_entry(sb, 0x30, 0x3c);
config_sb_audio_fb(sb, 0x1c, (1 << 2), (1 << 3), (1 << 4)); /* not ok */
config_sb_audio_hs(sb, 0x00, 0x24, 0x28, 0x28, 0x00, 0x00);
/* channels: 0? maybe 2=external, 1=internal? */
/* stream type: always PS-ADPCM (interleave unknown) */
/* sb->cfg.audio_stream_string = "STRM.SM1"; */ /* fixed */
config_sb_sequence(sb, 0x2c, 0x10); /* this is normal enough */
/* layers have a weird format too */
return 1;
}
#endif
#if 0
//todo group flags and maybe num_samples for sfx are off
/* Myst III: Exile (2001)(PS2)-map */
@ -2333,7 +2586,7 @@ static int config_sb_version(ubi_sb_header * sb, STREAMFILE *streamFile) {
}
/* Splinter Cell (2002)(PS2)-map */
/* Splinter Cell: Pandora Tomorrow (2006)(PS2)-map 0x00000007 */
/* Splinter Cell: Pandora Tomorrow (2004)(PS2)-map */
if (sb->version == 0x00000007 && sb->platform == UBI_PS2) {
config_sb_entry(sb, 0x40, 0x70);
@ -2373,7 +2626,7 @@ static int config_sb_version(ubi_sb_header * sb, STREAMFILE *streamFile) {
return 1;
}
/* Tom Clancy's Rainbow Six 3: Raven Shield + addons (2003)(PC)-bank 0x0000000B */
/* Tom Clancy's Rainbow Six 3: Raven Shield + addons (2003)(PC)-bank */
if (sb->version == 0x0000000B && sb->platform == UBI_PC) {
config_sb_entry(sb, 0x5c, 0x7c);
@ -2388,7 +2641,7 @@ static int config_sb_version(ubi_sb_header * sb, STREAMFILE *streamFile) {
return 1;
}
/* Prince of Persia: The Sands of Time Demo (2003)(Xbox)-bank 0x0000000D */
/* Prince of Persia: The Sands of Time (Demo)(2003)(Xbox)-bank */
if (sb->version == 0x0000000D && sb->platform == UBI_XBOX) {
config_sb_entry(sb, 0x5c, 0x74);
@ -2396,21 +2649,21 @@ static int config_sb_version(ubi_sb_header * sb, STREAMFILE *streamFile) {
config_sb_audio_hs(sb, 0x46, 0x40, 0x2c, 0x34, 0x4c, 0x48);
sb->cfg.audio_has_internal_names = 1;
config_sb_sequence(sb, 0x28, 0x34);
//config_sb_sequence(sb, 0x28, 0x34);
config_sb_layer_hs(sb, 0x20, 0x60, 0x58, 0x30);
config_sb_layer_sh(sb, 0x14, 0x00, 0x06, 0x08, 0x10);
return 1;
}
/* Prince of Persia: The Sands of Time Demo (2003)(Xbox)-bank 0x000A0000 */
/* Prince of Persia: The Sands of Time (Demo)(2003)(Xbox)-bank */
if (sb->version == 0x000A0000 && sb->platform == UBI_XBOX) {
config_sb_entry(sb, 0x64, 0x78);
config_sb_audio_fs(sb, 0x24, 0x28, 0x2c);
config_sb_audio_hs(sb, 0x4a, 0x44, 0x30, 0x38, 0x50, 0x4c);
config_sb_sequence(sb, 0x28, 0x14);
//config_sb_sequence(sb, 0x28, 0x14);
config_sb_layer_hs(sb, 0x20, 0x60, 0x58, 0x30);
config_sb_layer_sh(sb, 0x14, 0x00, 0x06, 0x08, 0x10);
@ -2448,7 +2701,7 @@ static int config_sb_version(ubi_sb_header * sb, STREAMFILE *streamFile) {
/* Tom Clancy's Rainbow Six 3 (2003)(PS2)-bank 0x000A0007 */
/* Tom Clancy's Ghost Recon 2 (2004)(PS2)-bank 0x000A0007 */
/* Splinter Cell: Pandora Tomorrow (2006)(PS2)-bank 0x000A0008 (separate banks from main map) */
/* Prince of Persia: Warrior Within Demo (2004)(PS2)-bank 0x00100000 */
/* Prince of Persia: Warrior Within (Demo)(2004)(PS2)-bank 0x00100000 */
/* Prince of Persia: Warrior Within (2004)(PS2)-bank 0x00120009 */
if ((sb->version == 0x000A0002 && sb->platform == UBI_PS2) ||
(sb->version == 0x000A0004 && sb->platform == UBI_PS2) ||
@ -2471,8 +2724,8 @@ static int config_sb_version(ubi_sb_header * sb, STREAMFILE *streamFile) {
return 1;
}
/* Brothers in Arms: Road to Hill 30 (2005)[PS2] */
/* Brothers in Arms: Earned in Blood (2005)[PS2] */
/* Brothers in Arms: Road to Hill 30 (2005)(PS2) */
/* Brothers in Arms: Earned in Blood (2005)(PS2) */
if (sb->version == 0x000A0007 && sb->platform == UBI_PS2 && is_bia_ps2) {
config_sb_entry(sb, 0x5c, 0x14c);
@ -2492,7 +2745,7 @@ static int config_sb_version(ubi_sb_header * sb, STREAMFILE *streamFile) {
return 1;
}
/* Batman: Rise of Sin Tzu (2003)(Xbox)-map 0x000A0003 */
/* Batman: Rise of Sin Tzu (2003)(Xbox)-map */
if (sb->version == 0x000A0003 && sb->platform == UBI_XBOX) {
config_sb_entry(sb, 0x64, 0x80);
@ -2546,7 +2799,7 @@ static int config_sb_version(ubi_sb_header * sb, STREAMFILE *streamFile) {
return 1;
}
/* Tom Clancy's Rainbow Six 3 (2003)(Xbox)-bank 0x000A0007 */
/* Tom Clancy's Rainbow Six 3 (2003)(Xbox)-bank */
if (sb->version == 0x000A0007 && sb->platform == UBI_XBOX) {
config_sb_entry(sb, 0x64, 0x8c);
@ -2564,7 +2817,7 @@ static int config_sb_version(ubi_sb_header * sb, STREAMFILE *streamFile) {
return 1;
}
/* Myst IV Demo (2004)(PC)-bank */
/* Myst IV (Demo)(2004)(PC)-bank */
if (sb->version == 0x00100000 && sb->platform == UBI_PC) {
config_sb_entry(sb, 0x68, 0xa4);
@ -2574,7 +2827,7 @@ static int config_sb_version(ubi_sb_header * sb, STREAMFILE *streamFile) {
return 1;
}
/* Prince of Persia: Warrior Within Demo (2004)(PC)-bank 0x00120006 */
/* Prince of Persia: Warrior Within (Demo)(2004)(PC)-bank 0x00120006 */
/* Prince of Persia: Warrior Within (2004)(PC)-bank 0x00120009 */
if ((sb->version == 0x00120006 && sb->platform == UBI_PC) ||
(sb->version == 0x00120009 && sb->platform == UBI_PC)) {
@ -2830,7 +3083,7 @@ static int config_sb_version(ubi_sb_header * sb, STREAMFILE *streamFile) {
return 1;
}
/* Open Season (2006)(PC)-map 0x00180003 */
/* Open Season (2006)(PC)-map */
if (sb->version == 0x00180003 && sb->platform == UBI_PC) {
config_sb_entry(sb, 0x68, 0x78);
@ -2846,7 +3099,7 @@ static int config_sb_version(ubi_sb_header * sb, STREAMFILE *streamFile) {
return 1;
}
/* Open Season (2006)(Xbox)-map 0x00180003 */
/* Open Season (2006)(Xbox)-map */
if (sb->version == 0x00180003 && sb->platform == UBI_XBOX) {
config_sb_entry(sb, 0x48, 0x58);
@ -2862,7 +3115,7 @@ static int config_sb_version(ubi_sb_header * sb, STREAMFILE *streamFile) {
return 1;
}
/* Open Season (2006)(GC)-map 0x00180003 */
/* Open Season (2006)(GC)-map */
if (sb->version == 0x00180003 && sb->platform == UBI_GC) {
config_sb_entry(sb, 0x68, 0x6c);
@ -3039,8 +3292,8 @@ static int config_sb_version(ubi_sb_header * sb, STREAMFILE *streamFile) {
return 1;
}
/* Cranium Kabookii (2007)(Wii)-bank 0x001a0003 */
if (sb->version == 0x001a0003 && sb->platform == UBI_WII) {
/* Cranium Kabookii (2007)(Wii)-bank */
if (sb->version == 0x001A0003 && sb->platform == UBI_WII) {
config_sb_entry(sb, 0x6c, 0x78);
config_sb_audio_fs(sb, 0x2c, 0x30, 0x34);

View File

@ -351,6 +351,7 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = {
init_vgmstream_ubi_sb,
init_vgmstream_ubi_sm,
init_vgmstream_ubi_bnm,
init_vgmstream_ubi_blk,
init_vgmstream_ezw,
init_vgmstream_vxn,
init_vgmstream_ea_snr_sns,

View File

@ -1 +0,0 @@
#define VERSION "r1050-40-g33563f4"

View File

@ -1,2 +0,0 @@
# static version string; update manually before and after every release.
VERSION = "r1050-40-g33563f4"