vgmstream/src/meta/ubi_sb.c

2249 lines
83 KiB
C
Raw Normal View History

#include "meta.h"
#include "../coding/coding.h"
typedef enum { 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;
2019-01-05 04:32:07 +01:00
typedef enum { UBI_PC, UBI_PS2, UBI_XBOX, UBI_GC, UBI_X360, UBI_PSP, UBI_PS3, UBI_WII, UBI_3DS } ubi_sb_platform;
typedef struct {
ubi_sb_platform platform;
int big_endian;
int total_streams;
int is_external;
int autodetect_external;
ubi_sb_codec codec;
/* map base header info */
off_t map_start;
off_t map_num;
size_t map_entry_size;
uint32_t map_type;
uint32_t map_zero;
off_t map_offset;
off_t map_size;
char map_name[255];
uint32_t map_unknown;
/* SB info (some values are derived depending if it's standard sbX or map sbX) */
int is_map;
uint32_t version; /* 16b+16b major+minor version */
uint32_t version_empty; /* map sbX versions are empty */
/* folders? events? (often share header_id/type with some descriptors,
* but may exists without headers or header exist without this) */
size_t section1_num;
size_t section1_offset;
/* descriptors, audio header or other config types */
size_t section2_num;
size_t section2_offset;
/* internal streams table (id and offset), referenced by each header
* headers normally only use 0 or 1, and id 0 may not even be defined and sound start from 1 */
size_t section3_num;
size_t section3_offset;
/* section with sounds in some map versions */
size_t section4_num;
size_t section4_offset;
/* extra table, config for certain types (DSP coefs, external resources, layer headers, etc) */
size_t sectionX_size;
size_t sectionX_offset;
/* unknown, usually -1 but can be others (0/1/2/etc) */
int flag1;
int flag2;
/* where sound data starts, derived */
size_t sounds_offset;
2018-12-22 22:47:24 +01:00
/* header/stream info config */
/* audio header varies slightly per game/version but not enough parse case by case,
* instead we configure sizes and offsets to where each variable is */
int map_version; /* represents map style (1=first, 2=mid, 3=latest) */
size_t section1_entry_size;
size_t section2_entry_size;
size_t section3_entry_size;
size_t resource_name_size;
off_t cfg_stream_size;
off_t cfg_stream_offset;
off_t cfg_extra_offset;
off_t cfg_stream_id;
off_t cfg_stream_type;
off_t cfg_external_flag; /* stream is external */
off_t cfg_samples_flag; /* stream loops */
off_t cfg_num_samples; /* num_samples/loop start */
off_t cfg_num_samples2; /* num_samples/loop end (if loop set) */
off_t cfg_sample_rate;
off_t cfg_channels;
off_t cfg_stream_name; /* where the resource name is within the header */
off_t cfg_extra_name; /* where the resource name is within sectionX */
off_t cfg_xma_offset;
int and_external_flag; /* value for some flags can be an int or bitflags */
int and_samples_flag;
int and_stream_id;
int shr_stream_id;
int has_short_channels; /* channels value can be 16b or 32b */
int has_internal_names; /* resource name doubles as internal name in earlier games */
int has_extra_name_flag; /* if cfg_extra_name is set (since often extra_name = -1 is 'not set' and >= 0 is offset) */
int has_rotating_ids; /* stream id isn't set but is assigned using sequential rotation of sorts */
2018-12-22 22:47:24 +01:00
/* header/stream info */
uint32_t header_id; /* 16b+16b group+sound id identifier (should be unique within sbX, but not smX) */
uint32_t header_type; /* audio type (we only need 'standard audio' or 'layered audio') */
size_t stream_size; /* size of the audio data */
off_t stream_offset; /* offset within the data section (internal) or absolute (external) to the audio */
off_t extra_offset; /* offset within sectionX to extra data */
uint32_t stream_id; /* internal id to reference in section3 */
uint32_t stream_type; /* rough codec value */
int loop_flag;
int loop_start; /* loop starts that aren't 0 do exist but are very rare (ex. Beowulf PSP #33407) */
int num_samples; /* sometimes not set for internal resources, when set it's the same as manually calculated */
int sample_rate;
int channels;
char resource_name[255]; /* filename to the external stream, or internal stream info for some games */
char readable_name[255]; /* constructed name to show externally */
int header_index; /* position within section2 (considering all possible header types) */
off_t header_offset; /* offset of parsed audio header */
off_t xma_header_offset; /* XMA has some extra header stuff*/
int types[16]; /* counts for each possible header types, for debugging */
} ubi_sb_header;
2018-12-22 22:47:24 +01:00
static VGMSTREAM * init_vgmstream_ubi_sb_main(ubi_sb_header *sb, STREAMFILE *streamFile);
2018-12-23 05:20:36 +01:00
static int parse_sb_header(ubi_sb_header * sb, STREAMFILE *streamFile, int target_stream);
2019-01-05 04:32:07 +01:00
static int config_sb_platform(ubi_sb_header * sb, STREAMFILE *streamFile);
static int config_sb_version(ubi_sb_header * sb, STREAMFILE *streamFile);
/* .SBx - banks from Ubisoft's DARE (Digital Audio Rendering Engine) engine games in ~2000-2008+ */
VGMSTREAM * init_vgmstream_ubi_sb(STREAMFILE *streamFile) {
STREAMFILE *streamTest = NULL;
2018-12-22 22:47:24 +01:00
int32_t(*read_32bit)(off_t, STREAMFILE*) = NULL;
ubi_sb_header sb = { 0 };
int ok;
2018-12-23 05:20:36 +01:00
int target_stream = streamFile->stream_index;
/* check extension (number represents the platform, see later) */
2018-12-22 22:47:24 +01:00
if (!check_extensions(streamFile, "sb0,sb1,sb2,sb3,sb4,sb5,sb6,sb7"))
goto fail;
/* .sbX (sound bank) is a small multisong format (loaded in memory?) that contains SFX data
* but can also reference .ss0/ls0 (sound stream) external files for longer streams.
* A companion .sp0 (sound project) describes files and if it uses BANKs (.sb0) or MAPs (.sm0). */
/* PLATFORM DETECTION */
2019-01-05 04:32:07 +01:00
if (!config_sb_platform(&sb, streamFile))
2018-12-22 22:47:24 +01:00
goto fail;
if (sb.big_endian) {
read_32bit = read_32bitBE;
} else {
read_32bit = read_32bitLE;
}
if (target_stream == 0) target_stream = 1;
2018-12-22 22:47:24 +01:00
/* use smaller I/O buffer for performance, as this read lots of small headers all over the place */
streamTest = reopen_streamfile(streamFile, 0x100);
if (!streamTest) goto fail;
2018-12-22 22:47:24 +01:00
/* SB HEADER */
/* SBx layout: base header, section1, section2, extra section, section3, data (all except base header can be null) */
2018-12-22 22:47:24 +01:00
sb.is_map = 0;
sb.version = read_32bit(0x00, streamFile);
sb.section1_num = read_32bit(0x04, streamFile);
sb.section2_num = read_32bit(0x08, streamFile);
sb.section3_num = read_32bit(0x0c, streamFile);
sb.sectionX_size = read_32bit(0x10, streamFile);
sb.flag1 = read_32bit(0x14, streamFile);
sb.flag2 = read_32bit(0x18, streamFile);
2018-12-22 22:47:24 +01:00
ok = config_sb_version(&sb, streamFile);
if (!ok) goto fail;
sb.section1_offset = 0x1c;
sb.section2_offset = sb.section1_offset + sb.section1_entry_size * sb.section1_num;
sb.sectionX_offset = sb.section2_offset + sb.section2_entry_size * sb.section2_num;
sb.section3_offset = sb.sectionX_offset + sb.sectionX_size;
sb.sounds_offset = sb.section3_offset + sb.section3_entry_size * sb.section3_num;
if (!parse_sb_header(&sb, streamTest, target_stream))
goto fail;
close_streamfile(streamTest);
/* CREATE VGMSTREAM */
2018-12-22 22:47:24 +01:00
return init_vgmstream_ubi_sb_main(&sb, streamFile);
fail:
close_streamfile(streamTest);
2018-12-22 22:47:24 +01:00
return NULL;
}
/* .SMx - maps (sets of custom SBx files) also from Ubisoft's sound engine games in ~2000-2008+ */
2018-12-22 22:47:24 +01:00
VGMSTREAM * init_vgmstream_ubi_sm(STREAMFILE *streamFile) {
STREAMFILE *streamTest = NULL;
2018-12-22 22:47:24 +01:00
int32_t(*read_32bit)(off_t, STREAMFILE*) = NULL;
//int16_t(*read_16bit)(off_t, STREAMFILE*) = NULL;
2018-12-22 22:47:24 +01:00
ubi_sb_header sb = { 0 };
int ok, i;
2018-12-23 05:20:36 +01:00
int target_stream = streamFile->stream_index;
2018-12-22 22:47:24 +01:00
2018-12-22 22:47:24 +01:00
/* check extension (number represents the platform, see later) */
2018-12-30 17:32:13 +01:00
if (!check_extensions(streamFile, "sm0,sm1,sm2,sm3,sm4,sm5,sm6,sm7,lm0,lm1,lm2,lm3,lm4,lm5,lm6,lm7"))
2018-12-22 22:47:24 +01:00
goto fail;
/* .smX (sound map) is a set of slightly different sbX files, compiled into one "map" file.
* Map has a sbX per named area (example: menu, level1, boss1, level2...).
* This counts subsongs from all sbX, so totals can be massive, but there are splitters into mini-smX. */
2018-12-23 05:20:36 +01:00
/* PLATFORM DETECTION */
2019-01-05 04:32:07 +01:00
if (!config_sb_platform(&sb, streamFile))
2018-12-22 22:47:24 +01:00
goto fail;
if (sb.big_endian) {
read_32bit = read_32bitBE;
} else {
read_32bit = read_32bitLE;
}
if (target_stream == 0) target_stream = 1;
2018-12-22 22:47:24 +01:00
/* use smaller I/O buffer for performance, as this read lots of small headers all over the place */
streamTest = reopen_streamfile(streamFile, 0x100);
if (!streamTest) goto fail;
/* SM BASE HEADER */
/* SMx layout: base header with N map area offset/sizes (some? offsets within a SBx are relative) */
/* SBx layout: base header, section1, section2, section4, extra section, section3, data (all except base header can be null?) */
sb.is_map = 1;
sb.version = read_32bit(0x00, streamFile);
sb.map_start = read_32bit(0x04, streamFile);
sb.map_num = read_32bit(0x08, streamFile);
ok = config_sb_version(&sb, streamFile);
if (!ok || sb.map_version == 0) goto fail;
sb.map_entry_size = (sb.map_version < 2) ? 0x30 : 0x34;
2018-12-22 22:47:24 +01:00
for (i = 0; i < sb.map_num; i++) {
off_t offset = sb.map_start + i * sb.map_entry_size;
/* SM AREA HEADER */
sb.map_type = read_32bit(offset + 0x00, streamFile); /* usually 0/1=first, 0=rest */
sb.map_zero = read_32bit(offset + 0x04, streamFile);
sb.map_offset = read_32bit(offset + 0x08, streamFile);
sb.map_size = read_32bit(offset + 0x0c, streamFile); /* includes sbX header, but not internal streams */
read_string(sb.map_name, 0x20+1, offset + 0x10, streamFile); /* null-terminated and may contain garbage after null */
if (sb.map_version >= 3)
sb.map_unknown = read_32bit(offset + 0x30, streamFile); /* uncommon, id/config? longer name? mem garbage? */
/* SB HEADER */
sb.version_empty = read_32bit(sb.map_offset + 0x00, streamFile); /* sbX in maps don't set version */
sb.section1_offset = read_32bit(sb.map_offset + 0x04, streamFile) + sb.map_offset;
sb.section1_num = read_32bit(sb.map_offset + 0x08, streamFile);
sb.section2_offset = read_32bit(sb.map_offset + 0x0c, streamFile) + sb.map_offset;
sb.section2_num = read_32bit(sb.map_offset + 0x10, streamFile);
if (sb.map_version < 3) {
sb.section3_offset = read_32bit(sb.map_offset + 0x14, streamFile) + sb.map_offset;
sb.section3_num = read_32bit(sb.map_offset + 0x18, streamFile);
sb.sectionX_offset = read_32bit(sb.map_offset + 0x1c, streamFile) + sb.map_offset;
sb.sectionX_size = read_32bit(sb.map_offset + 0x20, streamFile);
} else {
sb.section4_offset = read_32bit(sb.map_offset + 0x14, streamFile);
sb.section4_num = read_32bit(sb.map_offset + 0x18, streamFile);
sb.section3_offset = read_32bit(sb.map_offset + 0x1c, streamFile) + sb.map_offset;
sb.section3_num = read_32bit(sb.map_offset + 0x20, streamFile);
sb.sectionX_offset = read_32bit(sb.map_offset + 0x24, streamFile) + sb.map_offset;
sb.sectionX_size = read_32bit(sb.map_offset + 0x28, streamFile);
/* latest map format has another section with sounds after section 2 */
sb.section2_num += sb.section4_num; /* Let's just merge it with section 2 */
sb.sectionX_offset += sb.section4_offset; /* for some reason, this is relative to section 4 here */
}
2018-12-22 22:47:24 +01:00
VGM_ASSERT(sb.map_type != 0 && sb.map_type != 1, "UBI SM: unknown map_type %x\n", (uint32_t)offset);
VGM_ASSERT(sb.map_zero != 0, "UBI SM: unknown map_zero %x\n", (uint32_t)offset);
//;VGM_ASSERT(sb.map_unknown != 0, "UBI SM: unknown map_unknown at %x\n", (uint32_t)offset);
VGM_ASSERT(sb.version_empty != 0, "UBI SM: unknown version_empty %x\n", (uint32_t)offset);
if (!parse_sb_header(&sb, streamTest, target_stream))
2018-12-22 22:47:24 +01:00
goto fail;
}
2018-12-23 05:20:36 +01:00
if (sb.total_streams == 0) {
VGM_LOG("UBI SB: no streams\n");
goto fail;
}
if (target_stream < 0 || target_stream > sb.total_streams) {
VGM_LOG("UBI SB: wrong target stream (target=%i, total=%i)\n", target_stream, sb.total_streams);
goto fail;
}
close_streamfile(streamTest);
/* CREATE VGMSTREAM */
2018-12-22 22:47:24 +01:00
return init_vgmstream_ubi_sb_main(&sb, streamFile);
fail:
close_streamfile(streamTest);
2018-12-22 22:47:24 +01:00
return NULL;
}
static VGMSTREAM * init_vgmstream_ubi_sb_main(ubi_sb_header *sb, STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
STREAMFILE *streamData = NULL;
off_t start_offset;
/* open external stream if needed */
2018-12-22 22:47:24 +01:00
if (sb->autodetect_external) { /* works most of the time but could give false positives */
VGM_LOG("UBI SB: autodetecting external stream '%s'\n", sb->resource_name);
streamData = open_streamfile_by_filename(streamFile,sb->resource_name);
if (!streamData) {
streamData = streamFile; /* assume internal */
2018-12-22 22:47:24 +01:00
if (sb->stream_size > get_streamfile_size(streamData)) {
VGM_LOG("UBI SB: expected external stream\n");
goto fail;
}
} else {
2018-12-22 22:47:24 +01:00
sb->is_external = 1;
}
}
2018-12-22 22:47:24 +01:00
else if (sb->is_external) {
streamData = open_streamfile_by_filename(streamFile,sb->resource_name);
if (!streamData) {
VGM_LOG("UBI SB: external stream '%s' not found\n", sb->resource_name);
goto fail;
}
}
else {
streamData = streamFile;
}
;VGM_LOG("UBI SB: stream offset=%x, size=%x, external=%i\n", (uint32_t)sb->stream_offset, sb->stream_size, sb->is_external);
2018-12-23 16:55:20 +01:00
start_offset = sb->stream_offset;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(sb->channels,sb->loop_flag);
if (!vgmstream) goto fail;
vgmstream->meta_type = meta_UBI_SB;
2018-12-22 22:47:24 +01:00
vgmstream->sample_rate = sb->sample_rate;
vgmstream->num_streams = sb->total_streams;
vgmstream->stream_size = sb->stream_size;
2018-12-22 22:47:24 +01:00
switch(sb->codec) {
case UBI_ADPCM:
vgmstream->coding_type = coding_UBI_IMA;
vgmstream->layout_type = layout_none;
if (sb->num_samples == 0)
sb->num_samples = ubi_ima_bytes_to_samples(sb->stream_size, sb->channels, streamData, start_offset);
break;
case RAW_PCM:
2017-11-11 17:46:18 +01:00
vgmstream->coding_type = coding_PCM16LE; /* always LE even on Wii */
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = 0x02;
if (sb->num_samples == 0)
sb->num_samples = pcm_bytes_to_samples(sb->stream_size, sb->channels, 16);
break;
case RAW_PSX:
vgmstream->coding_type = coding_PSX;
vgmstream->layout_type = layout_interleave;
2018-12-22 22:47:24 +01:00
vgmstream->interleave_block_size = (sb->stream_type == 0x00) ? sb->stream_size / sb->channels : 0x10; /* TODO: needs testing */
if (sb->num_samples == 0)
sb->num_samples = ps_bytes_to_samples(sb->stream_size, sb->channels) ;
break;
case RAW_XBOX:
vgmstream->coding_type = coding_XBOX_IMA;
vgmstream->layout_type = layout_none;
if (sb->num_samples == 0)
sb->num_samples = xbox_ima_bytes_to_samples(sb->stream_size, sb->channels);
break;
case RAW_DSP:
vgmstream->coding_type = coding_NGC_DSP;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = align_size_to_block(sb->stream_size / sb->channels, 0x04);
if (sb->num_samples == 0)
sb->num_samples = dsp_bytes_to_samples(sb->stream_size, sb->channels);
/* DSP extra info entry size is 0x40 (first/last 0x10 = unknown), per channel */
dsp_read_coefs_be(vgmstream,streamFile,sb->extra_offset + 0x10, 0x40);
break;
case FMT_VAG:
/* skip VAG header (some sb4 use VAG and others raw PSX) */ //todo remove
if (read_32bitBE(start_offset, streamData) == 0x56414770) { /* "VAGp" */
start_offset += 0x30;
2018-12-22 22:47:24 +01:00
sb->stream_size -= 0x30;
}
vgmstream->coding_type = coding_PSX;
vgmstream->layout_type = layout_interleave;
2018-12-22 22:47:24 +01:00
vgmstream->interleave_block_size = sb->stream_size / sb->channels;
//if (vgmstream->num_samples == 0) //todo remove
// vgmstream->num_samples = ps_bytes_to_samples(sb->stream_size, sb->channels);
break;
#ifdef VGM_USE_FFMPEG
case FMT_AT3: {
ffmpeg_codec_data *ffmpeg_data;
2017-12-07 23:54:05 +01:00
/* skip weird value (3, 4) in Brothers in Arms: D-Day (PSP) */
if (read_32bitBE(start_offset+0x04,streamData) == 0x52494646) {
VGM_LOG("UBI SB: skipping unknown value 0x%x before RIFF\n", read_32bitBE(start_offset+0x00,streamData));
2017-12-07 23:54:05 +01:00
start_offset += 0x04;
2018-12-22 22:47:24 +01:00
sb->stream_size -= 0x04;
2017-12-07 23:54:05 +01:00
}
2018-12-22 22:47:24 +01:00
ffmpeg_data = init_ffmpeg_offset(streamData, start_offset, sb->stream_size);
if ( !ffmpeg_data ) goto fail;
vgmstream->codec_data = ffmpeg_data;
vgmstream->coding_type = coding_FFmpeg;
vgmstream->layout_type = layout_none;
if (sb->num_samples == 0)
sb->num_samples = (int32_t)ffmpeg_data->totalSamples;
2018-08-14 16:35:17 +02:00
if (ffmpeg_data->skipSamples <= 0) /* in case FFmpeg didn't get them */
ffmpeg_set_skip_samples(ffmpeg_data, riff_get_fact_skip_samples(streamData, start_offset));
break;
}
case RAW_AT3: {
ffmpeg_codec_data *ffmpeg_data;
uint8_t buf[0x100];
int32_t bytes, block_size, encoder_delay, joint_stereo;
2018-12-22 22:47:24 +01:00
block_size = 0x98 * sb->channels;
joint_stereo = 0;
encoder_delay = 0x00; /* TODO: this is 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);
if (!ffmpeg_data) goto fail;
vgmstream->codec_data = ffmpeg_data;
vgmstream->coding_type = coding_FFmpeg;
vgmstream->layout_type = layout_none;
break;
}
case FMT_XMA1: {
ffmpeg_codec_data *ffmpeg_data;
uint8_t buf[0x100];
2018-12-31 21:05:58 +01:00
uint32_t sec1_num, sec2_num, sec3_num, num_frames, bits_per_frame;
uint8_t flag;
size_t bytes, chunk_size, header_size, data_size;
off_t header_offset;
chunk_size = 0x20;
/* formatted XMA sounds have a strange custom header */
header_offset = start_offset; /* XMA fmt chunk at the start */
flag = read_8bit(header_offset + 0x20, streamData);
sec2_num = read_32bitBE(header_offset + 0x24, streamData); /* number of XMA frames */
sec1_num = read_32bitBE(header_offset + 0x28, streamData);
sec3_num = read_32bitBE(header_offset + 0x2c, streamData);
2018-12-31 21:05:58 +01:00
num_frames = sec2_num;
bits_per_frame = 4;
if (flag == 0x04) {
sec1_num--;
2018-12-31 21:05:58 +01:00
sec2_num += 4;
} else if (flag == 0x02) {
bits_per_frame = 2;
}
2018-12-31 21:05:58 +01:00
header_size = 0x30;
header_size += sec1_num * 0x04;
header_size += align_size_to_block(sec2_num * bits_per_frame, 32) / 8; /* bitstream with 4 or 2 bits for each frame */
header_size += sec3_num * 0x08;
start_offset += header_size;
2018-12-31 21:05:58 +01:00
data_size = num_frames * 0x800;
bytes = ffmpeg_make_riff_xma_from_fmt_chunk(buf, 0x100, header_offset, chunk_size, data_size, streamData, 1);
ffmpeg_data = init_ffmpeg_header_offset(streamData, buf, bytes, start_offset, data_size);
if (!ffmpeg_data) goto fail;
vgmstream->codec_data = ffmpeg_data;
vgmstream->coding_type = coding_FFmpeg;
vgmstream->layout_type = layout_none;
vgmstream->stream_size = data_size;
break;
}
case RAW_XMA1: {
ffmpeg_codec_data *ffmpeg_data;
uint8_t buf[0x100];
size_t bytes, chunk_size;
off_t header_offset;
2018-12-22 22:47:24 +01:00
VGM_ASSERT(sb->is_external, "Ubi SB: Raw XMA used for external sound\n");
/* get XMA header from extra section */
chunk_size = 0x20;
2018-12-23 16:55:20 +01:00
header_offset = sb->xma_header_offset;
2018-12-22 22:47:24 +01:00
bytes = ffmpeg_make_riff_xma_from_fmt_chunk(buf, 0x100, header_offset, chunk_size, sb->stream_size, streamFile, 1);
2018-12-22 22:47:24 +01:00
ffmpeg_data = init_ffmpeg_header_offset(streamData, buf, bytes, start_offset, sb->stream_size);
if (!ffmpeg_data) goto fail;
vgmstream->codec_data = ffmpeg_data;
vgmstream->coding_type = coding_FFmpeg;
vgmstream->layout_type = layout_none;
break;
}
case FMT_OGG: {
ffmpeg_codec_data *ffmpeg_data;
2018-12-22 22:47:24 +01:00
ffmpeg_data = init_ffmpeg_offset(streamData, start_offset, sb->stream_size);
if ( !ffmpeg_data ) goto fail;
vgmstream->codec_data = ffmpeg_data;
vgmstream->coding_type = coding_FFmpeg;
vgmstream->layout_type = layout_none;
break;
}
#endif
case FMT_CWAV:
if (sb->channels > 1) goto fail; //todo improve parsing
vgmstream->coding_type = coding_NGC_DSP;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = 0x08;
dsp_read_coefs_le(vgmstream,streamFile,start_offset + 0x7c, 0x40);
start_offset += 0xe0;
break;
default:
VGM_LOG("UBI SB: unknown codec\n");
goto fail;
}
/* at the end to let codec fill num_samples */
vgmstream->num_samples = sb->num_samples;
vgmstream->loop_start_sample = sb->loop_start;
vgmstream->loop_end_sample = sb->num_samples;
strcpy(vgmstream->stream_name, sb->readable_name);
/* open the actual for decoding (streamData can be an internal or external stream) */
if ( !vgmstream_open_stream(vgmstream, streamData, start_offset) )
goto fail;
2018-12-22 22:47:24 +01:00
if (sb->is_external && streamData) close_streamfile(streamData);
return vgmstream;
fail:
2018-12-22 22:47:24 +01:00
if (sb->is_external && streamData) close_streamfile(streamData);
close_vgmstream(vgmstream);
return NULL;
}
/* debug stuff, for now */
static void parse_header_type(ubi_sb_header * sb, uint32_t header_type, off_t offset) {
/* all types may contain memory garbage, making it harder to identify
* usually next types can contain memory from the previous type header,
* so if some non-audio type looks like audio it's probably repeating old data.
* This even happens for common fields (ex. type 06 at 0x08 has prev garbage, not stream size) */
switch(header_type) {
case 0x01: sb->types[0x01]++; break; /* audio (all games) */
case 0x02: sb->types[0x02]++; break; /* config? (later games) */
case 0x03: sb->types[0x03]++; break; /* config? (later games) */
case 0x04: sb->types[0x04]++; break; /* config? (all games) [recheck: SC:CT] */
case 0x05: sb->types[0x05]++; break; /* config? (all games) [recheck: SC:CT] */
case 0x06: sb->types[0x06]++; break; /* layer (later games) */
case 0x07: sb->types[0x07]++; break; /* config? (later games) [recheck: SC:CT] */
case 0x08: sb->types[0x08]++; break; /* config? (all games) [recheck: SC:CT] */
//case 0x09: sb->types[0x09]++; break; /* ? */
case 0x0a: sb->types[0x0a]++; break; /* config? (early games) */
//case 0x0b: sb->types[0x0b]++; break; /* ? */
case 0x0c: sb->types[0x0c]++; break; /* config? (early games) */
case 0x0d: sb->types[0x0d]++; break; /* layer (early games) */
case 0x0e: sb->types[0x0e]++; break; /* config? (early games) */
case 0x0f: sb->types[0x0f]++; break; /* config? (early games) */
default:
VGM_LOG("UBI SB: unknown type %x at %x size %x\n", header_type, (uint32_t)offset, sb->section2_entry_size);
break; //goto fail;
}
;VGM_ASSERT(header_type == 0x06 || header_type == 0x0d,
"UBI SB: type %x at %x size %x\n", header_type, (uint32_t)offset, sb->section2_entry_size);
/* layer info for later
* some values may be flags/config as multiple 0x06 can point to the same layer, with different 'flags' */
/* headers/layers/blocks also respect endianness (codec is separate, ex. Ubi IMA header is always LE) */
/* 0x0d layer [Splinter Cell] */
/* - type header:
* 0x18: stream offset?
* 0x20: (sample rate * layers) + 1?
* 0x24: layers/channels?
* 0x30: external flag
* 0x34: external name
* 0x5C: stream offset
* 0x64: stream size (not including padding)
* 0x78/7c: codec?
*
* - layer header at stream_offset:
* 0x00: version? (0x00000002)
* 0x04: layers
* 0x08: stream size (not including padding)
* 0x0c: size?
* 0x10: count?
* 0x14: min block size?
*
* - blocked data (unlike other layers, first block data is standard Ubi IMA headers, v3):
* 0x00: block number (from 0x01 to block_count)
* 0x04: current offset (within stream_offset)
* - per layer:
* 0x00: layer data size (varies between blocks, and one layer may have more than other, even the header)
*/
/* Rainbow Six 3 */
/* Prince of Persia: Sands of Time (all) 0x000A0004 */
/* Batman: Rise of Sin Tzu (2003)(GC)-map - 0x000A0002 */
/* - type header (bizarrely but thankfully doesn't change between platforms):
* 0x1c: sample rate * layers
* 0x20: layers/channels?
* 0x2c: external flag?
* 0x30: external name
* 0x58: stream offset
* 0x5c: original rate * layers?
* 0x60: stream size (not including padding)
* 0x64: number of samples
*
* - layer header at stream_offset (BE on GC):
* 0x00: version? (0x00000004)
* 0x04: layers
* 0x08: stream size (not including padding)
* 0x0c: blocks count
* 0x10: block header size
* 0x14: block size
* 0x18: ?
* 0x1c: size of next section
* - per layer:
* 0x00: layer header size
* codec header per layer
* 0x00~0x20: standard Ubi IMA header (version 0x05, LE)
*
* - blocked data:
* 0x00: block number (from 0x01 to block_count)
* 0x04: current offset (within stream_offset)
* 0x08: always 0x03
* - per layer:
* 0x00: layer data size (varies between blocks, and one layer may have more than other)
*/
/* Splinter Cell: Essentials (PSP)-map 0x0012000C */
/* - type header:
* 0x08: header extra offset
* 0x1c: layers
* 0x28: external flag?
* 0x2c: external flag?
* 0x30: stream name
* 0x5c: sample rate * layers
* 0x60: stream size
* 0x64: stream offset?
*
* - in header extra offset (style 0x10)
* 0x00: sample rate
* 0x04: 16?
* 0x08: channels?
* 0x0c: codec?
*
* - layer header at stream_offset:
* 0x00: version (0x00000007)
* 0x04: config?
* 0x08: layers
* 0x0c: stream size
* 0x10: blocks count
* 0x14: block header size
* 0x18: next block size
* - per layer:
* 0x00: approximate layer data size per block
* - per layer
* 0x00~0x0c: weird header thing? -1/0/0c...
*
* - blocked data:
* 0x00: block number (from 0x01 to block_count)
* 0x04: current offset (within stream_offset)
* 0x08: always 0x03
* - per layer:
* 0x00: layer data size (varies between blocks, and one layer may have more than other)
*/
/* Splinter Cell: Double Agent (2006)(X360)-map 0x00180006 */
/* - type header:
* 0x08: header extra offset
* 0x20: layers
* 0x28: external flag?
* 0x30: external flag?
* 0x34: sample rate * layers
* 0x38: stream size
* 0x3c: stream offset
* 0x44: name extra offset
*
* - in header extra offset
* style 0x10 (codec 05 XMA), possible total size 0x34
*
* - layer header at stream_offset:
* version 0x000C0008 same as 0x000B0008, but codec header size is 0
*
* - blocked data:
* version same as 0x000B0008 (including mini XMA header + weird tables)
*/
/* Splinter Cell 3D (2011)(3DS)-map 0x00130001 */
/* - type header:
* 0x08: header extra offset
* 0x10: layers
* 0x28: stream size
* 0x30: stream offset
* 0x34: name extra offset
*
* - in header extra offset
* style 0x10 (size 0x18?)
*
* - layer header at stream_offset:
* same as 0x00000007 but weird header thing is 0x00~0x08, has header sizes after that
*
* - blocked data:
* same as 0x00000007
*/
/* TMNT (2007)(PS2)-bank 0x00190002 */
/* - type header:
* 0x08: header extra offset
* 0x1c: external flag?
* 0x20: layers
* 0x28: sample rate * layers
* 0x2c: stream size
* 0x30: stream offset
* 0x38: name extra offset
*
* - in header extra offset (style 0x0c)
* 0x00: sample rate
* 0x04: channels
* 0x08: codec
*
* - layer header at stream_offset:
* 0x00: version? (0x000B0008)
* 0x04: config? (0x00/0x0e/0x0b/etc)
* 0x08: layers
* 0x0c: blocks count
* 0x10: block header size
* 0x14: size of header sizes
* 0x18: next block size
* - per layer:
* 0x00: layer header size
* - per layer (if layer header size > 0)
* 0x00~0x20: standard Ubi IMA header (version 0x05), PCM data
*
* - blocked data:
* 0x00: always 0x03
* 0x04: next block size
* - per layer:
* 0x00: layer data size (varies between blocks, and one layer may have more than other)
*/
/* Open Season (2005)(PS2)-map 0x00180003 */
/* Rainbow Six Vegas (2007)(PSP)-bank 0x00180006 */
/* Star Wars - Lethal Alliance (2006)(PSP)-map 0x00180007 */
/* - type header:
* 0x0c: header extra offset
* 0x20: layers
* 0x2c: stream size
* 0x30: stream offset
* 0x38: name extra offset
*
* - in header extra offset
* style 0x10 (codec 03=Ubi, 01=PCM16LE in SW:LA/RS:V)
* - layer header at stream_offset:
* - blocked data:
* version 0x000B0008
*/
/* TMNT (2007)(PSP)-map 0x00190001 */
/* - type header:
* 0x0c: header extra offset
* 0x20: layers
* 0x24: total channels?
* 0x28: sample rate * layers?
* 0x2c: stream size
* 0x30: stream offset
* 0x38: name extra offset
*
* - in header extra offset
* style 0x0c
* - layer header at stream_offset:
* - blocked data:
* version 0x000B0008, but codec header size is 0
*/
/* TMNT (2007)(GC)-bank 0x00190002 */
/* Surf's Up (PS3)-bank 0x00190005 */
/* - type header:
* 0x08: header extra offset
* 0x1c: external flag?
* 0x20: layers
* 0x2c: external flag? (would match header 01)
* 0x30: sample rate * layers
* 0x34: stream size
* 0x38: stream offset
* 0x40: name extra offset
*
* - in header extra offset
* style 0x0c
* - layer header at stream_offset:
* - blocked data:
* version 0x000B0008
*/
/* Splinter Cell Classic Trilogy HD (2011)(PS3)-map 0x001d0000 */
/* - type header:
* 0x0c: header extra offset
* 0x20: layers
* 0x44: stream size
* 0x48: stream offset
* 0x54: name extra offset
*
* - in header extra offset
* style 0x0c
* - layer header at stream_offset:
* - blocked data:
* version 0x000B0008, but codec header size is 0
*/
}
2018-12-23 05:20:36 +01:00
static int parse_sb_header(ubi_sb_header * sb, STREAMFILE *streamFile, int target_stream) {
int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL;
int16_t (*read_16bit)(off_t,STREAMFILE*) = NULL;
2018-12-23 05:20:36 +01:00
int i, j, k, current_type = -1, current_id = -1, bank_streams = 0, prev_streams;
;VGM_LOG("UBI SB: s1=%x (%x*%x), s2=%x (%x*%x), s3=%x (%x*%x), sX=%x (%x)\n",
sb->section1_offset,sb->section1_entry_size,sb->section1_num,sb->section2_offset,sb->section2_entry_size,sb->section2_num,
sb->section3_offset,sb->section3_entry_size,sb->section3_num,sb->sectionX_offset,sb->sectionX_size);
if (sb->big_endian) {
read_32bit = read_32bitBE;
read_16bit = read_16bitBE;
} else {
read_32bit = read_32bitLE;
read_16bit = read_16bitLE;
}
2018-12-23 05:20:36 +01:00
prev_streams = sb->total_streams;
/* find target stream info in section2 */
for (i = 0; i < sb->section2_num; i++) {
2018-12-22 22:47:24 +01:00
off_t offset = sb->section2_offset + sb->section2_entry_size*i;
uint32_t header_type;
header_type = read_32bit(offset + 0x04, streamFile);
parse_header_type(sb, header_type, offset);
/* ignore non-audio entries */
if (header_type != 0x01)
continue;
/* weird case when there is no internal substream ID and just seem to rotate every time type changes, joy */
if (sb->has_rotating_ids) { /* assumes certain configs can't happen in this case */
int current_is_external = 0;
int type = read_32bit(offset + sb->cfg_stream_type, streamFile);
if (sb->cfg_external_flag) {
current_is_external = (read_32bit(offset + sb->cfg_external_flag, streamFile) & sb->and_external_flag);
} else if (sb->has_extra_name_flag && read_32bit(offset + sb->cfg_extra_name, streamFile) != 0xFFFFFFFF) {
current_is_external = 1; /* -1 in extra_name means internal */
}
if (!current_is_external) {
2018-12-31 00:57:58 +01:00
if (sb->is_map) {
if (current_type == -1)
current_type = type;
if (current_id == -1) /* they seem to always start with 0 in maps */
current_id = 0x00;
if (!current_is_external) {
if (current_type != type) {
current_type = type;
current_id++; /* rotate */
/* we'll warp it around later when parsing section 3 */
}
}
} else {
if (current_type == -1)
current_type = type;
if (current_id == -1) /* use first ID in section3 */
current_id = read_32bit(sb->section3_offset + 0x00, streamFile);
if (!current_is_external) {
if (current_type != type) {
current_type = type;
current_id++; /* rotate */
if (current_id >= sb->section3_num)
current_id = 0; /* reset */
}
}
}
}
}
/* update streams (total_stream also doubles as current) */
sb->total_streams++;
2018-12-23 05:20:36 +01:00
bank_streams++;
if (sb->total_streams != target_stream)
continue;
/* parse audio entry based on config */
sb->header_index = i;
sb->header_offset = offset;
sb->header_id = read_32bit(offset + 0x00, streamFile);
sb->header_type = read_32bit(offset + 0x04, streamFile);
sb->stream_size = read_32bit(offset + sb->cfg_stream_size, streamFile);
sb->extra_offset = read_32bit(offset + sb->cfg_extra_offset, streamFile) + sb->sectionX_offset;
sb->stream_offset = read_32bit(offset + sb->cfg_stream_offset, streamFile);
sb->channels = (sb->has_short_channels) ?
(uint16_t)read_16bit(offset + sb->cfg_channels, streamFile) :
(uint32_t)read_32bit(offset + sb->cfg_channels, streamFile);
sb->sample_rate = read_32bit(offset + sb->cfg_sample_rate, streamFile);
sb->stream_type = read_32bit(offset + sb->cfg_stream_type, streamFile);
if (sb->cfg_samples_flag) {
int samples_flag = read_32bit(offset + sb->cfg_samples_flag, streamFile);
sb->loop_flag = (samples_flag & sb->and_samples_flag);
}
if (sb->loop_flag) {
sb->loop_start = read_32bit(offset + sb->cfg_num_samples, streamFile);
sb->num_samples = read_32bit(offset + sb->cfg_num_samples2, streamFile) + sb->loop_start;
VGM_ASSERT(sb->num_samples == 0, "UBI SB: found 0 loop end\n"); /* possible in earlier games */
} else {
sb->num_samples = read_32bit(offset + sb->cfg_num_samples, streamFile);
}
if (sb->has_rotating_ids) {
sb->stream_id = current_id;
} else if (sb->cfg_stream_id) {
sb->stream_id = read_32bit(offset + sb->cfg_stream_id, streamFile);
if (sb->and_stream_id) sb->stream_id &= sb->and_stream_id;
if (sb->shr_stream_id) sb->stream_id >>= sb->shr_stream_id;
}
/* external stream name can be found in the header (first versions) or the sectionX table (later versions) */
if (sb->cfg_stream_name) {
read_string(sb->resource_name, sb->resource_name_size, offset + sb->cfg_stream_name, streamFile);
} else {
sb->cfg_stream_name = read_32bit(offset + sb->cfg_extra_name, streamFile);
if (sb->cfg_stream_name != 0xFFFFFFFF)
read_string(sb->resource_name, sb->resource_name_size, sb->sectionX_offset + sb->cfg_stream_name, streamFile);
}
/* external flag is not always set and must be derived */
if (sb->cfg_external_flag) {
sb->is_external = (read_32bit(offset + sb->cfg_external_flag, streamFile) & sb->and_external_flag);
} else if (sb->has_extra_name_flag && read_32bit(offset + sb->cfg_extra_name, streamFile) != 0xFFFFFFFF) {
sb->is_external = 1; /* -1 in extra_name means internal */
} else if (sb->section3_num == 0) {
sb->is_external = 1;
} else {
sb->autodetect_external = 1; /* let the parser guess later */
if (sb->resource_name[0] == '\0')
sb->autodetect_external = 0; /* no name */
if (sb->sectionX_size > 0 && sb->cfg_stream_name > sb->sectionX_size)
sb->autodetect_external = 0; /* name outside extra table == is internal */
}
2018-12-30 17:04:19 +01:00
//todo check sb->has_internal_names in case of garbage (ex POP:WW PS2), needs to guess external first
2018-12-30 17:04:19 +01:00
/* build a full name for stream */
if (sb->is_map) {
if (sb->resource_name[0]) {
snprintf(sb->readable_name, sizeof(sb->readable_name), "%s/%d/%08x/%s", sb->map_name, bank_streams, sb->header_id, sb->resource_name);
2018-12-30 17:04:19 +01:00
} else {
snprintf(sb->readable_name, sizeof(sb->readable_name), "%s/%d/%08x", sb->map_name, bank_streams, sb->header_id);
2018-12-30 17:04:19 +01:00
}
} else {
if (sb->resource_name[0]) {
snprintf(sb->readable_name, sizeof(sb->readable_name), "%08x/%s", sb->header_id, sb->resource_name);
} else {
snprintf(sb->readable_name, sizeof(sb->readable_name), "%08x", sb->header_id);
}
2018-12-30 17:04:19 +01:00
}
}
//;VGM_LOG("UBI SB: types "); for (int i = 0; i < 16; i++) { VGM_ASSERT(sb->types[i], "%02x=%i ",i,sb->types[i]); } VGM_LOG("\n");
2018-12-23 05:20:36 +01:00
if (sb->is_map) {
if (bank_streams == 0 || target_stream <= prev_streams || target_stream > sb->total_streams)
return 1; /* Target stream is not in this map */
} else {
if (sb->total_streams == 0) {
VGM_LOG("UBI SB: no streams\n");
goto fail;
}
if (target_stream < 0 || target_stream > sb->total_streams) {
VGM_LOG("UBI SB: wrong target stream (target=%i, total=%i)\n", target_stream, sb->total_streams);
goto fail;
}
VGM_ASSERT(sb->section3_num > 2, "UBI SB: section3 > 2 found\n");
}
if (!(sb->cfg_stream_id || sb->has_rotating_ids || sb->is_map) && sb->section3_num > 1) {
VGM_LOG("UBI SB: unexpected number of internal stream groups %i\n", sb->section3_num);
goto fail;
}
;VGM_LOG("UBI SB: target at %x (cfg %x), extra=%x, name=%s, id=%i\n", (uint32_t)sb->header_offset, sb->section2_entry_size, (uint32_t)sb->extra_offset, sb->resource_name, sb->stream_id);
/* happens in a few internal sounds from early Xbox games */
if (sb->stream_type > 0xFF) {
VGM_LOG("UBI SB: garbage in stream_type\n");
sb->stream_type = 0;
}
/* guess codec */
2018-12-17 12:01:00 +01:00
switch (sb->stream_type) {
case 0x00: /* platform default (rarely external) */
switch (sb->platform) {
case UBI_PC:
sb->codec = RAW_PCM;
break;
case UBI_PS2:
sb->codec = RAW_PSX;
break;
case UBI_PSP:
if (check_extensions(streamFile, "sb4,sm4")) {
sb->codec = FMT_VAG;
2018-12-17 12:01:00 +01:00
} else {
sb->codec = RAW_PSX;
}
break;
case UBI_XBOX:
sb->codec = RAW_XBOX;
break;
case UBI_GC:
case UBI_WII:
sb->codec = RAW_DSP;
break;
case UBI_X360:
sb->codec = RAW_XMA1;
break;
#if 0
case UBI_PS3:
/* Need to confirm */
sb->codec = RAW_AT3;
break;
#endif
case UBI_3DS:
sb->codec = FMT_CWAV;
break;
default:
VGM_LOG("UBI SB: unknown internal format\n");
goto fail;
}
break;
case 0x01: /* PCM (rarely used, ex. Wii/PSP/3DS) */
2017-11-11 17:46:18 +01:00
sb->codec = RAW_PCM;
break;
case 0x02: /* PS ADPCM (PS3) */
sb->codec = RAW_PSX;
break;
case 0x03: /* Ubi ADPCM (main external stream codec, has subtypes) */
sb->codec = UBI_ADPCM;
break;
case 0x04: /* Ogg (later PC games) */
sb->codec = FMT_OGG;
break;
case 0x05: /* AT3 (PSP, PS3) or XMA1 (X360) */
switch (sb->platform) {
case UBI_X360:
sb->codec = FMT_XMA1;
break;
case UBI_PS3:
case UBI_PSP:
sb->codec = FMT_AT3;
break;
default:
VGM_LOG("UBI SB: unknown codec for stream_type %x\n", sb->stream_type);
goto fail;
}
break;
case 0x06: /* PS ADPCM (later PSP and PS3(?) games) */
sb->codec = RAW_PSX;
break;
case 0x07:
sb->codec = RAW_AT3;
break;
case 0x08:
sb->codec = FMT_AT3;
break;
default:
VGM_LOG("UBI SB: unknown stream_type %x\n", sb->stream_type);
goto fail;
}
if (sb->codec == RAW_XMA1) {
/* this field is only seen in X360 games, points at XMA1 header in extra section */
sb->xma_header_offset = read_32bit(sb->header_offset + sb->cfg_xma_offset, streamFile) + sb->sectionX_offset;
}
/* uncommon but possible */
//VGM_ASSERT(sb->is_external && sb->section3_num != 0, "UBI SS: mixed external and internal streams\n");
/* seems that can be safely ignored */
//VGM_ASSERT(sb->is_external && sb->cfg_stream_id && sb->stream_id > 0, "UBI SB: unexpected external stream with stream id\n");
2018-12-22 22:47:24 +01:00
/* section 3: internal stream info */
if (!sb->is_external) {
if (sb->is_map) {
2018-12-22 22:47:24 +01:00
/* maps store internal sounds offsets in a separate table, find the matching entry */
for (i = 0; i < sb->section3_num; i++) {
2018-12-22 22:47:24 +01:00
off_t offset = sb->section3_offset + 0x14 * i;
off_t table_offset = read_32bit(offset + 0x04, streamFile) + sb->section3_offset;
uint32_t table_num = read_32bit(offset + 0x08, streamFile);
2018-12-22 22:47:24 +01:00
off_t table2_offset = read_32bit(offset + 0x0c, streamFile) + sb->section3_offset;
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;
if (index == sb->header_index) {
if (!(sb->cfg_stream_id || sb->has_rotating_ids) && table2_num > 1) {
VGM_LOG("UBI SB: unexpected number of internal stream map groups %i at %x\n", table2_num, (uint32_t)table2_offset);
goto fail;
}
2018-12-31 00:57:58 +01:00
if (sb->has_rotating_ids) {
sb->stream_id %= table2_num;
}
sb->stream_offset = read_32bit(table_offset + 0x08 * j + 0x04, streamFile);
for (k = 0; k < table2_num; k++) {
2018-12-22 22:47:24 +01:00
/* entry layout:
* 0x00 - group ID
* 0x04 - size with padding included
* 0x08 - size without padding
* 0x0c - absolute offset */
uint32_t id = read_32bit(table2_offset + 0x10 * k + 0x00, streamFile);
2018-12-22 22:47:24 +01:00
if (id == sb->stream_id) {
sb->stream_offset += read_32bit(table2_offset + 0x10 * k + 0x0c, streamFile);
break;
2018-12-22 22:47:24 +01:00
}
}
break;
}
}
if (sb->stream_offset)
break;
}
2018-12-23 16:55:20 +01:00
} else {
sb->stream_offset += sb->sounds_offset;
if ((sb->cfg_stream_id || sb->has_rotating_ids) && sb->section3_num > 1) { //todo test always using id
2018-12-23 16:55:20 +01:00
/* internal sounds are split into groups based on their type with their offsets being relative to group start
* this table contains sizes of each group, adjust offset based on group ID of our sound */
for (i = 0; i < sb->section3_num; i++) {
off_t offset = sb->section3_offset + sb->section3_entry_size * i;
/* table has unordered ids+size, so if our id doesn't match current data offset must be beyond */
if (read_32bit(offset + 0x00, streamFile) == sb->stream_id)
break;
sb->stream_offset += read_32bit(offset + 0x04, streamFile);
}
}
}
}
return 1;
fail:
return 0;
}
2019-01-05 04:32:07 +01:00
static int config_sb_platform(ubi_sb_header * sb, STREAMFILE *streamFile) {
char filename[PATH_LIMIT];
int filename_len;
char platform_char;
uint32_t version;
/* to find out hijacking platforms */
version = read_32bitLE(0x00, streamFile);
/* get X from .sbX/smX */
get_streamfile_name(streamFile,filename,sizeof(filename));
filename_len = strlen(filename);
platform_char = filename[filename_len - 1];
switch(platform_char) {
case '0':
sb->platform = UBI_PC;
break;
case '1':
sb->platform = UBI_PS2;
break;
case '2':
sb->platform = UBI_XBOX;
break;
case '3':
sb->platform = UBI_GC;
break;
case '4':
switch(version) { /* early PSP clashes with X360 */
case 0x0012000C: /* multiple games use this ID and all are sb4/sm4 */
sb->platform = UBI_PSP;
break;
default:
sb->platform = UBI_X360;
break;
}
break;
case '5':
switch(version) { /* 3DS could be sb8/sm8 but somehow hijacks extension */
case 0x00130001: /* Splinter Cell 3DS (2011) */
sb->platform = UBI_3DS;
break;
default:
sb->platform = UBI_PSP;
break;
}
break;
case '6':
sb->platform = UBI_PS3;
break;
case '7':
sb->platform = UBI_WII;
break;
default:
goto fail;
}
sb->big_endian =
sb->platform == UBI_GC ||
sb->platform == UBI_PS3 ||
sb->platform == UBI_X360 ||
sb->platform == UBI_WII;
return 1;
fail:
return 0;
}
static int config_sb_version(ubi_sb_header * sb, STREAMFILE *streamFile) {
2017-12-07 23:54:05 +01:00
int is_biadd_psp = 0;
/* Type 1 audio header varies with almost every game + platform (some kind of struct serialization?)
* Support is done case-by-case as offsets/order/fields change slightly. Header usually contains:
* - fixed part (id, type, stream size, extra offset, stream offset)
* - flags
* - stream samples (early games may or may not set it for internal streams, even in the same game)
* - stream size (same as the fixed part)
* - bitrate / original sample rate
* - sample rate
* - pcm bits?
* - channels
* - stream type
* - external filename or internal filename on some platforms (earlier versions)
* - external filename offset in the extra table (later versions)
* - end flags
*
* In between all those there are may be unused fields contain uninitialized memory garbage
* (null, part of strings, data from previous header, etc).
*
* Earlier games or those with bigger header size have most fields, while later remove
* or change around some, or sometimes only use fields for external streams.
* We only configure offsets for fields actually needed.
*/
/* common */
sb->section3_entry_size = 0x08;
sb->resource_name_size = 0x24; /* maybe 0x28 or 0x20 for some but ok enough (null terminated) */
/* this is same in all games since ~2003 */
sb->cfg_stream_size = 0x08;
sb->cfg_extra_offset = 0x0c;
sb->cfg_stream_offset = 0x10;
sb->and_external_flag = 0x01;
sb->and_samples_flag = 0x01;
/* Batman: Vengeance (2001)(PS2)-map 0x00000003 */
/* Batman: Vengeance (2001)(GC)-map 0x00000003 */
/* Disney's Tarzan: Untamed (2001)(GC)-map 0x00000003 */
/* Tom Clancy's Rainbow Six - Vegas 2 (2008)(PC)-? */
#if 0
/* Donald Duck: Goin' Quackers (2002)(GC)-map */
if (sb->version == 0x00000003 && sb->platform == UBI_GC) {
/* Stream types:
* 0x01: Nintendo DSP ADPCM
* 0x08: unsupported codec, looks like standard IMA with custom 0x29 bytes header
*/
sb->section1_entry_size = 0x40;
sb->section2_entry_size = 0x6c;
sb->map_version = 1;
sb->cfg_stream_size = 0x0c;
sb->cfg_extra_offset = 0x10;
sb->cfg_stream_offset = 0x14;
sb->cfg_external_flag = 0x30;
sb->cfg_stream_id = 0x34;
sb->cfg_num_samples = 0x48;
sb->cfg_sample_rate = 0x50;
sb->cfg_channels = 0x56;
sb->cfg_stream_type = 0x58;
sb->cfg_stream_name = 0x5c;
sb->has_short_channels = 1;
return 1;
}
#endif
#if 0
2018-12-22 22:47:24 +01:00
/* Splinter Cell (2002)(PC)-map */
/* Splinter Cell: Pandora Tomorrow (2004)(PC)-map */
if (sb->version == 0x00000007 && sb->platform == UBI_PC) {
/* Stream types:
* 0x01: PCM
* 0x02: unsupported codec, appears to be Ubi IMA in a blocked layout
* 0x04: Ubi IMA v3 (not Vorbis)
*/
sb->section1_entry_size = 0x58;
sb->section2_entry_size = 0x80;
sb->map_version = 1;
sb->cfg_stream_size = 0x0c;
sb->cfg_extra_offset = 0x10;
sb->cfg_stream_offset = 0x14;
//sb->cfg_samples_flag = 0x24; //?
sb->cfg_external_flag = 0x28;
sb->cfg_stream_id = 0x2c;
sb->cfg_num_samples = 0x30;
//sb->cfg_num_samples2 = 0x38;
sb->cfg_sample_rate = 0x44;
sb->cfg_channels = 0x4a;
sb->cfg_stream_type = 0x4c;
sb->cfg_stream_name = 0x50;
sb->has_short_channels = 1;
sb->has_internal_names = 1;
return 1;
}
#endif
2018-12-22 22:47:24 +01:00
/* Prince of Persia: Sands of Time (2003)(PC)-bank */
if ((sb->version == 0x000A0002 && sb->platform == UBI_PC) || /* (not sure if exists, just in case) */
(sb->version == 0x000A0004 && sb->platform == UBI_PC)) { /* main game */
sb->section1_entry_size = 0x64;
sb->section2_entry_size = 0x80;
sb->cfg_external_flag = 0x24;
sb->cfg_samples_flag = 0x28;
sb->cfg_stream_id = 0x2c;
sb->cfg_num_samples = 0x30;
sb->cfg_num_samples2 = 0x38;
sb->cfg_sample_rate = 0x44;
sb->cfg_channels = 0x4a;
sb->cfg_stream_type = 0x4c;
sb->cfg_stream_name = 0x50;
sb->has_short_channels = 1;
sb->has_internal_names = 1;
//has layer 0d (main game)
return 1;
}
2018-12-22 22:47:24 +01:00
/* Prince of Persia: Sands of Time (2003)(PS2)-bank */
if ((sb->version == 0x000A0002 && sb->platform == UBI_PS2) || /* Prince of Persia 1 port */
(sb->version == 0x000A0004 && sb->platform == UBI_PS2)) { /* main game */
sb->section1_entry_size = 0x48;
sb->section2_entry_size = 0x6c;
sb->cfg_external_flag = 0x18;
sb->cfg_samples_flag = 0x18;
sb->cfg_stream_id = 0x18;
sb->cfg_channels = 0x20;
sb->cfg_sample_rate = 0x24;
sb->cfg_num_samples = 0x30; /* may be null */
sb->cfg_num_samples2 = 0x38; /* may be null */
sb->cfg_stream_name = 0x40;
sb->cfg_stream_type = 0x68;
sb->and_external_flag = 0x04;
sb->and_samples_flag = 0x10;
sb->and_stream_id = 0x08;
sb->shr_stream_id = 3;
//has layer 0d (main game)
return 1;
}
2018-12-22 22:47:24 +01:00
/* Prince of Persia: Sands of Time (2003)(Xbox)-bank */
if ((sb->version == 0x000A0002 && sb->platform == UBI_XBOX) || /* Prince of Persia 1 port */
(sb->version == 0x000A0004 && sb->platform == UBI_XBOX)) { /* main game */
sb->section1_entry_size = 0x64;
sb->section2_entry_size = 0x78;
sb->cfg_external_flag = 0x24;
sb->cfg_stream_id = 0x28;
sb->cfg_samples_flag = 0x2c;
sb->cfg_num_samples = 0x30;
sb->cfg_num_samples2 = 0x38;
sb->cfg_sample_rate = 0x44;
sb->cfg_channels = 0x4a;
sb->cfg_stream_type = 0x4c; /* may contain garbage */
sb->cfg_stream_name = 0x50;
sb->has_short_channels = 1;
sb->has_internal_names = 1;
//has layer 0d (main game)
return 1;
}
/* Batman: Rise of Sin Tzu (2003)(GC)-map - 0x000A0002 */
/* Prince of Persia: Sands of Time (2003)(GC)-bank 0x000A0002/0x000A0004 */
if ((sb->version == 0x000A0002 && sb->platform == UBI_GC) || /* Prince of Persia 1 port */
(sb->version == 0x000A0004 && sb->platform == UBI_GC)) { /* main game */
sb->section1_entry_size = 0x64;
sb->section2_entry_size = 0x74;
sb->map_version = 2;
sb->cfg_external_flag = 0x20;
sb->cfg_stream_id = 0x24;
sb->cfg_samples_flag = 0x28;
sb->cfg_num_samples = 0x2c;
sb->cfg_num_samples = 0x34;
sb->cfg_sample_rate = 0x40;
sb->cfg_channels = 0x46;
sb->cfg_stream_type = 0x48;
sb->cfg_stream_name = 0x4c;
sb->has_short_channels = 1;
//has layer 0d (POP:SOT main game, Batman)
return 1;
}
/* Tom Clancy's Rainbow Six 3 (2003)(PS2)-bank */
if (sb->version == 0x000A0007 && sb->platform == UBI_PS2) {
sb->section1_entry_size = 0x48;
sb->section2_entry_size = 0x6c;
sb->cfg_external_flag = 0x18;
sb->cfg_samples_flag = 0x18;
sb->cfg_stream_id = 0x18;
sb->cfg_channels = 0x20;
sb->cfg_sample_rate = 0x24;
sb->cfg_num_samples = 0x30; /* may be null */
sb->cfg_num_samples2 = 0x38; /* may be null */
sb->cfg_stream_name = 0x40;
sb->cfg_stream_type = 0x68;
sb->and_external_flag = 0x04;
sb->and_samples_flag = 0x10;
sb->and_stream_id = 0x08;
sb->shr_stream_id = 3;
//has layer 0d
return 1;
}
/* Splincer Cell: Pandora Tomorrow (2006)(PS2)-bank (?) */
if (sb->version == 0x000A0008 && sb->platform == UBI_PS2) {
sb->section1_entry_size = 0x48;
sb->section2_entry_size = 0x6c;
sb->cfg_external_flag = 0x18;
sb->cfg_samples_flag = 0x18;
sb->cfg_stream_id = 0x18;
sb->cfg_channels = 0x20;
sb->cfg_sample_rate = 0x24;
sb->cfg_num_samples = 0x30; /* may be null */
sb->cfg_num_samples2 = 0x38; /* may be null */
sb->cfg_stream_name = 0x40;
sb->cfg_stream_type = 0x68;
sb->and_external_flag = 0x04;
sb->and_samples_flag = 0x10;
sb->and_stream_id = 0x08;
sb->shr_stream_id = 3;
//has layer?
return 1;
}
/* Myst IV Demo (2004)(PC)-bank */
if (sb->version == 0x00100000 && sb->platform == UBI_PC) { /* final game is different */
2017-12-03 20:25:50 +01:00
sb->section1_entry_size = 0x68;
sb->section2_entry_size = 0xa4;
sb->cfg_external_flag = 0x24;
sb->cfg_samples_flag = 0x28;
sb->cfg_stream_id = 0x2c;
sb->cfg_num_samples = 0x30;
sb->cfg_num_samples2 = 0x38;
sb->cfg_sample_rate = 0x44;
sb->cfg_channels = 0x4c;
sb->cfg_stream_type = 0x50;
sb->cfg_stream_name = 0x54;
2017-12-03 20:25:50 +01:00
sb->has_internal_names = 1;
return 1;
}
2018-12-22 22:47:24 +01:00
/* Prince of Persia: Warrior Within (2004)(PC)-bank */
if (sb->version == 0x00120009 && sb->platform == UBI_PC) {
sb->section1_entry_size = 0x6c;
sb->section2_entry_size = 0x84;
sb->cfg_external_flag = 0x24;
sb->cfg_samples_flag = 0x28;
sb->cfg_stream_id = 0x2c;
sb->cfg_num_samples = 0x30;
sb->cfg_num_samples2 = 0x38;
sb->cfg_sample_rate = 0x44;
sb->cfg_channels = 0x4c;
sb->cfg_stream_type = 0x50;
sb->cfg_stream_name = 0x54;
sb->has_internal_names = 1;
//no layers
return 1;
}
2018-12-22 22:47:24 +01:00
/* Prince of Persia: Warrior Within (2004)(PS2)-bank */
if (sb->version == 0x00120009 && sb->platform == UBI_PS2) {
sb->section1_entry_size = 0x48;
sb->section2_entry_size = 0x6c;
sb->cfg_external_flag = 0x18;
sb->cfg_samples_flag = 0x18;
sb->cfg_stream_id = 0x18;
sb->cfg_channels = 0x20;
sb->cfg_sample_rate = 0x24;
sb->cfg_num_samples = 0x30; /* may be null */
sb->cfg_num_samples2 = 0x38; /* may be null */
sb->cfg_stream_name = 0x40;
sb->cfg_stream_type = 0x68;
sb->and_external_flag = 0x04;
sb->and_samples_flag = 0x10;
sb->and_stream_id = 0x08;
sb->shr_stream_id = 3;
//no layers
return 1;
}
2018-12-22 22:47:24 +01:00
/* Prince of Persia: Warrior Within (2004)(Xbox)-bank */
if (sb->version == 0x00120009 && sb->platform == UBI_XBOX) {
sb->section1_entry_size = 0x6c;
sb->section2_entry_size = 0x90;
sb->cfg_external_flag = 0x24;
sb->cfg_stream_id = 0x28;
sb->cfg_samples_flag = 0x40;
sb->cfg_num_samples = 0x44;
sb->cfg_num_samples2 = 0x4c;
sb->cfg_sample_rate = 0x58;
sb->cfg_channels = 0x60;
sb->cfg_stream_type = 0x64; /* may contain garbage */
sb->cfg_stream_name = 0x68;
sb->has_internal_names = 1;
//no layers
return 1;
}
2018-12-22 22:47:24 +01:00
/* Prince of Persia: Warrior Within (2004)(GC)-bank */
if (sb->version == 0x00120009 && sb->platform == UBI_GC) {
sb->section1_entry_size = 0x6c;
sb->section2_entry_size = 0x78;
sb->cfg_external_flag = 0x20;
sb->cfg_stream_id = 0x24;
sb->cfg_samples_flag = 0x28;
sb->cfg_num_samples = 0x2c;
sb->cfg_num_samples2 = 0x34;
sb->cfg_sample_rate = 0x40;
sb->cfg_channels = 0x48;
sb->cfg_stream_type = 0x4c;
sb->cfg_stream_name = 0x50;
//no layers
return 1;
}
/* two configs with same id and both sb4/sm4; use project file as identifier */
if (sb->version == 0x0012000C && sb->platform == UBI_PSP) {
STREAMFILE * streamTest = open_streamfile_by_filename(streamFile, "BIAAUDIO.SP4");
2017-12-07 23:54:05 +01:00
if (streamTest) {
is_biadd_psp = 1;
close_streamfile(streamTest);
}
}
2018-12-22 22:47:24 +01:00
/* Prince of Persia: Revelations (2005)(PSP)-bank */
/* Splinter Cell: Essentials (2006)(PSP)-map */
/* Beowulf: The Game (2007)(PSP)-map */
if (sb->version == 0x0012000C && sb->platform == UBI_PSP && !is_biadd_psp) {
sb->section1_entry_size = 0x68;
sb->section2_entry_size = 0x84;
sb->map_version = 2;
sb->cfg_external_flag = 0x24;
sb->cfg_samples_flag = 0x28;
sb->cfg_stream_id = 0x2c;
sb->cfg_num_samples = 0x30;
sb->cfg_num_samples2 = 0x38;
sb->cfg_sample_rate = 0x44;
sb->cfg_channels = 0x4c;
sb->cfg_stream_type = 0x50;
sb->cfg_stream_name = 0x54;
sb->has_internal_names = 1;
//has layers 06 (SC:E only)
return 1;
}
2018-12-22 22:47:24 +01:00
/* Brothers in Arms - D-Day (2006)(PSP)-bank */
if (sb->version == 0x0012000C && sb->platform == UBI_PSP && is_biadd_psp) {
2017-12-07 23:54:05 +01:00
sb->section1_entry_size = 0x80;
sb->section2_entry_size = 0x94;
sb->cfg_external_flag = 0x24;
sb->cfg_samples_flag = 0x28;
sb->cfg_stream_id = 0x2c;
sb->cfg_num_samples = 0x30;
sb->cfg_num_samples2 = 0x38;
sb->cfg_sample_rate = 0x44;
sb->cfg_channels = 0x4c;
sb->cfg_stream_type = 0x50;
sb->cfg_stream_name = 0x54;
2017-12-07 23:54:05 +01:00
sb->has_internal_names = 1;
return 1;
}
/* Splinter Cell 3D (2011)(3DS)-map */
if (sb->version == 0x00130001 && sb->platform == UBI_3DS) {
sb->section1_entry_size = 0x48;
sb->section2_entry_size = 0x4c;
sb->map_version = 2;
sb->cfg_external_flag = 0x18;
sb->cfg_samples_flag = 0x18;
sb->cfg_stream_id = 0x18;
sb->cfg_num_samples = 0x1c;
sb->cfg_num_samples2 = 0x24;
sb->cfg_sample_rate = 0x30;
sb->cfg_channels = 0x38;
sb->cfg_stream_type = 0x3c;
sb->cfg_extra_name = 0x40;
sb->and_external_flag = 0x04;
sb->and_samples_flag = 0x10;
sb->and_stream_id = 0x08;
sb->shr_stream_id = 3;
//has layer 06
return 1;
}
2018-12-22 22:47:24 +01:00
/* Prince of Persia: The Two Thrones (2005)(PC)-bank */
if (sb->version == 0x00150000 && sb->platform == UBI_PC) {
sb->section1_entry_size = 0x68;
sb->section2_entry_size = 0x78;
sb->cfg_external_flag = 0x2c;
sb->cfg_samples_flag = 0x30;
sb->cfg_stream_id = 0x34;
sb->cfg_num_samples = 0x40;
sb->cfg_num_samples2 = 0x48;
sb->cfg_sample_rate = 0x54;
sb->cfg_channels = 0x5c;
sb->cfg_stream_type = 0x60;
sb->cfg_extra_name = 0x64;
//no layers
return 1;
}
2018-12-22 22:47:24 +01:00
/* Prince of Persia: The Two Thrones (2005)(PS2)-bank */
if (sb->version == 0x00150000 && sb->platform == UBI_PS2) {
sb->section1_entry_size = 0x48;
sb->section2_entry_size = 0x5c;
sb->cfg_external_flag = 0x20;
sb->cfg_samples_flag = 0x20;
sb->cfg_stream_id = 0x20;
sb->cfg_channels = 0x2c;
sb->cfg_sample_rate = 0x30;
sb->cfg_num_samples = 0x3c;
sb->cfg_num_samples2 = 0x44;
sb->cfg_extra_name = 0x4c;
sb->cfg_stream_type = 0x50;
sb->and_external_flag = 0x04;
sb->and_samples_flag = 0x10;
sb->and_stream_id = 0x08;
sb->shr_stream_id = 3;
//no layers
return 1;
}
2018-12-22 22:47:24 +01:00
/* Prince of Persia: The Two Thrones (2005)(Xbox)-bank */
if (sb->version == 0x00150000 && sb->platform == UBI_XBOX) {
sb->section1_entry_size = 0x48;
sb->section2_entry_size = 0x58;
sb->cfg_external_flag = 0x20;
sb->cfg_samples_flag = 0x20;
sb->cfg_stream_id = 0x20;
sb->cfg_num_samples = 0x28;
sb->cfg_num_samples2 = 0x30;
sb->cfg_sample_rate = 0x3c;
sb->cfg_channels = 0x44;
sb->cfg_stream_type = 0x48;
sb->cfg_extra_name = 0x4c;
sb->and_external_flag = 0x0008;
sb->and_samples_flag = 0x0400;
sb->and_stream_id = 0x0010;
sb->shr_stream_id = 4;
//no layers
return 1;
}
2018-12-22 22:47:24 +01:00
/* Prince of Persia: The Two Thrones (2005)(GC)-bank */
if (sb->version == 0x00150000 && sb->platform == UBI_GC) {
sb->section1_entry_size = 0x68;
sb->section2_entry_size = 0x6c;
sb->cfg_external_flag = 0x28;
//sb->cfg_stream_id = 0x2c; /* assumed */
sb->cfg_samples_flag = 0x30;
sb->cfg_num_samples = 0x3c;
sb->cfg_num_samples2 = 0x44;
sb->cfg_sample_rate = 0x50;
sb->cfg_channels = 0x58;
sb->cfg_stream_type = 0x5c;
sb->cfg_extra_name = 0x60;
//no layers
return 1;
}
2018-12-22 22:47:24 +01:00
/* Splinter Cell: Chaos Theory (2005)(PC)-map */
if (sb->version == 0x00120012 && sb->platform == UBI_PC) {
sb->section1_entry_size = 0x68;
sb->section2_entry_size = 0x60;
sb->map_version = 2;
2018-12-22 22:47:24 +01:00
sb->cfg_external_flag = 0x24;
//sb->cfg_stream_id = 0x28;
//sb->cfg_samples_flag = 0x2c; //todo test
sb->cfg_num_samples = 0x30;
sb->cfg_num_samples2 = 0x38;
sb->cfg_sample_rate = 0x44;
sb->cfg_channels = 0x4c;
sb->cfg_stream_type = 0x50;
sb->cfg_extra_name = 0x54;
return 1;
}
2018-12-22 22:47:24 +01:00
/* Splinter Cell: Chaos Theory (2005)(Xbox)-map */
if (sb->version == 0x00120012 && sb->platform == UBI_XBOX) {
sb->section1_entry_size = 0x48;
sb->section2_entry_size = 0x4c;
sb->map_version = 2;
2018-12-22 22:47:24 +01:00
sb->cfg_external_flag = 0x18;
sb->cfg_stream_id = 0x18;
sb->cfg_samples_flag = 0x18;
sb->cfg_num_samples = 0x1c;
sb->cfg_num_samples2 = 0x24;
sb->cfg_sample_rate = 0x30;
sb->cfg_channels = 0x38;
sb->cfg_stream_type = 0x3c;
sb->cfg_extra_name = 0x40;
sb->and_external_flag = 0x0008;
sb->and_samples_flag = 0x0400;
sb->and_stream_id = 0x0010;
sb->shr_stream_id = 4;
//no layers
return 1;
}
2018-06-30 21:24:19 +02:00
#if 0
2018-12-22 22:47:24 +01:00
/* Far cry: Instincts - Evolution (2006)(Xbox)-bank */
if (sb->version == 0x00170000 && sb->platform == UBI_XBOX) {
2018-06-30 21:24:19 +02:00
sb->section1_entry_size = 0x48;
sb->section2_entry_size = 0x6c;
sb->cfg_external_flag = 0x20?;
sb->cfg_stream_id = 0x20?;
sb->cfg_num_samples = 0x28;
sb->cfg_sample_rate = 0x3c;
sb->cfg_channels = 0x44;
sb->cfg_stream_type = 0x48;
sb->cfg_extra_name = 0x58;
2018-06-30 21:24:19 +02:00
?
2018-06-30 21:24:19 +02:00
return 1;
}
#endif
2018-12-22 22:47:24 +01:00
/* Red Steel (2006)(Wii)-bank */
if (sb->version == 0x00180006 && sb->platform == UBI_WII) {
sb->section1_entry_size = 0x68;
sb->section2_entry_size = 0x6c;
sb->cfg_external_flag = 0x28;
//sb->cfg_stream_id = 0x2c; /* assumed */
sb->cfg_samples_flag = 0x30;
sb->cfg_num_samples = 0x3c;
sb->cfg_num_samples2 = 0x44;
sb->cfg_sample_rate = 0x50;
sb->cfg_channels = 0x58;
sb->cfg_stream_type = 0x5c;
sb->cfg_extra_name = 0x60;
return 1;
}
2018-12-22 22:47:24 +01:00
/* Splinter Cell: Double Agent (2006)(PC)-map */
if (sb->version == 0x00180006 && sb->platform == UBI_PC) {
sb->section1_entry_size = 0x68;
sb->section2_entry_size = 0x7c;
sb->map_version = 3;
2018-12-22 22:47:24 +01:00
sb->cfg_external_flag = 0x2c;
//sb->cfg_samples_flag = 0x30; //todo test
sb->cfg_stream_id = 0x34;
sb->cfg_channels = 0x5c;
sb->cfg_sample_rate = 0x54;
sb->cfg_num_samples = 0x40;
sb->cfg_num_samples2 = 0x48;
sb->cfg_stream_type = 0x60;
sb->cfg_extra_name = 0x64;
return 1;
}
2018-12-22 22:47:24 +01:00
/* Splinter Cell: Double Agent (2006)(X360)-map */
if (sb->version == 0x00180006 && sb->platform == UBI_X360) {
sb->section1_entry_size = 0x68;
sb->section2_entry_size = 0x78;
sb->map_version = 3;
2018-12-22 22:47:24 +01:00
sb->cfg_external_flag = 0x2c;
sb->cfg_stream_id = 0x30;
sb->cfg_samples_flag = 0x34;
sb->cfg_channels = 0x5c;
sb->cfg_sample_rate = 0x54;
sb->cfg_num_samples = 0x40;
sb->cfg_num_samples2 = 0x48;
sb->cfg_stream_type = 0x60;
sb->cfg_extra_name = 0x64;
sb->cfg_xma_offset = 0x70;
//has layer 06
return 1;
}
2018-12-22 22:47:24 +01:00
/* Splinter Cell: Double Agent (2006)(Xbox)-map */
if (sb->version == 0x00160002 && sb->platform == UBI_XBOX) {
sb->section1_entry_size = 0x48;
sb->section2_entry_size = 0x58;
sb->map_version = 3;
2018-12-22 22:47:24 +01:00
sb->cfg_external_flag = 0x20;
sb->cfg_stream_id = 0x20;
sb->cfg_samples_flag = 0x20;
sb->cfg_num_samples = 0x28;
sb->cfg_num_samples2 = 0x30;
sb->cfg_sample_rate = 0x3c;
sb->cfg_channels = 0x44;
sb->cfg_stream_type = 0x48;
sb->cfg_extra_name = 0x4c;
sb->and_external_flag = 0x0008;
sb->and_samples_flag = 0x0400;
sb->and_stream_id = 0x0010;
sb->shr_stream_id = 4;
//no layers
return 1;
}
/* Splinter Cell: Double Agent (2006)(GC)-map */
if (sb->version == 0x00160002 && sb->platform == UBI_GC) {
sb->section1_entry_size = 0x68;
sb->section2_entry_size = 0x6c;
sb->map_version = 3;
sb->cfg_external_flag = 0x28;
sb->cfg_stream_id = 0x2c;
sb->cfg_samples_flag = 0x30; //todo test
sb->cfg_num_samples = 0x3c;
sb->cfg_num_samples2 = 0x44;
sb->cfg_sample_rate = 0x50;
sb->cfg_channels = 0x58;
sb->cfg_stream_type = 0x5c;
sb->cfg_extra_name = 0x60;
return 1;
}
/* Splinter Cell: Double Agent (2006)(PS2)-map */
if (sb->version == 0x00160002 && sb->platform == UBI_PS2) {
sb->section1_entry_size = 0x48;
sb->section2_entry_size = 0x54;
sb->map_version = 3;
sb->cfg_external_flag = 0x20;
sb->cfg_samples_flag = 0x20;
sb->cfg_stream_id = 0x20;
sb->cfg_channels = 0x28;
sb->cfg_sample_rate = 0x2c;
sb->cfg_num_samples = 0x34;
sb->cfg_num_samples2 = 0x38;
sb->cfg_extra_name = 0x44;
sb->cfg_stream_type = 0x48;
sb->and_external_flag = 0x04;
sb->and_samples_flag = 0x10;
sb->and_stream_id = 0x08;
sb->shr_stream_id = 3;
return 1;
}
/* Open Season (2005)(PS2)-map [0x00180003] */
/* Open Season (2005)(PSP)-map [0x00180003] */
/* Prince of Persia: Rival Swords (2007)(PSP)-bank [0x00180005] */
/* Rainbow Six Vegas (2007)(PSP)-bank [0x00180006] */
/* Star Wars: Lethal Alliance (2006)(PSP)-map [0x00180007] */
if ((sb->version == 0x00180003 && sb->platform == UBI_PS2) ||
(sb->version == 0x00180003 && sb->platform == UBI_PSP) ||
(sb->version == 0x00180005 && sb->platform == UBI_PSP) ||
(sb->version == 0x00180006 && sb->platform == UBI_PSP) ||
(sb->version == 0x00180007 && sb->platform == UBI_PSP)) {
2017-12-07 23:54:05 +01:00
sb->section1_entry_size = 0x48;
sb->section2_entry_size = 0x54;
2017-11-11 17:46:18 +01:00
sb->map_version = 3;
sb->cfg_external_flag = 0x20;
sb->cfg_samples_flag = 0x20;
sb->cfg_stream_id = 0x20;
sb->cfg_channels = 0x28;
sb->cfg_sample_rate = 0x2c;
sb->cfg_num_samples = 0x34;
sb->cfg_num_samples2 = 0x3c;
sb->cfg_extra_name = 0x44;
sb->cfg_stream_type = 0x48;
2017-11-11 17:46:18 +01:00
sb->and_external_flag = 0x04;
sb->and_samples_flag = 0x10;
sb->and_stream_id = 0x08;
sb->shr_stream_id = 3;
//has layer 06
2017-11-11 17:46:18 +01:00
return 1;
}
/* Prince of Persia: Rival Swords (2007)(Wii)-bank */
if (sb->version == 0x00190003 && sb->platform == UBI_WII) {
sb->section1_entry_size = 0x68;
sb->section2_entry_size = 0x70;
sb->cfg_external_flag = 0x28;
//sb->cfg_stream_id = 0x2c; /* assumed */
sb->cfg_samples_flag = 0x30;
sb->cfg_channels = 0x3c;
sb->cfg_sample_rate = 0x40;
sb->cfg_num_samples = 0x48;
sb->cfg_num_samples2 = 0x50;
sb->cfg_extra_name = 0x58;
sb->cfg_stream_type = 0x5c;
//has layer 06 (TMNT)
2017-12-07 23:54:05 +01:00
return 1;
}
/* TMNT (2007)(GC)-bank */
if (sb->version == 0x00190002 && sb->platform == UBI_GC) {
sb->section1_entry_size = 0x68;
sb->section2_entry_size = 0x6c;
sb->cfg_external_flag = 0x28;
//sb->cfg_stream_id = 0x2c; /* assumed */
sb->cfg_samples_flag = 0x30;
sb->cfg_channels = 0x3c;
sb->cfg_sample_rate = 0x40;
sb->cfg_num_samples = 0x48;
sb->cfg_num_samples2 = 0x50;
sb->cfg_extra_name = 0x58;
sb->cfg_stream_type = 0x5c;
return 1;
}
2018-12-17 11:20:53 +01:00
2018-12-22 22:47:24 +01:00
/* TMNT (2007)(PC)-bank */
2018-12-17 11:20:53 +01:00
if (sb->version == 0x00190002 && sb->platform == UBI_PC) {
sb->section1_entry_size = 0x68;
sb->section2_entry_size = 0x74;
sb->cfg_external_flag = 0x28;
sb->cfg_stream_id = 0x2c;
sb->cfg_samples_flag = 0x30; //todo test
sb->cfg_channels = 0x3c;
sb->cfg_sample_rate = 0x40;
sb->cfg_num_samples = 0x48;
sb->cfg_num_samples2 = 0x50;
sb->cfg_extra_name = 0x58;
sb->cfg_stream_type = 0x5c;
2018-12-17 11:20:53 +01:00
return 1;
}
2018-12-22 22:47:24 +01:00
/* TMNT (2007)(PS2)-bank */
if (sb->version == 0x00190002 && sb->platform == UBI_PS2) {
2017-12-03 20:25:50 +01:00
sb->section1_entry_size = 0x48;
sb->section2_entry_size = 0x5c;
sb->cfg_external_flag = 0x20;
sb->cfg_samples_flag = 0x20;
//sb->cfg_stream_id = 0x20; /* assumed */
sb->cfg_channels = 0x28;
sb->cfg_sample_rate = 0x2c;
sb->cfg_num_samples = 0x34;
sb->cfg_num_samples2 = 0x3c;
sb->cfg_extra_name = 0x44;
sb->cfg_stream_type = 0x48;
2017-12-03 20:25:50 +01:00
sb->and_external_flag = 0x04;
sb->and_samples_flag = 0x10;
//has layer 06
2017-12-03 20:25:50 +01:00
return 1;
}
/* TMNT (2007)(X360)-bank */
2018-12-17 11:20:53 +01:00
if (sb->version == 0x00190002 && sb->platform == UBI_X360) {
sb->section1_entry_size = 0x68;
sb->section2_entry_size = 0x70;
sb->cfg_external_flag = 0x28;
//sb->cfg_stream_id = 0x2c; /* assumed */
sb->cfg_samples_flag = 0x30;
sb->cfg_channels = 0x3c;
sb->cfg_sample_rate = 0x40;
sb->cfg_num_samples = 0x48;
sb->cfg_num_samples2 = 0x50;
sb->cfg_stream_type = 0x5c;
sb->cfg_extra_name = 0x58;
sb->cfg_xma_offset = 0x6c;
2018-12-17 11:20:53 +01:00
return 1;
}
/* TMNT (2007)(PSP)-map */
if (sb->version == 0x00190001 && sb->platform == UBI_PSP) {
sb->section1_entry_size = 0x48;
sb->section2_entry_size = 0x58;
sb->map_version = 3;
sb->cfg_external_flag = 0x20;
sb->cfg_samples_flag = 0x20;
//sb->cfg_stream_id = 0x20; /* assumed */
sb->cfg_channels = 0x28;
sb->cfg_sample_rate = 0x2c;
sb->cfg_num_samples = 0x34;
sb->cfg_num_samples2 = 0x3c;
sb->cfg_stream_type = 0x48;
sb->cfg_extra_name = 0x44;
sb->and_external_flag = 0x04;
sb->and_samples_flag = 0x10;
//has layer 06
2018-12-17 11:20:53 +01:00
return 1;
}
2018-12-22 22:47:24 +01:00
/* Surf's Up (2007)(PC)-bank */
if (sb->version == 0x00190005 && sb->platform == UBI_PC) {
2018-02-17 02:07:43 +01:00
sb->section1_entry_size = 0x68;
sb->section2_entry_size = 0x74;
sb->cfg_external_flag = 0x28;
//sb->cfg_stream_id = 0x2c; /* assumed */
sb->cfg_samples_flag = 0x30; //todo test
sb->cfg_channels = 0x3c;
sb->cfg_sample_rate = 0x40;
sb->cfg_num_samples = 0x48;
sb->cfg_num_samples2 = 0x50;
sb->cfg_extra_name = 0x58;
sb->cfg_stream_type = 0x5c;
2018-02-17 02:07:43 +01:00
return 1;
}
2018-12-22 22:47:24 +01:00
/* Surf's Up (2007)(PS3)-bank */
/* Splinter Cell: Double Agent (2007)(PS3)-map */
if (sb->version == 0x00190005 && sb->platform == UBI_PS3) {
sb->section1_entry_size = 0x68;
sb->section2_entry_size = 0x70;
sb->map_version = 3;
2018-12-22 22:47:24 +01:00
sb->cfg_external_flag = 0x28;
sb->cfg_stream_id = 0x2c;
sb->cfg_samples_flag = 0x30;
sb->cfg_channels = 0x3c;
sb->cfg_sample_rate = 0x40;
sb->cfg_num_samples = 0x48;
sb->cfg_num_samples2 = 0x50;
sb->cfg_extra_name = 0x58;
sb->cfg_stream_type = 0x5c;
return 1;
}
2018-12-22 22:47:24 +01:00
/* Surf's Up (2007)(X360)-bank */
if (sb->version == 0x00190005 && sb->platform == UBI_X360) {
sb->section1_entry_size = 0x68;
sb->section2_entry_size = 0x70;
sb->cfg_external_flag = 0x28;
//sb->cfg_stream_id = 0x2c; /* assumed */
sb->cfg_samples_flag = 0x30;
sb->cfg_channels = 0x3c;
sb->cfg_sample_rate = 0x40;
sb->cfg_num_samples = 0x48;
sb->cfg_num_samples2 = 0x50;
sb->cfg_stream_type = 0x5c;
sb->cfg_extra_name = 0x58;
sb->cfg_xma_offset = 0x6c;
return 1;
}
/* Surf's Up (2007)(PSP)-map */
if (sb->version == 0x00190005 && sb->platform == UBI_PSP) {
sb->section1_entry_size = 0x48;
sb->section2_entry_size = 0x58;
sb->map_version = 3;
sb->cfg_external_flag = 0x20;
sb->cfg_samples_flag = 0x20;
//sb->cfg_stream_id = 0x20; /* assumed */
sb->cfg_channels = 0x28;
sb->cfg_sample_rate = 0x2c;
sb->cfg_num_samples = 0x34;
sb->cfg_num_samples2 = 0x38;
sb->cfg_extra_name = 0x44;
sb->cfg_stream_type = 0x48;
sb->and_external_flag = 0x04;
sb->and_samples_flag = 0x10;
//no layers
return 1;
}
/* Michael Jackson: The Experience (2010)(PSP)-map */
if (sb->version == 0x001d0000 && sb->platform == UBI_PSP) {
sb->section1_entry_size = 0x40;
sb->section2_entry_size = 0x60;
sb->map_version = 3;
sb->cfg_external_flag = 0x20;
sb->cfg_samples_flag = 0x20;
//sb->cfg_stream_id = 0x20; /* assumed */
sb->cfg_channels = 0x28;
sb->cfg_sample_rate = 0x30;
sb->cfg_num_samples = 0x38;
sb->cfg_num_samples2 = 0x40;
sb->cfg_extra_name = 0x48;
sb->cfg_stream_type = 0x4c;
sb->and_external_flag = 0x04;
sb->and_samples_flag = 0x20;
//no layers
return 1;
}
/* Splinter Cell Classic Trilogy HD (2011)(PS3)-map */
if (sb->version == 0x001d0000 && sb->platform == UBI_PS3) {
sb->section1_entry_size = 0x5c;
sb->section2_entry_size = 0x80;
sb->map_version = 3;
sb->cfg_external_flag = 0x28;
sb->cfg_stream_id = 0x30;
sb->cfg_samples_flag = 0x34;
sb->cfg_channels = 0x44;
sb->cfg_sample_rate = 0x4c;
sb->cfg_num_samples = 0x54;
sb->cfg_num_samples2 = 0x5c;
sb->cfg_extra_name = 0x64;
sb->cfg_stream_type = 0x68;
//has layer 06
return 1;
}
VGM_LOG("UBI SB: unknown SB/SM version+platform for %08x\n", sb->version);
return 0;
}