mirror of
https://github.com/vgmstream/vgmstream.git
synced 2025-01-19 00:04:04 +01:00
Fix .sm1+ss1 with garbage streams [Splinter Cell Pandora Tomorrow (PS2)]
This commit is contained in:
parent
6c240e0a78
commit
54ed84f96f
@ -400,6 +400,10 @@
|
||||
RelativePath=".\meta\ubi_sb_streamfile.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\meta\ubi_sb_garbage_streamfile.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\meta\ubi_lyn_streamfile.h"
|
||||
>
|
||||
|
@ -142,6 +142,7 @@
|
||||
<ClInclude Include="meta\txth_streamfile.h" />
|
||||
<ClInclude Include="meta\ubi_bao_streamfile.h" />
|
||||
<ClInclude Include="meta\ubi_sb_streamfile.h" />
|
||||
<ClInclude Include="meta\ubi_sb_garbage_streamfile.h" />
|
||||
<ClInclude Include="meta\ubi_lyn_streamfile.h" />
|
||||
<ClInclude Include="meta\meta.h" />
|
||||
<ClInclude Include="meta\hca_keys.h" />
|
||||
|
@ -191,6 +191,9 @@
|
||||
<ClInclude Include="meta\ubi_sb_streamfile.h">
|
||||
<Filter>meta\Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="meta\ubi_sb_garbage_streamfile.h">
|
||||
<Filter>meta\Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="meta\ubi_lyn_streamfile.h">
|
||||
<Filter>meta\Header Files</Filter>
|
||||
</ClInclude>
|
||||
|
@ -7,6 +7,9 @@
|
||||
#define SB_MAX_LAYER_COUNT 16 /* arbitrary max */
|
||||
#define SB_MAX_CHAIN_COUNT 256 /* +150 exist in Tonic Trouble */
|
||||
|
||||
#define LAYER_HIJACK_GRAW_X360 1
|
||||
#define LAYER_HIJACK_SCPT_PS2 2
|
||||
|
||||
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, FMT_MPDX, UBI_IMA_SCE } ubi_sb_codec;
|
||||
typedef enum { UBI_PC, UBI_DC, 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;
|
||||
@ -2065,7 +2068,9 @@ static int parse_type_layer(ubi_sb_header* sb, off_t offset, STREAMFILE* sf) {
|
||||
|
||||
if (sb->sample_rate != sample_rate || sb->stream_type != stream_type) {
|
||||
VGM_LOG("UBI SB: %i layer headers don't match at %x > %x\n", sb->layer_count, (uint32_t)offset, (uint32_t)table_offset);
|
||||
if (!sb->cfg.ignore_layer_error) /* layers of different rates happens sometimes */
|
||||
/* Layers of different rates happens sometimes. From decompilations, first layer's sample rate
|
||||
* looks used as main, though lower sample rate layer only seem to appear to after first. */
|
||||
if (!sb->cfg.ignore_layer_error)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
@ -2463,11 +2468,12 @@ static int parse_offsets(ubi_sb_header* sb, STREAMFILE* sf) {
|
||||
}
|
||||
} else {
|
||||
/* banks store internal sounds after all headers and adjusted by the subblock table, find the matching entry */
|
||||
off_t sounds_offset;
|
||||
|
||||
if (sb->is_external)
|
||||
return 1;
|
||||
|
||||
off_t sounds_offset = sb->section3_offset + sb->cfg.section3_entry_size*sb->section3_num;
|
||||
sounds_offset = sb->section3_offset + sb->cfg.section3_entry_size*sb->section3_num;
|
||||
if (sb->cfg.is_padded_sounds_offset)
|
||||
sounds_offset = align_size_to_block(sounds_offset, 0x10);
|
||||
sb->stream_offset = sounds_offset + sb->stream_offset;
|
||||
@ -3259,8 +3265,8 @@ static int config_sb_version(ubi_sb_header* sb, STREAMFILE* sf) {
|
||||
sb->cfg.audio_ram_streamed_flag = 0x1c;
|
||||
sb->cfg.audio_ram_streamed_and = (1 << 3);
|
||||
sb->cfg.audio_loop_and = (1 << 4);
|
||||
/* some RAM sounds have bad sizes */
|
||||
/* some amb .ss1 have garbage data mixed in, bad extraction/unused crap? */
|
||||
/* some RAM sounds have bad sizes (ex #252, #10874) */
|
||||
sb->cfg.layer_hijack = LAYER_HIJACK_SCPT_PS2; /* some amb .ss1 layers (ex. #226, not #1927) have mixed garbage */
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
@ -3743,7 +3749,7 @@ static int config_sb_version(ubi_sb_header* sb, STREAMFILE* sf) {
|
||||
|
||||
config_sb_layer_he(sb, 0x20, 0x38, 0x40, 0x48);
|
||||
config_sb_layer_sh(sb, 0x30, 0x00, 0x08, 0x0c, 0x14);
|
||||
sb->cfg.layer_hijack = 1; /* WTF!!! layer format different from other layers using same id!!! */
|
||||
sb->cfg.layer_hijack = LAYER_HIJACK_GRAW_X360; /* WTF!!! layer format different from other layers using same id!!! */
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
41
src/meta/ubi_sb_garbage_streamfile.h
Normal file
41
src/meta/ubi_sb_garbage_streamfile.h
Normal file
@ -0,0 +1,41 @@
|
||||
#ifndef _UBI_SB_GARBAGE_STREAMFILE_H_
|
||||
#define _UBI_SB_GARBAGE_STREAMFILE_H_
|
||||
#include "deblock_streamfile.h"
|
||||
|
||||
|
||||
/* In typical Ubisoft-insane fashion, some SC:PT PS2 (not GC) streams have mixed garbage (after 0x6B00 bytes has 0x4240).
|
||||
* No apparent flag but seems to be related to stream sizes or samples (only files of +10MB, but not all).
|
||||
*
|
||||
* Since garbage is consistent between all files we can detect by checking expected crap. stream_sizes do take
|
||||
* into account extra crap, while layers don't (offset field assumes no crap), so we use a separate de-garbage
|
||||
* streamfile to simulate. */
|
||||
static int is_garbage_stream(STREAMFILE* sf) {
|
||||
/* must test from file's beginning, not stream's */
|
||||
return get_streamfile_size(sf) >= 0x00800000 &&
|
||||
read_u32be(0x6B00, sf) == 0x6047BF7F &&
|
||||
read_u32be(0x6B04, sf) == 0x94FACC01;
|
||||
}
|
||||
|
||||
//static size_t get_garbage_stream_size(off_t offset, size_t size) {
|
||||
// /* readjust size removing all possible garbage taking into account offset */
|
||||
//}
|
||||
|
||||
static void block_callback(STREAMFILE* sf, deblock_io_data* data) {
|
||||
data->block_size = 0x6b00 + 0x4240;
|
||||
data->data_size = 0x6b00;
|
||||
}
|
||||
|
||||
static STREAMFILE* setup_ubi_sb_garbage_streamfile_f(STREAMFILE* new_sf) {
|
||||
//STREAMFILE *new_sf = NULL;
|
||||
deblock_config_t cfg = {0};
|
||||
|
||||
cfg.stream_start = 0;
|
||||
cfg.block_callback = block_callback;
|
||||
|
||||
/* setup sf */
|
||||
//new_sf = open_wrap_streamfile(sf);
|
||||
new_sf = open_io_deblock_streamfile_f(new_sf, &cfg);
|
||||
return new_sf;
|
||||
}
|
||||
|
||||
#endif /* _UBI_SB_GARBAGE_STREAMFILE_H_ */
|
@ -1,424 +1,417 @@
|
||||
#ifndef _UBI_SB_STREAMFILE_H_
|
||||
#define _UBI_SB_STREAMFILE_H_
|
||||
#include "../streamfile.h"
|
||||
|
||||
|
||||
typedef struct {
|
||||
/* config */
|
||||
off_t stream_offset;
|
||||
off_t stream_size;
|
||||
int layer_number;
|
||||
int layer_count;
|
||||
int layer_max;
|
||||
int big_endian;
|
||||
int layer_hijack;
|
||||
|
||||
/* internal config */
|
||||
off_t header_next_start; /* offset to header field */
|
||||
off_t header_sizes_start; /* offset to header table */
|
||||
off_t header_data_start; /* offset to header data */
|
||||
off_t block_next_start; /* offset to block field */
|
||||
off_t block_sizes_start; /* offset to block table */
|
||||
off_t block_data_start; /* offset to block data */
|
||||
size_t header_size; /* derived */
|
||||
|
||||
/* state */
|
||||
off_t logical_offset; /* fake offset */
|
||||
off_t physical_offset; /* actual offset */
|
||||
size_t block_size; /* current size */
|
||||
size_t next_block_size; /* next size */
|
||||
size_t skip_size; /* size from block start to reach data */
|
||||
size_t data_size; /* usable size in a block */
|
||||
|
||||
size_t logical_size;
|
||||
} ubi_sb_io_data;
|
||||
|
||||
|
||||
static size_t ubi_sb_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length, ubi_sb_io_data* data) {
|
||||
int32_t(*read_32bit)(off_t, STREAMFILE*) = data->big_endian ? read_32bitBE : read_32bitLE;
|
||||
size_t total_read = 0;
|
||||
int i;
|
||||
|
||||
|
||||
/* re-start when previous offset (can't map logical<>physical offsets) */
|
||||
if (data->logical_offset < 0 || offset < data->logical_offset) {
|
||||
data->physical_offset = data->stream_offset;
|
||||
data->logical_offset = 0x00;
|
||||
data->data_size = 0;
|
||||
|
||||
/* process header block (slightly different and data size may be 0) */
|
||||
{
|
||||
data->block_size = data->header_size;
|
||||
data->next_block_size = read_32bit(data->physical_offset + data->header_next_start, streamfile);
|
||||
|
||||
if (data->header_sizes_start) {
|
||||
data->skip_size = data->header_data_start;
|
||||
for (i = 0; i < data->layer_number; i++) {
|
||||
data->skip_size += read_32bit(data->physical_offset + data->header_sizes_start + i*0x04, streamfile);
|
||||
}
|
||||
data->data_size = read_32bit(data->physical_offset + data->header_sizes_start + data->layer_number*0x04, streamfile);
|
||||
}
|
||||
|
||||
if (data->data_size == 0) {
|
||||
data->physical_offset += data->block_size;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* read blocks */
|
||||
while (length > 0) {
|
||||
|
||||
/* ignore EOF */
|
||||
if (offset < 0 || data->physical_offset >= data->stream_offset + data->stream_size) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* process new block */
|
||||
if (data->data_size == 0) {
|
||||
data->block_size = data->next_block_size;
|
||||
if (data->block_next_start) /* not set when fixed block size */
|
||||
data->next_block_size = read_32bit(data->physical_offset + data->block_next_start, streamfile);
|
||||
|
||||
data->skip_size = data->block_data_start;
|
||||
for (i = 0; i < data->layer_number; i++) {
|
||||
data->skip_size += read_32bit(data->physical_offset + data->block_sizes_start + i*0x04, streamfile);
|
||||
}
|
||||
data->data_size = read_32bit(data->physical_offset + data->block_sizes_start + data->layer_number*0x04, streamfile);
|
||||
}
|
||||
|
||||
/* move to next block */
|
||||
if (offset >= data->logical_offset + data->data_size) {
|
||||
if (data->block_size == 0 || data->block_size == 0xFFFFFFFF)
|
||||
break;
|
||||
data->physical_offset += data->block_size;
|
||||
data->logical_offset += data->data_size;
|
||||
data->data_size = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* read data */
|
||||
{
|
||||
size_t bytes_consumed, bytes_done, to_read;
|
||||
|
||||
bytes_consumed = offset - data->logical_offset;
|
||||
to_read = data->data_size - bytes_consumed;
|
||||
if (to_read > length)
|
||||
to_read = length;
|
||||
bytes_done = read_streamfile(dest, data->physical_offset + data->skip_size + bytes_consumed, to_read, streamfile);
|
||||
|
||||
total_read += bytes_done;
|
||||
dest += bytes_done;
|
||||
offset += bytes_done;
|
||||
length -= bytes_done;
|
||||
|
||||
if (bytes_done != to_read || bytes_done == 0) {
|
||||
break; /* error/EOF */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return total_read;
|
||||
}
|
||||
|
||||
static size_t ubi_sb_io_size(STREAMFILE *streamfile, ubi_sb_io_data* data) {
|
||||
uint8_t buf[1];
|
||||
|
||||
if (data->logical_size)
|
||||
return data->logical_size;
|
||||
|
||||
/* force a fake read at max offset, to get max logical_offset (will be reset next read) */
|
||||
ubi_sb_io_read(streamfile, buf, 0x7FFFFFFF, 1, data);
|
||||
data->logical_size = data->logical_offset;
|
||||
|
||||
return data->logical_size;
|
||||
}
|
||||
|
||||
|
||||
static int ubi_sb_io_init(STREAMFILE *streamfile, ubi_sb_io_data* data) {
|
||||
int32_t (*read_32bit)(off_t,STREAMFILE*) = data->big_endian ? read_32bitBE : read_32bitLE;
|
||||
off_t offset = data->stream_offset;
|
||||
uint32_t version;
|
||||
int i;
|
||||
|
||||
if (data->stream_offset + data->stream_size > get_streamfile_size(streamfile)) {
|
||||
VGM_LOG("UBI SB: bad size\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Layers have a main header, then headered blocks with data.
|
||||
* We configure stuff to unify parsing of all variations. */
|
||||
version = (uint32_t)read_32bit(offset+0x00, streamfile);
|
||||
|
||||
/* it was bound to happen... orz */
|
||||
if (data->layer_hijack == 1 && version == 0x000B0008)
|
||||
version = 0xFFFF0007;
|
||||
|
||||
switch(version) {
|
||||
case 0x00000002: /* Splinter Cell */
|
||||
/* - layer header
|
||||
* 0x04: layer count
|
||||
* 0x08: stream size
|
||||
* 0x0c: block header size
|
||||
* 0x10: block size (fixed)
|
||||
* 0x14: min layer size?
|
||||
* - block header
|
||||
* 0x00: block number
|
||||
* 0x04: block offset
|
||||
* 0x08+(04*N): layer size per layer
|
||||
* 0xNN: layer data per layer */
|
||||
data->layer_max = read_32bit(offset+0x04, streamfile);
|
||||
|
||||
data->header_next_start = 0x10;
|
||||
data->header_sizes_start = 0;
|
||||
data->header_data_start = 0x18;
|
||||
|
||||
data->block_next_start = 0;
|
||||
data->block_sizes_start = 0x08;
|
||||
data->block_data_start = 0x08 + data->layer_max*0x04;
|
||||
break;
|
||||
|
||||
case 0x00000003: /* Rainbow Six 3 */
|
||||
/* - layer header
|
||||
* 0x04: layer count
|
||||
* 0x08: stream size
|
||||
* 0x0c: block header size
|
||||
* 0x10: block size (fixed)
|
||||
* 0x14: min layer data?
|
||||
* 0x18: size of header sizes and headers
|
||||
* 0x1c+(04*N): header size per layer
|
||||
* - block header
|
||||
* 0x00: block number
|
||||
* 0x04: block offset
|
||||
* 0x08+(04*N): layer size per layer
|
||||
* 0xNN: layer data per layer */
|
||||
data->layer_max = read_32bit(offset+0x04, streamfile);
|
||||
|
||||
data->header_next_start = 0x10;
|
||||
data->header_sizes_start = 0x1c;
|
||||
data->header_data_start = 0x1c + data->layer_max*0x04;
|
||||
|
||||
data->block_next_start = 0;
|
||||
data->block_sizes_start = 0x08;
|
||||
data->block_data_start = 0x08 + data->layer_max*0x04;
|
||||
break;
|
||||
|
||||
case 0x00000004: /* Prince of Persia: Sands of Time, Batman: Rise of Sin Tzu */
|
||||
/* - layer header
|
||||
* 0x04: layer count
|
||||
* 0x08: stream size
|
||||
* 0x0c: block count
|
||||
* 0x10: block header size
|
||||
* 0x14: block size (fixed)
|
||||
* 0x18: min layer data?
|
||||
* 0x1c: size of header sizes and headers
|
||||
* 0x20+(04*N): header size per layer
|
||||
* - block header
|
||||
* 0x00: block number
|
||||
* 0x04: block offset
|
||||
* 0x08: always 0x03
|
||||
* 0x0c+(04*N): layer size per layer
|
||||
* 0xNN: layer data per layer */
|
||||
data->layer_max = read_32bit(offset+0x04, streamfile);
|
||||
|
||||
data->header_next_start = 0x14;
|
||||
data->header_sizes_start = 0x20;
|
||||
data->header_data_start = 0x20 + data->layer_max*0x04;
|
||||
|
||||
data->block_next_start = 0;
|
||||
data->block_sizes_start = 0x0c;
|
||||
data->block_data_start = 0x0c + data->layer_max*0x04;
|
||||
break;
|
||||
|
||||
case 0x00000007: /* Splinter Cell: Essentials, Splinter Cell 3D */
|
||||
/* - layer header
|
||||
* 0x04: config?
|
||||
* 0x08: layer count
|
||||
* 0x0c: stream size
|
||||
* 0x10: block count
|
||||
* 0x14: block header size
|
||||
* 0x18: block size (fixed)
|
||||
* 0x1c+(04*8): min layer data? for 8 layers (-1 after layer count)
|
||||
* 0x3c: size of header sizes and headers
|
||||
* 0x40+(04*N): header size per layer
|
||||
* 0xNN: header data per layer
|
||||
* - block header
|
||||
* 0x00: block number
|
||||
* 0x04: block offset
|
||||
* 0x08: always 0x03
|
||||
* 0x0c+(04*N): layer size per layer
|
||||
* 0xNN: layer data per layer */
|
||||
data->layer_max = read_32bit(offset+0x08, streamfile);
|
||||
|
||||
data->header_next_start = 0x18;
|
||||
data->header_sizes_start = 0x40;
|
||||
data->header_data_start = 0x40 + data->layer_max*0x04;
|
||||
|
||||
data->block_next_start = 0;
|
||||
data->block_sizes_start = 0x0c;
|
||||
data->block_data_start = 0x0c + data->layer_max*0x04;
|
||||
break;
|
||||
|
||||
case 0xFFFF0007: /* Ghost Recon Advanced Warfighter (X360) */
|
||||
/* - layer header
|
||||
* 0x04: config?
|
||||
* 0x08: layer count
|
||||
* 0x0c: stream size
|
||||
* 0x10: block count
|
||||
* 0x14: block header size
|
||||
* 0x18: block size (fixed)
|
||||
* 0x1c+(04*11): min layer data? for 11 layers (-1 after layer count)
|
||||
* 0x48: size of header sizes and headers
|
||||
* 0x4c+(04*N): header size per layer
|
||||
* 0xNN: header data per layer
|
||||
* - block header
|
||||
* 0x00: block number
|
||||
* 0x04: block offset
|
||||
* 0x08: always 0x03
|
||||
* 0x0c+(04*N): layer size per layer
|
||||
* 0xNN: layer data per layer */
|
||||
data->layer_max = read_32bit(offset+0x08, streamfile);
|
||||
|
||||
data->header_next_start = 0x18;
|
||||
data->header_sizes_start = 0x4c;
|
||||
data->header_data_start = 0x4c + data->layer_max*0x04;
|
||||
|
||||
data->block_next_start = 0;
|
||||
data->block_sizes_start = 0x0c;
|
||||
data->block_data_start = 0x0c + data->layer_max*0x04;
|
||||
break;
|
||||
|
||||
case 0x00040008: /* Assassin's Creed */
|
||||
case 0x000B0008: /* Open Season, Surf's Up, TMNT, Splinter Cell HD */
|
||||
case 0x000C0008: /* Splinter Cell: Double Agent */
|
||||
case 0x00100008: /* Rainbow Six 2 */
|
||||
/* - layer header
|
||||
* 0x04: config?
|
||||
* 0x08: layer count
|
||||
* 0x0c: blocks count
|
||||
* 0x10: block header size
|
||||
* 0x14: size of header sizes and headers/data
|
||||
* 0x18: next block size
|
||||
* 0x1c+(04*N): layer header size
|
||||
* 0xNN: header data per layer
|
||||
* - block header:
|
||||
* 0x00: always 0x03
|
||||
* 0x04: next block size
|
||||
* 0x08+(04*N): layer size per layer
|
||||
* 0xNN: layer data per layer */
|
||||
data->layer_max = read_32bit(offset+0x08, streamfile);
|
||||
|
||||
data->header_next_start = 0x18;
|
||||
data->header_sizes_start = 0x1c;
|
||||
data->header_data_start = 0x1c + data->layer_max*0x04;
|
||||
|
||||
data->block_next_start = 0x04;
|
||||
data->block_sizes_start = 0x08;
|
||||
data->block_data_start = 0x08 + data->layer_max*0x04;
|
||||
break;
|
||||
|
||||
case 0x00100009: /* Splinter Cell: Pandora Tomorrow HD, Prince of Persia 2008, Scott Pilgrim */
|
||||
/* - layer header
|
||||
* 0x04: config?
|
||||
* 0x08: layer count
|
||||
* 0x0c: blocks count
|
||||
* 0x10: block header size
|
||||
* 0x14: size of header sizes and headers/data
|
||||
* 0x18: next block size
|
||||
* 0x1c+(04*10): usable size per layer
|
||||
* 0x5c+(04*N): layer header size
|
||||
* 0xNN: header data per layer
|
||||
* - block header:
|
||||
* 0x00: always 0x03
|
||||
* 0x04: next block size
|
||||
* 0x08+(04*N): layer size per layer
|
||||
* 0xNN: layer data per layer */
|
||||
data->layer_max = read_32bit(offset+0x08, streamfile);
|
||||
|
||||
data->header_next_start = 0x18;
|
||||
data->header_sizes_start = 0x5c;
|
||||
data->header_data_start = 0x5c + data->layer_max*0x04;
|
||||
|
||||
data->block_next_start = 0x04;
|
||||
data->block_sizes_start = 0x08;
|
||||
data->block_data_start = 0x08 + data->layer_max*0x04;
|
||||
break;
|
||||
|
||||
default:
|
||||
VGM_LOG("UBI SB: unknown layer header %08x\n", version);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* get base size to simplify later parsing */
|
||||
data->header_size = data->header_data_start;
|
||||
if (data->header_sizes_start) {
|
||||
for (i = 0; i < data->layer_max; i++) {
|
||||
data->header_size += read_32bit(offset + data->header_sizes_start + i*0x04, streamfile);
|
||||
}
|
||||
}
|
||||
|
||||
/* force read header block */
|
||||
data->logical_offset = -1;
|
||||
|
||||
/* just in case some headers may use less layers that stream has */
|
||||
VGM_ASSERT(data->layer_count != data->layer_max, "UBI SB: non-matching layer counts\n");
|
||||
if (data->layer_count > data->layer_max) {
|
||||
VGM_LOG("UBI SB: layer count bigger than layer max\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Common layer quirks:
|
||||
* - layer format depends on its own version and not on platform or DARE engine version
|
||||
* - codec header may be in the layer header, or in the first block
|
||||
* - stream size doesn't include padding
|
||||
* - block number goes from 1 to block_count
|
||||
* - block offset is relative to layer start
|
||||
* - blocks data size varies between blocks and between layers in the same block
|
||||
* - "config?" is a small value that varies between streams of the same game
|
||||
* - next block size is 0 at last block
|
||||
* - both Ubi SB and Ubi BAO use same-version layers
|
||||
*/
|
||||
|
||||
return 1;
|
||||
fail:
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Handles deinterleaving of Ubisoft's headered+blocked 'multitrack' streams */
|
||||
static STREAMFILE* setup_ubi_sb_streamfile(STREAMFILE *streamFile, off_t stream_offset, size_t stream_size, int layer_number, int layer_count, int big_endian, int layer_hijack) {
|
||||
STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL;
|
||||
ubi_sb_io_data io_data = {0};
|
||||
size_t io_data_size = sizeof(ubi_sb_io_data);
|
||||
|
||||
io_data.stream_offset = stream_offset;
|
||||
io_data.stream_size = stream_size;
|
||||
io_data.layer_number = layer_number;
|
||||
io_data.layer_count = layer_count;
|
||||
io_data.big_endian = big_endian;
|
||||
io_data.layer_hijack = layer_hijack;
|
||||
|
||||
if (!ubi_sb_io_init(streamFile, &io_data))
|
||||
goto fail;
|
||||
|
||||
/* setup subfile */
|
||||
new_streamFile = open_wrap_streamfile(streamFile);
|
||||
if (!new_streamFile) goto fail;
|
||||
temp_streamFile = new_streamFile;
|
||||
|
||||
new_streamFile = open_io_streamfile(new_streamFile, &io_data,io_data_size, ubi_sb_io_read,ubi_sb_io_size);
|
||||
if (!new_streamFile) goto fail;
|
||||
temp_streamFile = new_streamFile;
|
||||
|
||||
new_streamFile = open_buffer_streamfile(new_streamFile,0);
|
||||
if (!new_streamFile) goto fail;
|
||||
temp_streamFile = new_streamFile;
|
||||
|
||||
return temp_streamFile;
|
||||
|
||||
fail:
|
||||
close_streamfile(temp_streamFile);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif /* _UBI_SB_STREAMFILE_H_ */
|
||||
#ifndef _UBI_SB_STREAMFILE_H_
|
||||
#define _UBI_SB_STREAMFILE_H_
|
||||
#include "../streamfile.h"
|
||||
#include "ubi_sb_garbage_streamfile.h"
|
||||
|
||||
|
||||
typedef struct {
|
||||
/* config */
|
||||
off_t stream_offset;
|
||||
off_t stream_size;
|
||||
int layer_number;
|
||||
int layer_count;
|
||||
int layer_max;
|
||||
int big_endian;
|
||||
int layer_hijack;
|
||||
|
||||
/* internal config */
|
||||
off_t header_next_start; /* offset to header field */
|
||||
off_t header_sizes_start; /* offset to header table */
|
||||
off_t header_data_start; /* offset to header data */
|
||||
off_t block_next_start; /* offset to block field */
|
||||
off_t block_sizes_start; /* offset to block table */
|
||||
off_t block_data_start; /* offset to block data */
|
||||
size_t header_size; /* derived */
|
||||
|
||||
/* state */
|
||||
off_t logical_offset; /* fake offset */
|
||||
off_t physical_offset; /* actual offset */
|
||||
size_t block_size; /* current size */
|
||||
size_t next_block_size; /* next size */
|
||||
size_t skip_size; /* size from block start to reach data */
|
||||
size_t data_size; /* usable size in a block */
|
||||
|
||||
size_t logical_size;
|
||||
} ubi_sb_io_data;
|
||||
|
||||
|
||||
static size_t ubi_sb_io_read(STREAMFILE* sf, uint8_t* dest, off_t offset, size_t length, ubi_sb_io_data* data) {
|
||||
uint32_t(*read_u32)(off_t, STREAMFILE*) = data->big_endian ? read_u32be : read_u32le;
|
||||
size_t total_read = 0;
|
||||
int i;
|
||||
|
||||
|
||||
/* re-start when previous offset (can't map logical<>physical offsets) */
|
||||
if (data->logical_offset < 0 || offset < data->logical_offset) {
|
||||
data->physical_offset = data->stream_offset;
|
||||
data->logical_offset = 0x00;
|
||||
data->data_size = 0;
|
||||
|
||||
/* process header block (slightly different and data size may be 0) */
|
||||
{
|
||||
data->block_size = data->header_size;
|
||||
data->next_block_size = read_u32(data->physical_offset + data->header_next_start, sf);
|
||||
|
||||
if (data->header_sizes_start) {
|
||||
data->skip_size = data->header_data_start;
|
||||
for (i = 0; i < data->layer_number; i++) {
|
||||
data->skip_size += read_u32(data->physical_offset + data->header_sizes_start + i*0x04, sf);
|
||||
}
|
||||
data->data_size = read_u32(data->physical_offset + data->header_sizes_start + data->layer_number*0x04, sf);
|
||||
}
|
||||
|
||||
if (data->data_size == 0) {
|
||||
data->physical_offset += data->block_size;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* read blocks */
|
||||
while (length > 0) {
|
||||
|
||||
/* ignore EOF */
|
||||
if (offset < 0 || data->physical_offset >= data->stream_offset + data->stream_size) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* process new block */
|
||||
if (data->data_size == 0) {
|
||||
data->block_size = data->next_block_size;
|
||||
if (data->block_next_start) /* not set when fixed block size */
|
||||
data->next_block_size = read_u32(data->physical_offset + data->block_next_start, sf);
|
||||
|
||||
data->skip_size = data->block_data_start;
|
||||
for (i = 0; i < data->layer_number; i++) {
|
||||
data->skip_size += read_u32(data->physical_offset + data->block_sizes_start + i*0x04, sf);
|
||||
}
|
||||
data->data_size = read_u32(data->physical_offset + data->block_sizes_start + data->layer_number*0x04, sf);
|
||||
}
|
||||
|
||||
/* move to next block */
|
||||
if (offset >= data->logical_offset + data->data_size) {
|
||||
if (data->block_size == 0 || data->block_size == 0xFFFFFFFF)
|
||||
break;
|
||||
data->physical_offset += data->block_size;
|
||||
data->logical_offset += data->data_size;
|
||||
data->data_size = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* read data */
|
||||
{
|
||||
size_t bytes_consumed, bytes_done, to_read;
|
||||
|
||||
bytes_consumed = offset - data->logical_offset;
|
||||
to_read = data->data_size - bytes_consumed;
|
||||
if (to_read > length)
|
||||
to_read = length;
|
||||
bytes_done = read_streamfile(dest, data->physical_offset + data->skip_size + bytes_consumed, to_read, sf);
|
||||
|
||||
total_read += bytes_done;
|
||||
dest += bytes_done;
|
||||
offset += bytes_done;
|
||||
length -= bytes_done;
|
||||
|
||||
if (bytes_done != to_read || bytes_done == 0) {
|
||||
break; /* error/EOF */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return total_read;
|
||||
}
|
||||
|
||||
static size_t ubi_sb_io_size(STREAMFILE* sf, ubi_sb_io_data* data) {
|
||||
uint8_t buf[1];
|
||||
|
||||
if (data->logical_size)
|
||||
return data->logical_size;
|
||||
|
||||
/* force a fake read at max offset, to get max logical_offset (will be reset next read) */
|
||||
ubi_sb_io_read(sf, buf, 0x7FFFFFFF, 1, data);
|
||||
data->logical_size = data->logical_offset;
|
||||
|
||||
return data->logical_size;
|
||||
}
|
||||
|
||||
|
||||
static int ubi_sb_io_init(STREAMFILE* sf, ubi_sb_io_data* data) {
|
||||
uint32_t(*read_u32)(off_t, STREAMFILE*) = data->big_endian ? read_u32be : read_u32le;
|
||||
off_t offset = data->stream_offset;
|
||||
uint32_t version;
|
||||
int i;
|
||||
|
||||
if (data->stream_offset + data->stream_size > get_streamfile_size(sf)) {
|
||||
VGM_LOG("UBI SB: bad size\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Layers have a main header, then headered blocks with data.
|
||||
* We configure stuff to unify parsing of all variations. */
|
||||
version = read_u32(offset+0x00, sf);
|
||||
|
||||
/* it was bound to happen... orz */
|
||||
if (data->layer_hijack == 1 && version == 0x000B0008)
|
||||
version = 0xFFFF0007;
|
||||
|
||||
switch(version) {
|
||||
case 0x00000002: /* Splinter Cell */
|
||||
/* - layer header
|
||||
* 0x04: layer count
|
||||
* 0x08: stream size
|
||||
* 0x0c: block header size
|
||||
* 0x10: block size (fixed)
|
||||
* 0x14: min layer size?
|
||||
* - block header
|
||||
* 0x00: block number
|
||||
* 0x04: block offset
|
||||
* 0x08+(04*N): layer size per layer
|
||||
* 0xNN: layer data per layer */
|
||||
data->layer_max = read_u32(offset+0x04, sf);
|
||||
|
||||
data->header_next_start = 0x10;
|
||||
data->header_sizes_start = 0;
|
||||
data->header_data_start = 0x18;
|
||||
|
||||
data->block_next_start = 0;
|
||||
data->block_sizes_start = 0x08;
|
||||
data->block_data_start = 0x08 + data->layer_max*0x04;
|
||||
break;
|
||||
|
||||
case 0x00000003: /* Rainbow Six 3 */
|
||||
/* - layer header
|
||||
* 0x04: layer count
|
||||
* 0x08: stream size
|
||||
* 0x0c: block header size
|
||||
* 0x10: block size (fixed)
|
||||
* 0x14: min layer data?
|
||||
* 0x18: size of header sizes and headers
|
||||
* 0x1c+(04*N): header size per layer
|
||||
* - block header
|
||||
* 0x00: block number
|
||||
* 0x04: block offset
|
||||
* 0x08+(04*N): layer size per layer
|
||||
* 0xNN: layer data per layer */
|
||||
data->layer_max = read_u32(offset+0x04, sf);
|
||||
|
||||
data->header_next_start = 0x10;
|
||||
data->header_sizes_start = 0x1c;
|
||||
data->header_data_start = 0x1c + data->layer_max*0x04;
|
||||
|
||||
data->block_next_start = 0;
|
||||
data->block_sizes_start = 0x08;
|
||||
data->block_data_start = 0x08 + data->layer_max*0x04;
|
||||
break;
|
||||
|
||||
case 0x00000004: /* Prince of Persia: Sands of Time, Batman: Rise of Sin Tzu */
|
||||
/* - layer header
|
||||
* 0x04: layer count
|
||||
* 0x08: stream size
|
||||
* 0x0c: block count
|
||||
* 0x10: block header size
|
||||
* 0x14: block size (fixed)
|
||||
* 0x18: min layer data?
|
||||
* 0x1c: size of header sizes and headers
|
||||
* 0x20+(04*N): header size per layer
|
||||
* - block header
|
||||
* 0x00: block number
|
||||
* 0x04: block offset
|
||||
* 0x08: always 0x03
|
||||
* 0x0c+(04*N): layer size per layer
|
||||
* 0xNN: layer data per layer */
|
||||
data->layer_max = read_u32(offset+0x04, sf);
|
||||
|
||||
data->header_next_start = 0x14;
|
||||
data->header_sizes_start = 0x20;
|
||||
data->header_data_start = 0x20 + data->layer_max*0x04;
|
||||
|
||||
data->block_next_start = 0;
|
||||
data->block_sizes_start = 0x0c;
|
||||
data->block_data_start = 0x0c + data->layer_max*0x04;
|
||||
break;
|
||||
|
||||
case 0x00000007: /* Splinter Cell: Essentials, Splinter Cell 3D */
|
||||
/* - layer header
|
||||
* 0x04: config?
|
||||
* 0x08: layer count
|
||||
* 0x0c: stream size
|
||||
* 0x10: block count
|
||||
* 0x14: block header size
|
||||
* 0x18: block size (fixed)
|
||||
* 0x1c+(04*8): min layer data? for 8 layers (-1 after layer count)
|
||||
* 0x3c: size of header sizes and headers
|
||||
* 0x40+(04*N): header size per layer
|
||||
* 0xNN: header data per layer
|
||||
* - block header
|
||||
* 0x00: block number
|
||||
* 0x04: block offset
|
||||
* 0x08: always 0x03
|
||||
* 0x0c+(04*N): layer size per layer
|
||||
* 0xNN: layer data per layer */
|
||||
data->layer_max = read_u32(offset+0x08, sf);
|
||||
|
||||
data->header_next_start = 0x18;
|
||||
data->header_sizes_start = 0x40;
|
||||
data->header_data_start = 0x40 + data->layer_max*0x04;
|
||||
|
||||
data->block_next_start = 0;
|
||||
data->block_sizes_start = 0x0c;
|
||||
data->block_data_start = 0x0c + data->layer_max*0x04;
|
||||
break;
|
||||
|
||||
case 0xFFFF0007: /* Ghost Recon Advanced Warfighter (X360) */
|
||||
/* - layer header
|
||||
* 0x04: config?
|
||||
* 0x08: layer count
|
||||
* 0x0c: stream size
|
||||
* 0x10: block count
|
||||
* 0x14: block header size
|
||||
* 0x18: block size (fixed)
|
||||
* 0x1c+(04*11): min layer data? for 11 layers (-1 after layer count)
|
||||
* 0x48: size of header sizes and headers
|
||||
* 0x4c+(04*N): header size per layer
|
||||
* 0xNN: header data per layer
|
||||
* - block header
|
||||
* 0x00: block number
|
||||
* 0x04: block offset
|
||||
* 0x08: always 0x03
|
||||
* 0x0c+(04*N): layer size per layer
|
||||
* 0xNN: layer data per layer */
|
||||
data->layer_max = read_u32(offset+0x08, sf);
|
||||
|
||||
data->header_next_start = 0x18;
|
||||
data->header_sizes_start = 0x4c;
|
||||
data->header_data_start = 0x4c + data->layer_max*0x04;
|
||||
|
||||
data->block_next_start = 0;
|
||||
data->block_sizes_start = 0x0c;
|
||||
data->block_data_start = 0x0c + data->layer_max*0x04;
|
||||
break;
|
||||
|
||||
case 0x00040008: /* Assassin's Creed */
|
||||
case 0x000B0008: /* Open Season, Surf's Up, TMNT, Splinter Cell HD */
|
||||
case 0x000C0008: /* Splinter Cell: Double Agent */
|
||||
case 0x00100008: /* Rainbow Six 2 */
|
||||
/* - layer header
|
||||
* 0x04: config?
|
||||
* 0x08: layer count
|
||||
* 0x0c: blocks count
|
||||
* 0x10: block header size
|
||||
* 0x14: size of header sizes and headers/data
|
||||
* 0x18: next block size
|
||||
* 0x1c+(04*N): layer header size
|
||||
* 0xNN: header data per layer
|
||||
* - block header:
|
||||
* 0x00: always 0x03
|
||||
* 0x04: next block size
|
||||
* 0x08+(04*N): layer size per layer
|
||||
* 0xNN: layer data per layer */
|
||||
data->layer_max = read_u32(offset+0x08, sf);
|
||||
|
||||
data->header_next_start = 0x18;
|
||||
data->header_sizes_start = 0x1c;
|
||||
data->header_data_start = 0x1c + data->layer_max*0x04;
|
||||
|
||||
data->block_next_start = 0x04;
|
||||
data->block_sizes_start = 0x08;
|
||||
data->block_data_start = 0x08 + data->layer_max*0x04;
|
||||
break;
|
||||
|
||||
case 0x00100009: /* Splinter Cell: Pandora Tomorrow HD, Prince of Persia 2008, Scott Pilgrim */
|
||||
/* - layer header
|
||||
* 0x04: config?
|
||||
* 0x08: layer count
|
||||
* 0x0c: blocks count
|
||||
* 0x10: block header size
|
||||
* 0x14: size of header sizes and headers/data
|
||||
* 0x18: next block size
|
||||
* 0x1c+(04*10): usable size per layer
|
||||
* 0x5c+(04*N): layer header size
|
||||
* 0xNN: header data per layer
|
||||
* - block header:
|
||||
* 0x00: always 0x03
|
||||
* 0x04: next block size
|
||||
* 0x08+(04*N): layer size per layer
|
||||
* 0xNN: layer data per layer */
|
||||
data->layer_max = read_u32(offset+0x08, sf);
|
||||
|
||||
data->header_next_start = 0x18;
|
||||
data->header_sizes_start = 0x5c;
|
||||
data->header_data_start = 0x5c + data->layer_max*0x04;
|
||||
|
||||
data->block_next_start = 0x04;
|
||||
data->block_sizes_start = 0x08;
|
||||
data->block_data_start = 0x08 + data->layer_max*0x04;
|
||||
break;
|
||||
|
||||
default:
|
||||
VGM_LOG("UBI SB: unknown layer header %08x\n", version);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* get base size to simplify later parsing */
|
||||
data->header_size = data->header_data_start;
|
||||
if (data->header_sizes_start) {
|
||||
for (i = 0; i < data->layer_max; i++) {
|
||||
data->header_size += read_u32(offset + data->header_sizes_start + i*0x04, sf);
|
||||
}
|
||||
}
|
||||
|
||||
/* force read header block */
|
||||
data->logical_offset = -1;
|
||||
|
||||
/* just in case some headers may use less layers that stream has */
|
||||
VGM_ASSERT(data->layer_count != data->layer_max, "UBI SB: non-matching layer counts\n");
|
||||
if (data->layer_count > data->layer_max) {
|
||||
VGM_LOG("UBI SB: layer count bigger than layer max\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Common layer quirks:
|
||||
* - layer format depends on its own version and not on platform or DARE engine version
|
||||
* - codec header may be in the layer header, or in the first block
|
||||
* - stream size doesn't include padding
|
||||
* - block number goes from 1 to block_count
|
||||
* - block offset is relative to layer start
|
||||
* - blocks data size varies between blocks and between layers in the same block
|
||||
* - "config?" is a small value that varies between streams of the same game
|
||||
* - next block size is 0 at last block
|
||||
* - both Ubi SB and Ubi BAO use same-version layers
|
||||
*/
|
||||
|
||||
return 1;
|
||||
fail:
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Handles deinterleaving of Ubisoft's headered+blocked 'multitrack' streams */
|
||||
static STREAMFILE* setup_ubi_sb_streamfile(STREAMFILE* sf, off_t stream_offset, size_t stream_size, int layer_number, int layer_count, int big_endian, int layer_hijack) {
|
||||
STREAMFILE* new_sf = NULL;
|
||||
ubi_sb_io_data io_data = {0};
|
||||
|
||||
io_data.stream_offset = stream_offset;
|
||||
io_data.stream_size = stream_size;
|
||||
io_data.layer_number = layer_number;
|
||||
io_data.layer_count = layer_count;
|
||||
io_data.big_endian = big_endian;
|
||||
io_data.layer_hijack = layer_hijack;
|
||||
|
||||
if (!ubi_sb_io_init(sf, &io_data))
|
||||
goto fail;
|
||||
|
||||
/* setup subfile */
|
||||
new_sf = open_wrap_streamfile(sf);
|
||||
if (layer_hijack == 2 && is_garbage_stream(sf)) {
|
||||
new_sf = setup_ubi_sb_garbage_streamfile_f(new_sf);
|
||||
//io_data.stream_size = get_garbage_stream_size(stream_offset, stream_size); //todo must do relative calcs, doesn't seem needed
|
||||
}
|
||||
new_sf = open_io_streamfile_f(new_sf, &io_data, sizeof(ubi_sb_io_data), ubi_sb_io_read,ubi_sb_io_size);
|
||||
new_sf = open_buffer_streamfile_f(new_sf,0);
|
||||
return new_sf;
|
||||
fail:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif /* _UBI_SB_STREAMFILE_H_ */
|
||||
|
Loading…
x
Reference in New Issue
Block a user