mirror of
https://github.com/vgmstream/vgmstream.git
synced 2024-11-24 15:00:11 +01:00
Add Ubi .bnm [Rayman 2 (PC), Tonic Trouble (PC), Donald Duck (PC)]
This commit is contained in:
parent
c1b9df6a5e
commit
dd1bafb201
@ -91,6 +91,7 @@ static const char* extension_list[] = {
|
||||
"bmdx",
|
||||
"bms",
|
||||
"bnk",
|
||||
"bnm",
|
||||
"bns",
|
||||
"bnsf",
|
||||
"bo2",
|
||||
|
@ -673,6 +673,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_ezw(STREAMFILE * streamFile);
|
||||
|
||||
|
@ -5,9 +5,9 @@
|
||||
|
||||
|
||||
#define SB_MAX_LAYER_COUNT 16 /* arbitrary max */
|
||||
#define SB_MAX_CHAIN_COUNT 64 /* arbitrary max */
|
||||
#define SB_MAX_CHAIN_COUNT 256 /* +150 exist in Tonic Trouble */
|
||||
|
||||
typedef enum { UBI_IMA, UBI_ADPCM, RAW_PCM, RAW_PSX, RAW_DSP, RAW_XBOX, FMT_VAG, FMT_AT3, RAW_AT3, FMT_XMA1, RAW_XMA1, FMT_OGG, FMT_CWAV } ubi_sb_codec;
|
||||
typedef enum { UBI_IMA, UBI_ADPCM, RAW_PCM, RAW_PSX, RAW_DSP, RAW_XBOX, FMT_VAG, FMT_AT3, RAW_AT3, FMT_XMA1, RAW_XMA1, FMT_OGG, FMT_CWAV, FMT_APM } ubi_sb_codec;
|
||||
typedef enum { UBI_PC, UBI_PS2, UBI_XBOX, UBI_GC, UBI_X360, UBI_PSP, UBI_PS3, UBI_WII, UBI_3DS } ubi_sb_platform;
|
||||
typedef enum { UBI_NONE = 0, UBI_AUDIO, UBI_LAYER, UBI_SEQUENCE, UBI_SILENCE } ubi_sb_type;
|
||||
|
||||
@ -62,6 +62,11 @@ typedef struct {
|
||||
off_t silence_duration_int;
|
||||
off_t silence_duration_float;
|
||||
|
||||
off_t random_extra_offset;
|
||||
off_t random_sequence_count;
|
||||
size_t random_entry_size;
|
||||
int random_percent_int;
|
||||
|
||||
int is_padded_section1_offset;
|
||||
int is_padded_section2_offset;
|
||||
int is_padded_section3_offset;
|
||||
@ -94,7 +99,9 @@ typedef struct {
|
||||
uint32_t map_unknown;
|
||||
|
||||
/* SB info (some values are derived depending if it's standard sbX or map sbX) */
|
||||
int is_bank;
|
||||
int is_map;
|
||||
int is_bnm;
|
||||
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,
|
||||
@ -141,6 +148,8 @@ typedef struct {
|
||||
int layer_channels[SB_MAX_LAYER_COUNT];
|
||||
int sequence_count; /* number of segments in a sequence type */
|
||||
int sequence_chain[SB_MAX_CHAIN_COUNT]; /* sequence of entry numbers */
|
||||
int sequence_banks[SB_MAX_CHAIN_COUNT]; /* sequence of bnk bank numbers */
|
||||
int sequence_multibank; /* info flag */
|
||||
int sequence_loop; /* chain index to loop */
|
||||
int sequence_single; /* if que sequence plays once (loops by default) */
|
||||
|
||||
@ -154,6 +163,7 @@ typedef struct {
|
||||
int allowed_types[16];
|
||||
} ubi_sb_header;
|
||||
|
||||
static int parse_bnm_header(ubi_sb_header * sb, STREAMFILE *streamFile);
|
||||
static int parse_header(ubi_sb_header * sb, STREAMFILE *streamFile, off_t offset, int index);
|
||||
static int parse_sb(ubi_sb_header * sb, STREAMFILE *streamFile, int target_subsong);
|
||||
static VGMSTREAM * init_vgmstream_ubi_sb_header(ubi_sb_header *sb, STREAMFILE* streamTest, STREAMFILE *streamFile);
|
||||
@ -193,7 +203,7 @@ VGMSTREAM * init_vgmstream_ubi_sb(STREAMFILE *streamFile) {
|
||||
|
||||
/* SB HEADER */
|
||||
/* SBx layout: header, section1, section2, extra section, section3, data (all except header can be null) */
|
||||
sb.is_map = 0;
|
||||
sb.is_bank = 1;
|
||||
sb.version = read_32bit(0x00, streamFile);
|
||||
sb.section1_num = read_32bit(0x04, streamFile);
|
||||
sb.section2_num = read_32bit(0x08, streamFile);
|
||||
@ -342,18 +352,85 @@ fail:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#if 0
|
||||
/* .BNM - proto-sbX with map style format [Donald Duck: Goin' Quackers (PC), Tonic Trouble (PC)] */
|
||||
VGMSTREAM * init_vgmstream_ubi_bnm(STREAMFILE *streamFile) {
|
||||
|
||||
/* v0x00000000, header is somewhat like a map-style bank (offsets + sizes) but sectionX/3 fields
|
||||
* are fixed/reserved (unused?). Header entry sizes and config looks the same as early games.
|
||||
* Main codecs are 01=RAW_PCM and 04=FMT_APM (.apm if external, possibly Ubi IMA v0 but has a full header).
|
||||
* The sound engine doesn't seem to be named DARE yet.
|
||||
*/
|
||||
/* .BNM - proto-sbX with map style format [Rayman 2 (PC), Donald Duck: Goin' Quackers (PC), Tonic Trouble (PC)] */
|
||||
VGMSTREAM * init_vgmstream_ubi_bnm(STREAMFILE *streamFile) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
STREAMFILE *streamTest = NULL;
|
||||
ubi_sb_header sb = {0};
|
||||
int target_subsong = streamFile->stream_index;
|
||||
|
||||
if (target_subsong <= 0) target_subsong = 1;
|
||||
|
||||
|
||||
/* checks */
|
||||
if (!check_extensions(streamFile, "bnm"))
|
||||
goto fail;
|
||||
|
||||
/* v0, header is somewhat like a map-style bank (offsets + sizes) but sectionX/3 fields are
|
||||
* fixed/reserved (unused?). Header entry sizes and config works the same, and type numbers are
|
||||
* slightly different, but otherwise pretty much the same engine (not named DARE yet). Curiously
|
||||
* it may stream RIFF .wav (stream_offset pointing to "data"), and also .raw (PCM) or .apm IMA. */
|
||||
|
||||
/* use smaller header buffer for performance */
|
||||
streamTest = reopen_streamfile(streamFile, 0x100);
|
||||
if (!streamTest) goto fail;
|
||||
|
||||
if (!parse_bnm_header(&sb, streamTest))
|
||||
goto fail;
|
||||
|
||||
if (!parse_sb(&sb, streamTest, target_subsong))
|
||||
goto fail;
|
||||
|
||||
/* CREATE VGMSTREAM */
|
||||
vgmstream = init_vgmstream_ubi_sb_header(&sb, streamTest, streamFile);
|
||||
close_streamfile(streamTest);
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_streamfile(streamTest);
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int parse_bnm_header(ubi_sb_header * sb, STREAMFILE *streamFile) {
|
||||
int32_t(*read_32bit)(off_t, STREAMFILE*) = NULL;
|
||||
|
||||
/* PLATFORM DETECTION */
|
||||
sb->platform = UBI_PC;
|
||||
read_32bit = sb->big_endian ? read_32bitBE : read_32bitLE;
|
||||
|
||||
/* SB HEADER */
|
||||
/* SBx layout: header, section1, section2, extra section, section3, data (all except header can be null) */
|
||||
sb->is_bnm = 1;
|
||||
sb->version = read_32bit(0x00, streamFile);
|
||||
sb->section1_offset = read_32bit(0x04, streamFile);
|
||||
sb->section1_num = read_32bit(0x08, streamFile);
|
||||
sb->section2_offset = read_32bit(0x0c, streamFile);
|
||||
sb->section2_num = read_32bit(0x10, streamFile);
|
||||
/* next are data start offset x3 + data size offset x3 */
|
||||
sb->section3_offset = read_32bit(0x14, streamFile);
|
||||
sb->section3_num = 0;
|
||||
|
||||
if (!config_sb_version(sb, streamFile))
|
||||
goto fail;
|
||||
|
||||
sb->sectionX_offset = sb->section2_offset + sb->section2_num * sb->cfg.section2_entry_size;
|
||||
sb->sectionX_size = sb->section3_offset - sb->sectionX_offset;
|
||||
|
||||
return 1;
|
||||
fail:
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int is_bnm_other_bank(STREAMFILE *streamFile, int bank_number) {
|
||||
char current_name[PATH_LIMIT];
|
||||
char bank_name[255];
|
||||
|
||||
get_streamfile_filename(streamFile, current_name, PATH_LIMIT);
|
||||
sprintf(bank_name, "Bnk_%i.bnm", 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)] */
|
||||
@ -398,15 +475,15 @@ static VGMSTREAM * init_vgmstream_ubi_sb_base(ubi_sb_header *sb, STREAMFILE *str
|
||||
break;
|
||||
|
||||
case UBI_ADPCM:
|
||||
/* todo custom Ubi 4/6-bit ADPCM (4b for music/stereo and 6b for voices?)
|
||||
* - ~0x30: block-like header (some versions may not have it).
|
||||
* Seems to contain frame size, channels, sometimes sample rate/samples/etc.
|
||||
* - 0x34 per channel: channel header, starts with 0x02
|
||||
* - 0x600: frame data (unknown interleave)
|
||||
* - 0x02: ADPCM hist?
|
||||
*
|
||||
* frame data looks decoded in two passes (first may expand nibble somehow, other
|
||||
* seems to calculate diffs)
|
||||
/* todo custom Ubi 4/6-bit ADPCM (4b for music/stereo and 6b for voices)
|
||||
* - ~0x30: stream header (varies with versions).
|
||||
* contains frame sizes (normal/last), channels, sometimes sample rate/samples/etc.
|
||||
* - 0x34 per channel: channel header, starts with 0x02, contains ADPCM info
|
||||
* - 0x602/902: frame data divided into 2 chunks (with a padding byte)
|
||||
* for each chunk frame data is read as LE int32, then divided into codes
|
||||
* (some 6b span two int32). stereo interleaves 4 codes per channel.
|
||||
* data is decoded using 3 tables and ops to adjust sample and calculate next step
|
||||
* (6b decoding is much simpler than 4b, that applies more extra ops).
|
||||
*
|
||||
* used in:
|
||||
* - Batman: Vengeance (PC)
|
||||
@ -422,6 +499,12 @@ static VGMSTREAM * init_vgmstream_ubi_sb_base(ubi_sb_header *sb, STREAMFILE *str
|
||||
vgmstream->coding_type = coding_PCM16LE; /* always LE */
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = 0x02;
|
||||
|
||||
if (vgmstream->num_samples == 0) { /* happens in .bnm */
|
||||
//todo with external wav streams stream_size may be off?
|
||||
vgmstream->num_samples = pcm_bytes_to_samples(sb->stream_size, sb->channels, 16);
|
||||
vgmstream->loop_end_sample = vgmstream->num_samples;
|
||||
}
|
||||
break;
|
||||
|
||||
case RAW_PSX:
|
||||
@ -430,6 +513,7 @@ static VGMSTREAM * init_vgmstream_ubi_sb_base(ubi_sb_header *sb, STREAMFILE *str
|
||||
vgmstream->interleave_block_size = (sb->cfg.audio_interleave) ?
|
||||
sb->cfg.audio_interleave :
|
||||
sb->stream_size / sb->channels;
|
||||
|
||||
if (vgmstream->num_samples == 0) { /* early PS2 games may not set it for internal streams */
|
||||
vgmstream->num_samples = ps_bytes_to_samples(sb->stream_size, sb->channels);
|
||||
vgmstream->loop_end_sample = vgmstream->num_samples;
|
||||
@ -491,7 +575,7 @@ static VGMSTREAM * init_vgmstream_ubi_sb_base(ubi_sb_header *sb, STREAMFILE *str
|
||||
|
||||
block_size = 0x98 * sb->channels;
|
||||
joint_stereo = 0;
|
||||
encoder_delay = 0x00; /* TODO: this is incorrect */
|
||||
encoder_delay = 0x00; /* TODO: this is may be incorrect */
|
||||
|
||||
bytes = ffmpeg_make_riff_atrac3(buf, 0x100, sb->num_samples, sb->stream_size, sb->channels, sb->sample_rate, block_size, joint_stereo, encoder_delay);
|
||||
ffmpeg_data = init_ffmpeg_header_offset(streamData, buf, bytes, start_offset, sb->stream_size);
|
||||
@ -592,6 +676,53 @@ static VGMSTREAM * init_vgmstream_ubi_sb_base(ubi_sb_header *sb, STREAMFILE *str
|
||||
start_offset += 0xe0; /* skip CWAV header */
|
||||
break;
|
||||
|
||||
case FMT_APM:
|
||||
/* APM is a full format though most fields are repeated from .bnm
|
||||
* (info from https://github.com/Synthesis/ray2get)
|
||||
* 0x00(2): format tag (0x2000 for Ubisoft ADPCM)
|
||||
* 0x02(2): channels
|
||||
* 0x04(4): sample rate
|
||||
* 0x08(4): byte rate? PCM samples?
|
||||
* 0x0C(2): block align
|
||||
* 0x0E(2): bits per sample
|
||||
* 0x10(4): header size
|
||||
* 0x14(4): "vs12"
|
||||
* 0x18(4): file size
|
||||
* 0x1C(4): nibble size
|
||||
* 0x20(4): -1?
|
||||
* 0x24(4): 0?
|
||||
* 0x28(4): high/low nibble flag (when loaded in memory)
|
||||
* 0x2C(N): ADPCM info per channel, last to first
|
||||
* - 0x00(4): ADPCM hist
|
||||
* - 0x04(4): ADPCM step index
|
||||
* - 0x08(4): copy of ADPCM data (after interleave, ex. R from data + 0x01)
|
||||
* 0x60(4): "DATA"
|
||||
* 0x64(N): ADPCM data
|
||||
*/
|
||||
|
||||
vgmstream->coding_type = coding_DVI_IMA_int;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = 0x01;
|
||||
|
||||
/* read initial hist (last to first) */
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < sb->channels; i++) {
|
||||
vgmstream->ch[i].adpcm_history1_32 = read_32bitLE(start_offset + 0x2c + 0x0c*(sb->channels - 1 - i) + 0x00, streamData);
|
||||
vgmstream->ch[i].adpcm_step_index = read_32bitLE(start_offset + 0x2c + 0x0c*(sb->channels - 1 - i) + 0x04, streamData);
|
||||
}
|
||||
}
|
||||
//todo supposedly APM IMA removes lower 3b after assigning step, but wave looks a bit off (Rayman 2 only?):
|
||||
// ...; step = adpcm_table[step_index]; delta = (step >> 3); step &= (~7); ...
|
||||
|
||||
start_offset += 0x64; /* skip APM header (may be internal or external) */
|
||||
|
||||
if (vgmstream->num_samples == 0) {
|
||||
vgmstream->num_samples = ima_bytes_to_samples(sb->stream_size - 0x64, sb->channels);
|
||||
vgmstream->loop_end_sample = vgmstream->num_samples;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
VGM_LOG("UBI SB: unknown codec\n");
|
||||
goto fail;
|
||||
@ -718,6 +849,8 @@ static VGMSTREAM * init_vgmstream_ubi_sb_sequence(ubi_sb_header *sb, STREAMFILE
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
segmented_layout_data* data = NULL;
|
||||
int i;
|
||||
STREAMFILE *streamBank = streamTest;
|
||||
|
||||
|
||||
//todo optimization: open streamData once / only if new name (doesn't change 99% of the time)
|
||||
|
||||
@ -730,32 +863,60 @@ static VGMSTREAM * init_vgmstream_ubi_sb_sequence(ubi_sb_header *sb, STREAMFILE
|
||||
|
||||
/* open all segments and mix */
|
||||
for (i = 0; i < sb->sequence_count; i++) {
|
||||
ubi_sb_header temp_sb = *sb; /* memcpy'ed */
|
||||
ubi_sb_header temp_sb = {0};
|
||||
off_t entry_offset;
|
||||
int entry_index = sb->sequence_chain[i];
|
||||
off_t entry_offset = sb->section2_offset + sb->cfg.section2_entry_size * entry_index;
|
||||
|
||||
|
||||
/* bnm sequences may use to entries from other banks, do some voodoo */
|
||||
if (sb->is_bnm) {
|
||||
/* see if *current* bank has changed (may use a different bank N times) */
|
||||
if (is_bnm_other_bank(streamBank, sb->sequence_banks[i])) {
|
||||
char bank_name[255];
|
||||
sprintf(bank_name, "Bnk_%i.bnm", sb->sequence_banks[i]);
|
||||
|
||||
if (streamBank != streamTest)
|
||||
close_streamfile(streamBank);
|
||||
|
||||
streamBank = open_streamfile_by_filename(streamFile, bank_name);
|
||||
if (!streamBank) goto fail;
|
||||
}
|
||||
|
||||
/* re-parse the thing */
|
||||
if (!parse_bnm_header(&temp_sb, streamBank))
|
||||
goto fail;
|
||||
temp_sb.total_subsongs = 1; /* eh... just to keep parse_header happy */
|
||||
}
|
||||
else {
|
||||
temp_sb = *sb; /* memcpy'ed */
|
||||
}
|
||||
|
||||
/* parse expected entry */
|
||||
if (!parse_header(&temp_sb, streamTest, entry_offset, entry_index))
|
||||
entry_offset = temp_sb.section2_offset + temp_sb.cfg.section2_entry_size * entry_index;
|
||||
if (!parse_header(&temp_sb, streamBank, entry_offset, entry_index))
|
||||
goto fail;
|
||||
|
||||
if (temp_sb.type == UBI_NONE || temp_sb.type == UBI_SEQUENCE) {
|
||||
VGM_LOG("UBI SB: unexpected sequence entry type at %x\n", (uint32_t)entry_offset);
|
||||
goto fail; /* technically ok but too much recursiveness? */
|
||||
VGM_LOG("UBI SB: unexpected sequence %i entry type at %x\n", i, (uint32_t)entry_offset);
|
||||
goto fail; /* not seen, technically ok but too much recursiveness? */
|
||||
}
|
||||
|
||||
/* build the layer VGMSTREAM (current sb entry config) */
|
||||
data->segments[i] = init_vgmstream_ubi_sb_header(&temp_sb, streamTest, streamFile);
|
||||
data->segments[i] = init_vgmstream_ubi_sb_header(&temp_sb, streamBank, streamFile);
|
||||
if (!data->segments[i]) goto fail;
|
||||
|
||||
if (i == sb->sequence_loop)
|
||||
sb->loop_start = sb->num_samples;
|
||||
sb->num_samples += data->segments[i]->num_samples;
|
||||
|
||||
/* save current (silences don't have values, so this unsures they know when memcpy'ed) */
|
||||
/* save current (silences don't have values, so this ensures they know later, when memcpy'ed) */
|
||||
sb->channels = temp_sb.channels;
|
||||
sb->sample_rate = temp_sb.sample_rate;
|
||||
}
|
||||
|
||||
if (streamBank != streamTest)
|
||||
close_streamfile(streamBank);
|
||||
|
||||
if (!setup_layout_segmented(data))
|
||||
goto fail;
|
||||
|
||||
@ -782,6 +943,8 @@ fail:
|
||||
close_vgmstream(vgmstream);
|
||||
else
|
||||
free_layout_segmented(data);
|
||||
if (streamBank != streamTest)
|
||||
close_streamfile(streamBank);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -912,10 +1075,18 @@ static void build_readable_name(char * buf, size_t buf_size, ubi_sb_header * sb)
|
||||
int index;
|
||||
|
||||
/* config */
|
||||
if (sb->is_map)
|
||||
if (sb->is_map) {
|
||||
grp_name = sb->map_name;
|
||||
else
|
||||
}
|
||||
else if (sb->is_bnm) {
|
||||
if (sb->sequence_multibank)
|
||||
grp_name = "bnm-multi";
|
||||
else
|
||||
grp_name = "bnm";
|
||||
}
|
||||
else {
|
||||
grp_name = "bank";
|
||||
}
|
||||
id = sb->header_id;
|
||||
type = sb->header_type;
|
||||
if (sb->is_map)
|
||||
@ -1056,7 +1227,7 @@ static int parse_type_sequence(ubi_sb_header * sb, off_t offset, STREAMFILE* str
|
||||
sb->sequence_count = read_32bit(offset + sb->cfg.sequence_sequence_count, streamFile);
|
||||
|
||||
if (sb->sequence_count > SB_MAX_CHAIN_COUNT) {
|
||||
VGM_LOG("Ubi SB: incorrect layer count\n");
|
||||
VGM_LOG("Ubi SB: incorrect sequence count %i vs %i\n", sb->sequence_count, SB_MAX_CHAIN_COUNT);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
@ -1065,15 +1236,30 @@ static int parse_type_sequence(ubi_sb_header * sb, off_t offset, STREAMFILE* str
|
||||
for (i = 0; i < sb->sequence_count; i++) {
|
||||
uint32_t entry_number = (uint32_t)read_32bit(table_offset+sb->cfg.sequence_entry_number, streamFile);
|
||||
|
||||
/* bnm sequences may refer to entries from different banks, whee */
|
||||
if (sb->is_bnm) {
|
||||
int16_t bank_number = (entry_number >> 16) & 0xFFFF;
|
||||
entry_number = (entry_number >> 00) & 0xFFFF;
|
||||
|
||||
//;VGM_LOG("UBI SB: bnm sequence entry=%i, bank=%i\n", entry_number, bank_number);
|
||||
sb->sequence_banks[i] = bank_number;
|
||||
|
||||
/* info flag, does bank number point to another file? */
|
||||
if (!sb->sequence_multibank) {
|
||||
sb->sequence_multibank = is_bnm_other_bank(streamFile, bank_number);
|
||||
}
|
||||
}
|
||||
else {
|
||||
entry_number = entry_number & 0x3FFFFFFF;
|
||||
if (entry_number > sb->section2_num) {
|
||||
VGM_LOG("UBI SB: chain with wrong entry %i vs %i at %x\n", entry_number, sb->section2_num, (uint32_t)sb->extra_offset);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
/* some sequences have an upper bit (2 bits in Donald Duck voices) for some reason */
|
||||
//;VGM_ASSERT_ONCE(entry_number & 0xC0000000, "UBI SB: sequence bit entry found at %x\n", (uint32_t)sb->extra_offset);
|
||||
|
||||
entry_number = entry_number & 0x3FFFFFFF;
|
||||
if (entry_number > sb->section2_num) {
|
||||
VGM_LOG("UBI SB: chain with wrong entry %i vs %i at %x\n", entry_number, sb->section2_num, (uint32_t)sb->extra_offset);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
sb->sequence_chain[i] = entry_number;
|
||||
|
||||
table_offset += sb->cfg.sequence_entry_size;
|
||||
@ -1195,6 +1381,57 @@ fail:
|
||||
return 0;
|
||||
}
|
||||
|
||||
// todo improve, only used in bnm sequences as sequence end (and may point to another bnm)
|
||||
static int parse_type_random(ubi_sb_header * sb, off_t offset, STREAMFILE* streamFile) {
|
||||
int32_t (*read_32bit)(off_t,STREAMFILE*) = sb->big_endian ? read_32bitBE : read_32bitLE;
|
||||
|
||||
off_t sb_extra_offset, table_offset;
|
||||
int i, sb_sequence_count;
|
||||
|
||||
/* sequence chain */
|
||||
if (sb->cfg.random_entry_size == 0) {
|
||||
VGM_LOG("Ubi SB: random entry size not configured at %x\n", (uint32_t)offset);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
sb_extra_offset = read_32bit(offset + sb->cfg.random_extra_offset, streamFile) + sb->sectionX_offset;
|
||||
sb_sequence_count = read_32bit(offset + sb->cfg.random_sequence_count, streamFile);
|
||||
|
||||
|
||||
/* get chain in extra table */
|
||||
table_offset = sb_extra_offset;
|
||||
for (i = 0; i < sb_sequence_count; i++) {
|
||||
uint32_t entry_number = (uint32_t)read_32bit(table_offset+0x00, streamFile);
|
||||
//uint32_t entry_chance = (uint32_t)read_32bit(table_offset+0x04, streamFile);
|
||||
|
||||
if (sb->is_bnm) {
|
||||
int16_t bank_number = (entry_number >> 16) & 0xFFFF;
|
||||
entry_number = (entry_number >> 00) & 0xFFFF;
|
||||
|
||||
;VGM_LOG("UBI SB: bnm sequence entry=%i, bank=%i\n", entry_number, bank_number);
|
||||
//sb->sequence_banks[i] = bank_number;
|
||||
|
||||
/* not seen */
|
||||
if (is_bnm_other_bank(streamFile, bank_number)) {
|
||||
VGM_LOG("UBI SB: random in other bank\n");
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
//todo make rand or stuff (old chance: int from 0 to 0x10000, new: float from 0.0 to 1.0)
|
||||
{ //if (entry_chance == ...)
|
||||
off_t entry_offset = sb->section2_offset + sb->cfg.section2_entry_size * entry_number;
|
||||
return parse_type_audio(sb, entry_offset, streamFile);
|
||||
}
|
||||
|
||||
table_offset += sb->cfg.random_entry_size;
|
||||
}
|
||||
|
||||
return 1;
|
||||
fail:
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* find actual codec from type (as different games' stream_type can overlap) */
|
||||
static int parse_stream_codec(ubi_sb_header * sb) {
|
||||
@ -1286,6 +1523,10 @@ static int parse_stream_codec(ubi_sb_header * sb) {
|
||||
|
||||
case 0x04:
|
||||
switch (sb->version) {
|
||||
case 0x00000000: /* Rayman 2, Tonic Trouble */
|
||||
sb->codec = FMT_APM;
|
||||
break;
|
||||
|
||||
case 0x00000007: /* Splinter Cell, Splinter Cell: Pandora Tomorrow */
|
||||
sb->codec = UBI_IMA;
|
||||
break;
|
||||
@ -1461,6 +1702,7 @@ static int parse_header(ubi_sb_header * sb, STREAMFILE *streamFile, off_t offset
|
||||
goto fail;
|
||||
break;
|
||||
case 0x05:
|
||||
case 0x0b:
|
||||
case 0x0c:
|
||||
if (!parse_type_sequence(sb, offset, streamFile))
|
||||
goto fail;
|
||||
@ -1475,8 +1717,12 @@ static int parse_header(ubi_sb_header * sb, STREAMFILE *streamFile, off_t offset
|
||||
if (!parse_type_silence(sb, offset, streamFile))
|
||||
goto fail;
|
||||
break;
|
||||
case 0x0a:
|
||||
if (!parse_type_random(sb, offset, streamFile))
|
||||
goto fail;
|
||||
break;
|
||||
default:
|
||||
VGM_LOG("UBI SB: unknown header type at %x\n", (uint32_t)offset);
|
||||
VGM_LOG("UBI SB: unknown header type %x at %x\n", sb->header_type, (uint32_t)offset);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
@ -1509,7 +1755,7 @@ static int parse_sb(ubi_sb_header * sb, STREAMFILE *streamFile, int target_subso
|
||||
/*header_id =*/ read_32bit(offset + 0x00, streamFile); /* forces buffer read */
|
||||
header_type = read_32bit(offset + 0x04, streamFile);
|
||||
|
||||
if (header_type <= 0x00 || header_type >= 0x10 || header_type == 0x09 || header_type == 0x0b) {
|
||||
if (header_type <= 0x00 || header_type >= 0x10 || header_type == 0x09) {
|
||||
VGM_LOG("UBI SB: unknown type %x at %x\n", header_type, (uint32_t)offset);
|
||||
goto fail;
|
||||
}
|
||||
@ -1658,6 +1904,10 @@ static void config_sb_sequence(ubi_sb_header * sb, off_t sequence_count, off_t e
|
||||
sb->cfg.sequence_sequence_count = sequence_count;
|
||||
sb->cfg.sequence_entry_size = entry_size;
|
||||
sb->cfg.sequence_entry_number = 0x00;
|
||||
if (sb->is_bnm) {
|
||||
sb->cfg.sequence_sequence_loop = sequence_count - 0x0c;
|
||||
sb->cfg.sequence_sequence_single= sequence_count - 0x08;
|
||||
}
|
||||
}
|
||||
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 */
|
||||
@ -1690,7 +1940,14 @@ static void config_sb_silence_f(ubi_sb_header * sb, off_t duration) {
|
||||
sb->cfg.silence_duration_float = duration;
|
||||
}
|
||||
|
||||
static void config_sb_random_old(ubi_sb_header * sb, off_t sequence_count, off_t entry_size) {
|
||||
sb->cfg.random_sequence_count = sequence_count;
|
||||
sb->cfg.random_entry_size = entry_size;
|
||||
sb->cfg.random_percent_int = 1;
|
||||
}
|
||||
|
||||
static int config_sb_version(ubi_sb_header * sb, STREAMFILE *streamFile) {
|
||||
int is_dino_pc = 0;
|
||||
int is_bia_ps2 = 0, is_biadd_psp = 0;
|
||||
int is_sc2_ps2_gc = 0;
|
||||
int is_sc4_pc_online = 0;
|
||||
@ -1762,24 +2019,25 @@ static int config_sb_version(ubi_sb_header * sb, STREAMFILE *streamFile) {
|
||||
|
||||
/* debug strings reference:
|
||||
* - TYPE_SAMPLE: should be 0x01 (also "sound resource")
|
||||
* - TYPE_MULTITRACK: should be 0x06/0x0d (also "multilayer resource")
|
||||
* - TYPE_MULTITRACK: should be 0x06/0x0d/0x0b (also "multilayer resource")
|
||||
* - TYPE_SILENCE: should be 0x08
|
||||
* sequences may be "theme resource"
|
||||
* "class descryptor" is referenced too.
|
||||
* "class descriptor" is referenced too.
|
||||
*
|
||||
* Possible type names from .bnm (.sb's predecessor):
|
||||
* Type names from .bnm (.sb's predecessor):
|
||||
* 0: TYPE_INVALID
|
||||
* 1: TYPE_SAMPLE
|
||||
* 2: TYPE_MIDI
|
||||
* 3: TYPE_CDAUDIO
|
||||
* 4: TYPE_SEQUENCE
|
||||
* 4: TYPE_SEQUENCE (sfx chain?)
|
||||
* 5: TYPE_SWITCH_OLD
|
||||
* 6: TYPE_SPLIT
|
||||
* 7: TYPE_THEME_OLD
|
||||
* 8: TYPE_SWITCH
|
||||
* 9: TYPE_THEME_OLD2
|
||||
* A: TYPE_RANDOM
|
||||
* B: TYPE_THEME0
|
||||
* B: TYPE_THEME0 (sequence)
|
||||
* (only 1, 4, A and B are known)
|
||||
*/
|
||||
|
||||
/* All types may contain memory garbage, making it harder to identify fields (platforms
|
||||
@ -1804,7 +2062,22 @@ static int config_sb_version(ubi_sb_header * sb, STREAMFILE *streamFile) {
|
||||
|
||||
sb->cfg.map_entry_size = (sb->cfg.map_version < 2) ? 0x30 : 0x34;
|
||||
|
||||
if (sb->version <= 0x00000007) {
|
||||
if (sb->version <= 0x00000200) {
|
||||
sb->cfg.audio_stream_size = 0x0c;
|
||||
sb->cfg.audio_stream_offset = 0x10;
|
||||
//sb->cfg.audio_extra_offset = 0x10;
|
||||
//sb->cfg.audio_extra_size = 0x0c;
|
||||
|
||||
sb->cfg.sequence_extra_offset = 0x10;
|
||||
//sb->cfg.sequence_extra_size = 0x0c;
|
||||
|
||||
//sb->cfg.layer_extra_offset = 0x10;
|
||||
//sb->cfg.layer_extra_size = 0x0c;
|
||||
|
||||
sb->cfg.random_extra_offset = 0x10;
|
||||
//sb->cfg.random_extra_size = 0x0c;
|
||||
}
|
||||
else if (sb->version <= 0x00000007) {
|
||||
sb->cfg.audio_stream_size = 0x0c;
|
||||
sb->cfg.audio_extra_offset = 0x10;
|
||||
sb->cfg.audio_stream_offset = 0x14;
|
||||
@ -1830,6 +2103,10 @@ static int config_sb_version(ubi_sb_header * sb, STREAMFILE *streamFile) {
|
||||
sb->allowed_types[0x0d] = 1;
|
||||
//sb->allowed_types[0x08] = 1; /* only needed inside sequences */
|
||||
//sb->allowed_types[0x0f] = 1;
|
||||
if (sb->is_bnm) {
|
||||
//sb->allowed_types[0x0a] = 1; /* only needed inside sequences */
|
||||
sb->allowed_types[0x0b] = 1;
|
||||
}
|
||||
|
||||
#if 0
|
||||
{
|
||||
@ -1845,6 +2122,43 @@ static int config_sb_version(ubi_sb_header * sb, STREAMFILE *streamFile) {
|
||||
}
|
||||
#endif
|
||||
|
||||
/* two configs with same id; use SND file as identifier */
|
||||
if (sb->version == 0x00000000 && sb->platform == UBI_PC) {
|
||||
STREAMFILE * streamTest = open_streamfile_by_filename(streamFile, "Dino.lcb");
|
||||
if (streamTest) {
|
||||
is_dino_pc = 1;
|
||||
close_streamfile(streamTest);
|
||||
}
|
||||
}
|
||||
|
||||
/* some files in Dinosaur */
|
||||
if (sb->version == 0x00000200 && sb->platform == UBI_PC) {
|
||||
sb->version = 0x00000000;
|
||||
is_dino_pc = 1;
|
||||
}
|
||||
|
||||
/* Rayman 2: The Great Escape (1999)(PC)-bnm */
|
||||
/* Tonic Trouble (1999)(PC)-bnm */
|
||||
/* Donald Duck: Goin' Quackers (2000)(PC)-bnm */
|
||||
/* Disney's Dinosaur (2000)(PC)-bnm */
|
||||
if (sb->version == 0x00000000 && sb->platform == UBI_PC) {
|
||||
config_sb_entry(sb, 0x20, 0x5c);
|
||||
|
||||
config_sb_audio_fs(sb, 0x2c, 0x2c, 0x30); /* no group id */
|
||||
config_sb_audio_hs(sb, 0x42, 0x3c, 0x34, 0x34, 0x48, 0x44);
|
||||
sb->cfg.audio_has_internal_names = 1;
|
||||
|
||||
config_sb_sequence(sb, 0x24, 0x18);
|
||||
|
||||
config_sb_random_old(sb, 0x18, 0x0c); /* Rayman 2 needs it for rare sequence ends (ex. Bnk_31.bnm) */
|
||||
|
||||
/* no layers */
|
||||
|
||||
if (is_dino_pc)
|
||||
config_sb_entry(sb, 0x20, 0x60);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Batman: Vengeance (2001)(PC)-map */
|
||||
if (sb->version == 0x00000003 && sb->platform == UBI_PC) {
|
||||
config_sb_entry(sb, 0x40, 0x68);
|
||||
|
@ -374,6 +374,7 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = {
|
||||
init_vgmstream_naac,
|
||||
init_vgmstream_ubi_sb,
|
||||
init_vgmstream_ubi_sm,
|
||||
init_vgmstream_ubi_bnm,
|
||||
init_vgmstream_ezw,
|
||||
init_vgmstream_vxn,
|
||||
init_vgmstream_ea_snr_sns,
|
||||
@ -2902,6 +2903,7 @@ int vgmstream_open_stream(VGMSTREAM * vgmstream, STREAMFILE *streamFile, off_t s
|
||||
return 1;
|
||||
|
||||
fail:
|
||||
VGM_LOG("fail\n");
|
||||
/* open streams will be closed in close_vgmstream(), hopefully called by the meta */
|
||||
return 0;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user