2008-12-18 07:51:26 +01:00
# include "meta.h"
2010-09-15 21:37:54 +02:00
# include "../coding/coding.h"
2016-12-29 14:06:57 +01:00
/* ************************************************************************************************************
* FSB defines , copied from the public spec ( https : //www.fmod.org/questions/question/forum-4928/)
* The format is mostly compatible for FSB1 / 2 / 3 / 4 , but not FSB5 . Headers always use LE . A FSB contains
* main header + sample header ( s ) + raw data . In multistreams N sample headers are stored ( and
* if the BASICHEADERS flag is set , all headers but the first use HEADER_BASIC = numsamples + datasize )
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* These flags are used for FMOD_FSB_HEADER::mode */
# define FMOD_FSB_SOURCE_FORMAT 0x00000001 /* all samples stored in their original compressed format */
# define FMOD_FSB_SOURCE_BASICHEADERS 0x00000002 /* samples should use the basic header structure */
# define FMOD_FSB_SOURCE_ENCRYPTED 0x00000004 /* all sample data is encrypted */
# define FMOD_FSB_SOURCE_BIGENDIANPCM 0x00000008 /* pcm samples have been written out in big-endian format */
# define FMOD_FSB_SOURCE_NOTINTERLEAVED 0x00000010 /* Sample data is not interleaved. */
# define FMOD_FSB_SOURCE_MPEG_PADDED 0x00000020 /* Mpeg frames are now rounded up to the nearest 2 bytes for normal sounds, or 16 bytes for multichannel. */
# define FMOD_FSB_SOURCE_MPEG_PADDED4 0x00000040 /* Mpeg frames are now rounded up to the nearest 4 bytes for normal sounds, or 16 bytes for multichannel. */
/* These flags are used for FMOD_FSB_HEADER::version */
# define FMOD_FSB_VERSION_3_0 0x00030000 /* FSB version 3.0 */
# define FMOD_FSB_VERSION_3_1 0x00030001 /* FSB version 3.1 */
# define FMOD_FSB_VERSION_4_0 0x00040000 /* FSB version 4.0 */
/* FMOD 3 defines. These flags are used for FMOD_FSB_SAMPLE_HEADER::mode */
# define FSOUND_LOOP_OFF 0x00000001 /* For non looping samples. */
# define FSOUND_LOOP_NORMAL 0x00000002 /* For forward looping samples. */
# define FSOUND_LOOP_BIDI 0x00000004 /* For bidirectional looping samples. (no effect if in hardware). */
# define FSOUND_8BITS 0x00000008 /* For 8 bit samples. */
# define FSOUND_16BITS 0x00000010 /* For 16 bit samples. */
# define FSOUND_MONO 0x00000020 /* For mono samples. */
# define FSOUND_STEREO 0x00000040 /* For stereo samples. */
# define FSOUND_UNSIGNED 0x00000080 /* For user created source data containing unsigned samples. */
# define FSOUND_SIGNED 0x00000100 /* For user created source data containing signed data. */
# define FSOUND_MPEG 0x00000200 /* For MPEG layer 2/3 data. */
# define FSOUND_CHANNELMODE_ALLMONO 0x00000400 /* Sample is a collection of mono channels. */
# define FSOUND_CHANNELMODE_ALLSTEREO 0x00000800 /* Sample is a collection of stereo channel pairs */
# define FSOUND_HW3D 0x00001000 /* Attempts to make samples use 3d hardware acceleration. (if the card supports it) */
# define FSOUND_2D 0x00002000 /* Tells software (not hardware) based sample not to be included in 3d processing. */
# define FSOUND_SYNCPOINTS_NONAMES 0x00004000 /* Specifies that syncpoints are present with no names */
# define FSOUND_DUPLICATE 0x00008000 /* This subsound is a duplicate of the previous one i.e. it uses the same sample data but w/different mode bits */
# define FSOUND_CHANNELMODE_PROTOOLS 0x00010000 /* Sample is 6ch and uses L C R LS RS LFE standard. */
# define FSOUND_MPEGACCURATE 0x00020000 /* For FSOUND_Stream_Open - for accurate FSOUND_Stream_GetLengthMs/FSOUND_Stream_SetTime. WARNING, see FSOUND_Stream_Open for inital opening time performance issues. */
# define FSOUND_HW2D 0x00080000 /* 2D hardware sounds. allows hardware specific effects */
# define FSOUND_3D 0x00100000 /* 3D software sounds */
# define FSOUND_32BITS 0x00200000 /* For 32 bit (float) samples. */
# define FSOUND_IMAADPCM 0x00400000 /* Contents are stored compressed as IMA ADPCM */
# define FSOUND_VAG 0x00800000 /* For PS2 only - Contents are compressed as Sony VAG format */
# define FSOUND_XMA 0x01000000 /* For Xbox360 only - Contents are compressed as XMA format */
# define FSOUND_GCADPCM 0x02000000 /* For Gamecube only - Contents are compressed as Gamecube DSP-ADPCM format */
# define FSOUND_MULTICHANNEL 0x04000000 /* For PS2 and Gamecube only - Contents are interleaved into a multi-channel (more than stereo) format */
# define FSOUND_OGG 0x08000000 /* For vorbis encoded ogg data */
# define FSOUND_CELT 0x08000000 /* For vorbis encoded ogg data */
# define FSOUND_MPEG_LAYER3 0x10000000 /* Data is in MP3 format. */
# define FSOUND_MPEG_LAYER2 0x00040000 /* Data is in MP2 format. */
# define FSOUND_LOADMEMORYIOP 0x20000000 /* For PS2 only - "name" will be interpreted as a pointer to data for streaming and samples. The address provided will be an IOP address */
# define FSOUND_IMAADPCMSTEREO 0x20000000 /* Signify IMA ADPCM is actually stereo not two interleaved mono */
# define FSOUND_IGNORETAGS 0x40000000 /* Skips id3v2 etc tag checks when opening a stream, to reduce seek/read overhead when opening files (helps with CD performance) */
# define FSOUND_SYNCPOINTS 0x80000000 /* Specifies that syncpoints are present */
/* These flags are used for FMOD_FSB_SAMPLE_HEADER::mode */
# define FSOUND_CHANNELMODE_MASK (FSOUND_CHANNELMODE_ALLMONO | FSOUND_CHANNELMODE_ALLSTEREO | FSOUND_CHANNELMODE_PROTOOLS)
# define FSOUND_CHANNELMODE_DEFAULT 0x00000000 /* Determine channel assignment automatically from channel count. */
# define FSOUND_CHANNELMODE_RESERVED 0x00000C00
# define FSOUND_NORMAL (FSOUND_16BITS | FSOUND_SIGNED | FSOUND_MONO)
# define FSB_SAMPLE_DATA_ALIGN 32
/* simplified struct based on the original definitions */
typedef struct {
/* main header */
uint32_t id ;
2018-01-20 01:40:42 +01:00
int32_t total_subsongs ;
uint32_t sample_header_size ; /* all of the sample headers including extended information */
uint32_t data_size ;
uint32_t version ; /* extended fsb version (in FSB 3/3.1/4) */
uint32_t flags ; /* flags common to all streams (in FSB 3/3.1/4)*/
2016-12-29 14:06:57 +01:00
/* sample header */
2018-01-20 01:40:42 +01:00
uint32_t num_samples ;
uint32_t stream_size ;
uint32_t loop_start ;
uint32_t loop_end ;
2016-12-29 14:06:57 +01:00
uint32_t mode ;
2018-01-20 01:40:42 +01:00
int32_t sample_rate ;
uint16_t channels ;
2016-12-29 14:06:57 +01:00
/* extra */
2018-01-20 01:40:42 +01:00
uint32_t base_header_size ;
uint32_t sample_header_min ;
2017-08-12 11:46:28 +02:00
2016-12-29 14:06:57 +01:00
meta_t meta_type ;
2017-08-12 11:46:28 +02:00
off_t name_offset ;
size_t name_size ;
2017-08-26 03:23:39 +02:00
} fsb_header ;
2016-12-29 14:06:57 +01:00
/* ********************************************************************************** */
2008-12-18 07:51:26 +01:00
2016-12-29 14:06:57 +01:00
/* FSB4 */
VGMSTREAM * init_vgmstream_fsb ( STREAMFILE * streamFile ) {
2011-02-08 13:56:16 +01:00
VGMSTREAM * vgmstream = NULL ;
2017-01-27 18:01:40 +01:00
off_t start_offset ;
2016-12-29 14:06:57 +01:00
size_t custom_data_offset ;
2011-02-08 13:56:16 +01:00
int loop_flag = 0 ;
2017-08-12 11:46:28 +02:00
int target_stream = streamFile - > stream_index ;
2018-01-20 01:40:42 +01:00
fsb_header fsb = { 0 } ;
2011-02-08 13:56:16 +01:00
2018-01-20 00:55:37 +01:00
/* check extensions (.wii: fsb4_wav? .bnk = Hard Corps Uprising PS3) */
2017-08-12 11:46:28 +02:00
if ( ! check_extensions ( streamFile , " fsb,wii,bnk " ) )
2016-12-29 14:06:57 +01:00
goto fail ;
2011-02-08 13:56:16 +01:00
2016-12-29 14:06:57 +01:00
/* check header */
2018-01-20 01:40:42 +01:00
fsb . id = read_32bitBE ( 0x00 , streamFile ) ;
if ( fsb . id = = 0x46534231 ) { /* "FSB1" (somewhat different from other fsbs) */
fsb . meta_type = meta_FSB1 ;
fsb . base_header_size = 0x10 ;
fsb . sample_header_min = 0x40 ;
2016-12-29 14:06:57 +01:00
/* main header */
2018-01-20 01:40:42 +01:00
fsb . total_subsongs = read_32bitLE ( 0x04 , streamFile ) ;
fsb . data_size = read_32bitLE ( 0x08 , streamFile ) ;
fsb . sample_header_size = 0x40 ;
fsb . version = 0 ;
fsb . flags = 0 ;
2016-12-29 14:06:57 +01:00
2018-01-20 01:40:42 +01:00
if ( fsb . total_subsongs > 1 ) goto fail ;
2017-01-27 18:01:40 +01:00
/* sample header (first stream only, not sure if there are multi-FSB1) */
{
2018-01-20 01:40:42 +01:00
off_t s_off = fsb . base_header_size ;
2017-01-27 18:01:40 +01:00
2018-01-20 01:40:42 +01:00
fsb . name_offset = s_off ;
fsb . name_size = 0x20 ;
fsb . num_samples = read_32bitLE ( s_off + 0x20 , streamFile ) ;
fsb . stream_size = read_32bitLE ( s_off + 0x24 , streamFile ) ;
fsb . sample_rate = read_32bitLE ( s_off + 0x28 , streamFile ) ;
2017-01-27 18:01:40 +01:00
/* 0x2c:? 0x2e:? 0x30:? 0x32:? */
2018-01-20 01:40:42 +01:00
fsb . mode = read_32bitLE ( s_off + 0x34 , streamFile ) ;
fsb . loop_start = read_32bitLE ( s_off + 0x38 , streamFile ) ;
fsb . loop_end = read_32bitLE ( s_off + 0x3c , streamFile ) ;
2017-01-27 18:01:40 +01:00
2018-01-20 01:40:42 +01:00
fsb . channels = ( fsb . mode & FSOUND_STEREO ) ? 2 : 1 ;
if ( fsb . loop_end > fsb . num_samples ) /* this seems common... */
fsb . num_samples = fsb . loop_end ;
2017-01-27 18:01:40 +01:00
2018-01-20 01:40:42 +01:00
start_offset = fsb . base_header_size + fsb . sample_header_size ;
custom_data_offset = fsb . base_header_size + fsb . sample_header_min ; /* DSP coefs, seek tables, etc */
2017-01-27 18:01:40 +01:00
}
2011-02-08 13:56:16 +01:00
}
2016-12-29 14:06:57 +01:00
else { /* other FSBs (common/extended format) */
2018-01-20 01:40:42 +01:00
if ( fsb . id = = 0x46534232 ) { /* "FSB2" */
fsb . meta_type = meta_FSB2 ;
fsb . base_header_size = 0x10 ;
fsb . sample_header_min = 0x40 ; /* guessed */
} else if ( fsb . id = = 0x46534233 ) { /* "FSB3" */
fsb . meta_type = meta_FSB3 ;
fsb . base_header_size = 0x18 ;
fsb . sample_header_min = 0x40 ;
} else if ( fsb . id = = 0x46534234 ) { /* "FSB4" */
fsb . meta_type = meta_FSB4 ;
fsb . base_header_size = 0x30 ;
fsb . sample_header_min = 0x50 ;
2016-12-29 14:06:57 +01:00
} else {
goto fail ;
2011-02-08 13:56:16 +01:00
}
2016-12-29 14:06:57 +01:00
/* main header */
2018-01-20 01:40:42 +01:00
fsb . total_subsongs = read_32bitLE ( 0x04 , streamFile ) ;
fsb . sample_header_size = read_32bitLE ( 0x08 , streamFile ) ;
fsb . data_size = read_32bitLE ( 0x0c , streamFile ) ;
if ( fsb . base_header_size > 0x10 ) {
fsb . version = read_32bitLE ( 0x10 , streamFile ) ;
fsb . flags = read_32bitLE ( 0x14 , streamFile ) ;
2016-12-29 14:06:57 +01:00
/* FSB4: 0x18:hash 0x20:guid */
} else {
2018-01-20 01:40:42 +01:00
fsb . version = 0 ;
fsb . flags = 0 ;
2011-02-08 13:56:16 +01:00
}
2018-01-20 01:40:42 +01:00
if ( fsb . version = = FMOD_FSB_VERSION_3_1 ) {
fsb . sample_header_min = 0x50 ;
} else if ( fsb . version ! = 0 /* FSB2 */
& & fsb . version ! = FMOD_FSB_VERSION_3_0
& & fsb . version ! = FMOD_FSB_VERSION_4_0 ) {
2016-12-29 14:06:57 +01:00
goto fail ;
}
2008-12-18 07:51:26 +01:00
2018-01-20 01:40:42 +01:00
if ( fsb . sample_header_size < fsb . sample_header_min ) goto fail ;
2017-01-27 18:01:40 +01:00
if ( target_stream = = 0 ) target_stream = 1 ;
2018-01-20 01:40:42 +01:00
if ( target_stream < 0 | | target_stream > fsb . total_subsongs | | fsb . total_subsongs < 1 ) goto fail ;
2017-01-27 18:01:40 +01:00
/* sample header (N-stream) */
{
int i ;
2018-01-20 01:40:42 +01:00
off_t s_off = fsb . base_header_size ;
off_t d_off = fsb . base_header_size + fsb . sample_header_size ;
2017-01-27 18:01:40 +01:00
2017-08-12 11:46:28 +02:00
/* find target_stream header (variable sized) */
2018-01-20 01:40:42 +01:00
for ( i = 0 ; i < fsb . total_subsongs ; i + + ) {
2017-08-12 11:46:28 +02:00
size_t stream_header_size = ( uint16_t ) read_16bitLE ( s_off + 0x00 , streamFile ) ;
2018-01-20 01:40:42 +01:00
fsb . name_offset = s_off + 0x02 ;
fsb . name_size = 0x20 - 0x02 ;
fsb . num_samples = read_32bitLE ( s_off + 0x20 , streamFile ) ;
fsb . stream_size = read_32bitLE ( s_off + 0x24 , streamFile ) ;
fsb . loop_start = read_32bitLE ( s_off + 0x28 , streamFile ) ;
fsb . loop_end = read_32bitLE ( s_off + 0x2c , streamFile ) ;
fsb . mode = read_32bitLE ( s_off + 0x30 , streamFile ) ;
fsb . sample_rate = read_32bitLE ( s_off + 0x34 , streamFile ) ;
2017-01-27 18:01:40 +01:00
/* 0x38:defvol 0x3a:defpan 0x3c:defpri */
2018-01-20 01:40:42 +01:00
fsb . channels = read_16bitLE ( s_off + 0x3e , streamFile ) ;
/* FSB3.1/4: 0x40:mindistance 0x44:maxdistance 0x48:varfreq/size_32bits 0x4c:varvol 0x4e:fsb.varpan */
2017-08-12 11:46:28 +02:00
/* FSB3/4: 0x50:extended_data size_32bits (not always given) */
2017-01-27 18:01:40 +01:00
2017-08-12 11:46:28 +02:00
if ( i + 1 = = target_stream ) /* d_off found */
2017-01-27 18:01:40 +01:00
break ;
2017-08-12 11:46:28 +02:00
s_off + = stream_header_size ;
2018-01-20 01:40:42 +01:00
d_off + = fsb . stream_size ; /* there is no offset so manually count */
2017-08-12 11:46:28 +02:00
/* IMAs streams have weird end padding (maybe: FSB3=no padding, FSB4=always padding) */
2018-01-20 01:40:42 +01:00
if ( ( fsb . mode & FSOUND_IMAADPCM ) & & ( fsb . flags & FMOD_FSB_SOURCE_MPEG_PADDED4 ) ) {
2017-08-12 11:46:28 +02:00
if ( d_off % 0x20 )
d_off + = 0x20 - ( d_off % 0x20 ) ;
}
2017-01-27 18:01:40 +01:00
}
2018-01-20 01:40:42 +01:00
if ( i > fsb . total_subsongs ) goto fail ; /* not found */
2017-01-27 18:01:40 +01:00
start_offset = d_off ;
2018-01-20 01:40:42 +01:00
custom_data_offset = s_off + fsb . sample_header_min ; /* DSP coefs, seek tables, etc */
2017-01-27 18:01:40 +01:00
}
2016-12-29 14:06:57 +01:00
}
2008-12-18 07:51:26 +01:00
2017-08-12 11:46:28 +02:00
2017-03-24 16:37:56 +01:00
/* XOR encryption for some FSB4, though the flag is only seen after decrypting */
2018-01-20 01:40:42 +01:00
//VGM_ASSERT(fsb.flags & FMOD_FSB_SOURCE_ENCRYPTED, "FSB ENCRYPTED found\n");
2017-03-24 16:37:56 +01:00
2017-01-27 18:01:40 +01:00
/* sometimes there is garbage at the end or missing bytes due to improper demuxing */
2018-01-20 01:40:42 +01:00
VGM_ASSERT ( fsb . base_header_size + fsb . sample_header_size + fsb . data_size ! = streamFile - > get_size ( streamFile ) ,
" FSB wrong head/data_size found (expected 0x%x vs 0x%x) \n " ,
fsb . base_header_size + fsb . sample_header_size + fsb . data_size , streamFile - > get_size ( streamFile ) ) ;
2010-02-27 23:10:52 +01:00
2017-08-26 03:23:39 +02:00
/* Loops unless disabled. FMOD default seems full loops (0/num_samples-1) without flags, for repeating tracks
2018-01-20 01:40:42 +01:00
* that should loop and jingles / sfx that shouldn ' t . We ' ll try to disable looping if it looks jingly enough . */
loop_flag = ! ( fsb . mode & FSOUND_LOOP_OFF ) ;
if ( ! ( fsb . mode & FSOUND_LOOP_NORMAL ) /* rarely set */
& & fsb . loop_start + fsb . loop_end + 1 = = fsb . num_samples /* full loop */
& & fsb . num_samples < 20 * fsb . sample_rate ) /* seconds, lame but no other way to know */
2017-08-26 03:23:39 +02:00
loop_flag = 0 ;
2016-12-29 14:06:57 +01:00
/* ping-pong looping = no looping? (forward > reverse > forward) */
2018-01-20 01:40:42 +01:00
VGM_ASSERT ( fsb . mode & FSOUND_LOOP_BIDI , " FSB BIDI looping found \n " ) ;
2009-03-05 22:27:50 +01:00
2017-08-26 03:23:39 +02:00
2009-12-19 23:30:14 +01:00
/* build the VGMSTREAM */
2018-01-20 01:40:42 +01:00
vgmstream = allocate_vgmstream ( fsb . channels , loop_flag ) ;
2009-03-05 22:27:50 +01:00
if ( ! vgmstream ) goto fail ;
2018-01-20 01:40:42 +01:00
vgmstream - > sample_rate = fsb . sample_rate ;
vgmstream - > num_samples = fsb . num_samples ;
vgmstream - > loop_start_sample = fsb . loop_start ;
vgmstream - > loop_end_sample = fsb . loop_end ;
vgmstream - > num_streams = fsb . total_subsongs ;
vgmstream - > meta_type = fsb . meta_type ;
if ( fsb . name_offset )
read_string ( vgmstream - > stream_name , fsb . name_size + 1 , fsb . name_offset , streamFile ) ;
2017-08-12 11:46:28 +02:00
2009-03-05 22:27:50 +01:00
2018-01-20 01:40:42 +01:00
/* parse codec */
if ( fsb . mode & FSOUND_MPEG ) { /* FSB4: Shatter, Way of the Samurai 3/4 (PS3) */
2016-12-29 14:06:57 +01:00
# if defined(VGM_USE_MPEG)
2017-12-17 19:25:10 +01:00
mpeg_custom_config cfg = { 0 } ;
2017-07-29 23:14:04 +02:00
cfg . fsb_padding = ( vgmstream - > channels > 2 ? 16 :
2018-01-20 01:40:42 +01:00
( fsb . flags & FMOD_FSB_SOURCE_MPEG_PADDED4 ? 4 :
( fsb . flags & FMOD_FSB_SOURCE_MPEG_PADDED ? 2 : 0 ) ) ) ;
2017-03-13 20:05:28 +01:00
2018-01-20 01:40:42 +01:00
//VGM_ASSERT(fsb.mode & FSOUND_MPEG_LAYER2, "FSB FSOUND_MPEG_LAYER2 found\n");/* not always set anyway */
VGM_ASSERT ( fsb . mode & FSOUND_IGNORETAGS , " FSB FSOUND_IGNORETAGS found \n " ) ;
2017-03-13 20:05:28 +01:00
2018-01-04 23:22:03 +01:00
vgmstream - > codec_data = init_mpeg_custom ( streamFile , start_offset , & vgmstream - > coding_type , vgmstream - > channels , MPEG_FSB , & cfg ) ;
2017-07-29 23:14:04 +02:00
if ( ! vgmstream - > codec_data ) goto fail ;
2017-12-17 19:25:10 +01:00
vgmstream - > layout_type = layout_none ;
2016-12-29 14:06:57 +01:00
# else
2017-07-29 23:14:04 +02:00
goto fail ; /* FFmpeg can't properly read FSB4 or FMOD's 0-padded MPEG data @ start_offset */
2012-08-13 08:50:57 +02:00
# endif
}
2018-01-20 01:40:42 +01:00
else if ( fsb . mode & FSOUND_IMAADPCM ) { /* FSB3: Bioshock (PC); FSB4: Blade Kitten (PC) */
2017-02-04 19:37:48 +01:00
/* FSOUND_IMAADPCMSTEREO is "noninterleaved, true stereo IMA", but doesn't seem to be any different
* ( found in FSB4 : Shatter , Blade Kitten ( PC ) , Hard Corps : Uprising ( PS3 ) ) */
2012-08-13 08:50:57 +02:00
2017-02-04 19:37:48 +01:00
vgmstream - > coding_type = coding_XBOX ;
vgmstream - > layout_type = layout_none ;
2018-01-20 01:40:42 +01:00
/* "interleaved header" IMA, only used with >2ch (ex. Blade Kitten 5.1) */
2017-02-04 19:37:48 +01:00
if ( vgmstream - > channels > 2 )
vgmstream - > coding_type = coding_FSB_IMA ;
2012-08-13 08:50:57 +02:00
}
2018-01-20 01:40:42 +01:00
else if ( fsb . mode & FSOUND_VAG ) { /* FSB1: Jurassic Park Operation Genesis (PS2), FSB4: Spider Man Web of Shadows (PSP) */
2016-12-29 14:06:57 +01:00
vgmstream - > coding_type = coding_PSX ;
vgmstream - > layout_type = layout_interleave ;
vgmstream - > interleave_block_size = 0x10 ;
2012-08-13 08:50:57 +02:00
}
2018-01-20 01:40:42 +01:00
else if ( fsb . mode & FSOUND_XMA ) { /* FSB4: Armored Core V (X360), Hard Corps (X360) */
2016-12-29 14:06:57 +01:00
# if defined(VGM_USE_FFMPEG)
2018-01-20 01:40:42 +01:00
uint8_t buf [ 0x100 ] ;
2016-12-29 14:06:57 +01:00
size_t bytes , block_size , block_count ;
2018-01-20 01:40:42 +01:00
2017-09-29 23:26:42 +02:00
block_size = 0x8000 ; /* FSB default */
2018-01-20 01:40:42 +01:00
block_count = fsb . stream_size / block_size ; /* not accurate but not needed (custom_data_offset+0x14 -1?) */
2016-12-29 14:06:57 +01:00
2018-01-20 01:40:42 +01:00
bytes = ffmpeg_make_riff_xma2 ( buf , 0x100 , fsb . num_samples , fsb . stream_size , fsb . channels , fsb . sample_rate , block_count , block_size ) ;
if ( bytes < = 0 ) goto fail ;
2010-09-15 21:37:54 +02:00
2018-01-20 01:40:42 +01:00
vgmstream - > codec_data = init_ffmpeg_header_offset ( streamFile , buf , bytes , start_offset , fsb . stream_size ) ;
if ( ! vgmstream - > codec_data ) goto fail ;
2016-12-29 14:06:57 +01:00
vgmstream - > coding_type = coding_FFmpeg ;
vgmstream - > layout_type = layout_none ;
2010-09-15 21:37:54 +02:00
# else
goto fail ;
# endif
2016-12-29 14:06:57 +01:00
}
2018-01-20 01:40:42 +01:00
else if ( fsb . mode & FSOUND_GCADPCM ) {
2016-12-29 14:06:57 +01:00
/* FSB3: ?; FSB4: de Blob (Wii), Night at the Museum, M. Night Shyamalan Avatar: The Last Airbender */
2017-12-06 21:04:34 +01:00
vgmstream - > coding_type = coding_NGC_DSP_subint ;
vgmstream - > layout_type = layout_none ;
2016-12-29 14:06:57 +01:00
vgmstream - > interleave_block_size = 0x2 ;
2017-01-13 23:56:48 +01:00
dsp_read_coefs_be ( vgmstream , streamFile , custom_data_offset , 0x2e ) ;
2016-12-29 14:06:57 +01:00
}
2018-01-20 01:40:42 +01:00
else if ( fsb . mode & FSOUND_CELT ) { /* FSB4: War Thunder (PC), The Witcher 2 (PC) */
2016-12-29 14:06:57 +01:00
VGM_LOG ( " FSB4 FSOUND_CELT found \n " ) ;
2010-09-15 21:37:54 +02:00
goto fail ;
2016-12-29 14:06:57 +01:00
}
else { /* PCM */
2018-01-20 01:40:42 +01:00
if ( fsb . mode & FSOUND_8BITS ) {
vgmstream - > coding_type = ( fsb . mode & FSOUND_UNSIGNED ) ? coding_PCM8_U : coding_PCM8 ;
2016-12-29 14:06:57 +01:00
vgmstream - > layout_type = layout_interleave ;
vgmstream - > interleave_block_size = 0x1 ;
2017-08-12 11:46:28 +02:00
}
2018-01-20 01:40:42 +01:00
else { /* Rocket Knight (PC), Another Century's Episode R (PS3), Toy Story 3 (Wii) */
2016-12-29 14:06:57 +01:00
/* sometimes FSOUND_STEREO/FSOUND_MONO is not set (ex. Dead Space iOS),
* or only STEREO / MONO but not FSOUND_8BITS / FSOUND_16BITS is set */
2018-01-20 01:40:42 +01:00
vgmstream - > coding_type = ( fsb . flags & FMOD_FSB_SOURCE_BIGENDIANPCM ) ? coding_PCM16BE : coding_PCM16LE ;
2017-01-02 15:42:26 +01:00
vgmstream - > layout_type = layout_interleave ;
2016-12-29 14:06:57 +01:00
vgmstream - > interleave_block_size = 0x2 ;
}
}
2010-09-15 21:37:54 +02:00
2016-12-29 14:06:57 +01:00
/* full channel interleave, used in short streams (ex. de Blob Wii SFXs) */
2018-01-20 01:40:42 +01:00
if ( fsb . channels > 1 & & ( fsb . flags & FMOD_FSB_SOURCE_NOTINTERLEAVED ) ) {
2017-12-06 21:04:34 +01:00
if ( vgmstream - > coding_type = = coding_NGC_DSP_subint )
vgmstream - > coding_type = coding_NGC_DSP ;
2016-12-29 14:06:57 +01:00
vgmstream - > layout_type = layout_interleave ;
2018-01-20 01:40:42 +01:00
vgmstream - > interleave_block_size = fsb . stream_size / fsb . channels ;
2016-12-29 14:06:57 +01:00
}
2010-09-15 21:37:54 +02:00
/* open the file for reading */
2017-01-14 00:59:54 +01:00
if ( ! vgmstream_open_stream ( vgmstream , streamFile , start_offset ) )
2016-12-29 14:06:57 +01:00
goto fail ;
2010-09-15 21:37:54 +02:00
return vgmstream ;
fail :
2016-12-29 14:06:57 +01:00
close_vgmstream ( vgmstream ) ;
2010-09-15 21:37:54 +02:00
return NULL ;
}
2018-01-20 00:55:37 +01:00
2018-01-20 01:40:42 +01:00
2018-01-20 00:55:37 +01:00
/* FSB4 with "\0WAV" Header, found in Deadly Creatures (Wii).
* Has a 0x10 BE header that holds the filesize ( unsure if this is from a proper rip ) . */
VGMSTREAM * init_vgmstream_fsb4_wav ( STREAMFILE * streamFile ) {
VGMSTREAM * vgmstream = NULL ;
STREAMFILE * custom_streamFile = NULL ;
off_t custom_start = 0x10 ;
2018-01-20 01:40:42 +01:00
size_t custom_size = get_streamfile_size ( streamFile ) - 0x10 - 0x10 ; //todo
2018-01-20 00:55:37 +01:00
/* check extensions */
if ( ! check_extensions ( streamFile , " fsb,wii " ) )
goto fail ;
if ( read_32bitBE ( 0x00 , streamFile ) ! = 0x00574156 ) /* "\0WAV" */
goto fail ;
/* parse FSB subfile */
custom_streamFile = open_clamp_streamfile ( open_wrap_streamfile ( streamFile ) , custom_start , custom_size ) ;
if ( ! custom_streamFile ) goto fail ;
2018-01-20 01:40:42 +01:00
vgmstream = init_vgmstream_fsb ( custom_streamFile ) ;
2018-01-20 00:55:37 +01:00
if ( ! vgmstream ) goto fail ;
close_streamfile ( custom_streamFile ) ;
return vgmstream ;
fail :
close_streamfile ( custom_streamFile ) ;
close_vgmstream ( vgmstream ) ;
return NULL ;
}