Updated VGMStream to r1050-3533-g95709ce3

This commit is contained in:
Christopher Snowhill 2020-12-22 00:44:25 -08:00
parent 3a677f5f2e
commit f1c45a81ef
40 changed files with 2194 additions and 1296 deletions

View file

@ -504,6 +504,10 @@
838BDB7B1D3B1FC20022CA6F /* CoreMedia.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 838BDB7A1D3B1FC20022CA6F /* CoreMedia.framework */; };
838BDB7D1D3B1FCC0022CA6F /* CoreVideo.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 838BDB7C1D3B1FCC0022CA6F /* CoreVideo.framework */; };
838BDB7F1D3B1FD10022CA6F /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 838BDB7E1D3B1FD10022CA6F /* Cocoa.framework */; };
8399335B2591E896001855AF /* vorbis_custom_utils_awc.c in Sources */ = {isa = PBXBuildFile; fileRef = 839933572591E896001855AF /* vorbis_custom_utils_awc.c */; };
8399335F2591E8C1001855AF /* ifs.c in Sources */ = {isa = PBXBuildFile; fileRef = 8399335C2591E8C0001855AF /* ifs.c */; };
839933602591E8C1001855AF /* ubi_sb_garbage_streamfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 8399335D2591E8C0001855AF /* ubi_sb_garbage_streamfile.h */; };
839933612591E8C1001855AF /* sbk.c in Sources */ = {isa = PBXBuildFile; fileRef = 8399335E2591E8C0001855AF /* sbk.c */; };
83997F5B22D9569E00633184 /* rad.c in Sources */ = {isa = PBXBuildFile; fileRef = 83997F5722D9569E00633184 /* rad.c */; };
839B54521EEE1D9600048A2D /* ngc_ulw.c in Sources */ = {isa = PBXBuildFile; fileRef = 831BD11F1EEE1CF200198540 /* ngc_ulw.c */; };
839E21E01F2EDAF100EE54D7 /* vorbis_custom_data_fsb.h in Headers */ = {isa = PBXBuildFile; fileRef = 839E21D61F2EDAF000EE54D7 /* vorbis_custom_data_fsb.h */; };
@ -1243,6 +1247,10 @@
838BDB7A1D3B1FC20022CA6F /* CoreMedia.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreMedia.framework; path = System/Library/Frameworks/CoreMedia.framework; sourceTree = SDKROOT; };
838BDB7C1D3B1FCC0022CA6F /* CoreVideo.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreVideo.framework; path = System/Library/Frameworks/CoreVideo.framework; sourceTree = SDKROOT; };
838BDB7E1D3B1FD10022CA6F /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; };
839933572591E896001855AF /* vorbis_custom_utils_awc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = vorbis_custom_utils_awc.c; sourceTree = "<group>"; };
8399335C2591E8C0001855AF /* ifs.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ifs.c; sourceTree = "<group>"; };
8399335D2591E8C0001855AF /* ubi_sb_garbage_streamfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ubi_sb_garbage_streamfile.h; sourceTree = "<group>"; };
8399335E2591E8C0001855AF /* sbk.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = sbk.c; sourceTree = "<group>"; };
83997F5722D9569E00633184 /* rad.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = rad.c; sourceTree = "<group>"; };
839E21D61F2EDAF000EE54D7 /* vorbis_custom_data_fsb.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = vorbis_custom_data_fsb.h; sourceTree = "<group>"; };
839E21D71F2EDAF000EE54D7 /* vorbis_custom_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = vorbis_custom_decoder.c; sourceTree = "<group>"; };
@ -1598,6 +1606,7 @@
839E21DC1F2EDAF000EE54D7 /* vorbis_custom_data_wwise.h */,
839E21D71F2EDAF000EE54D7 /* vorbis_custom_decoder.c */,
839E21DB1F2EDAF000EE54D7 /* vorbis_custom_decoder.h */,
839933572591E896001855AF /* vorbis_custom_utils_awc.c */,
839E21DA1F2EDAF000EE54D7 /* vorbis_custom_utils_fsb.c */,
83E56BA01F2EE3500026BC60 /* vorbis_custom_utils_ogl.c */,
839E21DF1F2EDAF000EE54D7 /* vorbis_custom_utils_sk.c */,
@ -1787,6 +1796,7 @@
834FE0DF215C79EB000A5D3D /* hd3_bd3.c */,
836F6E5318BDC2180095E648 /* his.c */,
834FE0E0215C79EB000A5D3D /* idsp_ie.c */,
8399335C2591E8C0001855AF /* ifs.c */,
83C7280922BC893C00678B4A /* ikm.c */,
837CEAE623487F2B00E62A4A /* ima.c */,
832BF81121E05149006F50F1 /* imc.c */,
@ -1980,6 +1990,7 @@
836F6EEB18BDC2190095E648 /* sat_baka.c */,
836F6EEC18BDC2190095E648 /* sat_dvi.c */,
836F6EED18BDC2190095E648 /* sat_sap.c */,
8399335E2591E8C0001855AF /* sbk.c */,
8349A8F51FE6257D00E26435 /* scd_pcm.c */,
836F6EEE18BDC2190095E648 /* sd9.c */,
834FE0E6215C79EC000A5D3D /* sdf.c */,
@ -2031,6 +2042,7 @@
83031ED4243C510400C3F3E0 /* ubi_lyn_streamfile.h */,
8306B0CA2098458E000302D4 /* ubi_lyn.c */,
831BA6131EAC61A500CF89B0 /* ubi_raki.c */,
8399335D2591E8C0001855AF /* ubi_sb_garbage_streamfile.h */,
8375737521F950EC00F01AF5 /* ubi_sb_streamfile.h */,
8349A8F41FE6257D00E26435 /* ubi_sb.c */,
834FE0C5215C79E6000A5D3D /* ue4opus.c */,
@ -2156,6 +2168,7 @@
83FC176F23AC58D100E1025F /* cri_utf.h in Headers */,
83C7282822BC8C1500678B4A /* mixing.h in Headers */,
832BF82C21E0514B006F50F1 /* hca_keys_awb.h in Headers */,
839933602591E8C1001855AF /* ubi_sb_garbage_streamfile.h in Headers */,
8306B0E020984590000302D4 /* ppst_streamfile.h in Headers */,
83AA7F722519BFEA004C5298 /* vorbis_bitreader.h in Headers */,
834FE0BA215C798C000A5D3D /* ea_mt_decoder_utk.h in Headers */,
@ -2390,6 +2403,7 @@
837CEA7923487E2500E62A4A /* ubi_adpcm_decoder.c in Sources */,
834FE0EB215C79ED000A5D3D /* str_wav.c in Sources */,
83269DD32399F5DE00F49FE3 /* ivag.c in Sources */,
8399335B2591E896001855AF /* vorbis_custom_utils_awc.c in Sources */,
8349A8DF1FE6251F00E26435 /* vorbis_custom_utils_vid1.c in Sources */,
83A21F8D201D8982000F04B9 /* sqex_sead.c in Sources */,
83EED5D3203A8BC7008BEB45 /* ea_swvr.c in Sources */,
@ -2846,6 +2860,7 @@
836F6FBF18BDC2190095E648 /* otm.c in Sources */,
8306B0B420984552000302D4 /* blocked_tra.c in Sources */,
834FE0B8215C798C000A5D3D /* acm_decoder_decode.c in Sources */,
8399335F2591E8C1001855AF /* ifs.c in Sources */,
8373342A23F60CDC00DE14DC /* lrmd.c in Sources */,
836F6FBD18BDC2190095E648 /* nwa.c in Sources */,
837CEAFC23487F2C00E62A4A /* raw_wavm.c in Sources */,
@ -2859,6 +2874,7 @@
83AA7F7F2519C042004C5298 /* bsf.c in Sources */,
836F6F4118BDC2190095E648 /* blocked.c in Sources */,
836F6F3B18BDC2190095E648 /* ws_decoder.c in Sources */,
839933612591E8C1001855AF /* sbk.c in Sources */,
838BDB6E1D3B043C0022CA6F /* ffmpeg.c in Sources */,
832BF82B21E0514B006F50F1 /* xopus.c in Sources */,
836F6F3418BDC2190095E648 /* nwa_decoder.c in Sources */,

View file

@ -37,7 +37,8 @@ void decode_apple_ima4(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelsp
void decode_fsb_ima(VGMSTREAM* vgmstream, VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel);
void decode_wwise_ima(VGMSTREAM* vgmstream, VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel);
void decode_awc_ima(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
void decode_ubi_ima(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, int codec_config);
void decode_ubi_ima(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel);
void decode_ubi_sce_ima(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel);
void decode_h4m_ima(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, uint16_t frame_format);
void decode_cd_ima(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel);
size_t ima_bytes_to_samples(size_t bytes, int channels);
@ -343,6 +344,7 @@ typedef enum {
VORBIS_OGL, /* Shin'en OGL: custom packet headers */
VORBIS_SK, /* Silicon Knights AUD: "OggS" replaced by "SK" */
VORBIS_VID1, /* Neversoft VID1: custom packet blocks/headers */
VORBIS_AWC, /* Rockstar AWC: custom packet blocks/headers */
} vorbis_custom_t;
/* config for Wwise Vorbis (3 types for flexibility though not all combinations exist) */
@ -365,6 +367,9 @@ typedef struct {
wwise_header_t header_type;
wwise_packet_t packet_type;
/* AWC config */
off_t header_offset;
/* output (kinda ugly here but to simplify) */
off_t data_start_offset;
@ -543,6 +548,8 @@ typedef struct {
/* frame table */
off_t table_offset;
int table_count;
/* fixed frames */
uint16_t frame_size;
} opus_config;
ffmpeg_codec_data* init_ffmpeg_switch_opus_config(STREAMFILE* sf, off_t start_offset, size_t data_size, opus_config* cfg);
@ -552,6 +559,7 @@ ffmpeg_codec_data* init_ffmpeg_ea_opus(STREAMFILE* sf, off_t start_offset, size_
ffmpeg_codec_data* init_ffmpeg_x_opus(STREAMFILE* sf, off_t table_offset, int table_count, off_t data_offset, size_t data_size, int channels, int skip);
ffmpeg_codec_data* init_ffmpeg_fsb_opus(STREAMFILE* sf, off_t start_offset, size_t data_size, int channels, int skip, int sample_rate);
ffmpeg_codec_data* init_ffmpeg_wwise_opus(STREAMFILE* sf, off_t data_offset, size_t data_size, opus_config* cfg);
ffmpeg_codec_data* init_ffmpeg_fixed_opus(STREAMFILE* sf, off_t data_offset, size_t data_size, opus_config* cfg);
size_t switch_opus_get_samples(off_t offset, size_t stream_size, STREAMFILE* sf);

View file

@ -17,7 +17,7 @@
* https://github.com/hcs64/ww2ogg
*/
typedef enum { OPUS_SWITCH, OPUS_UE4_v1, OPUS_UE4_v2, OPUS_EA, OPUS_X, OPUS_FSB, OPUS_WWISE } opus_type_t;
typedef enum { OPUS_SWITCH, OPUS_UE4_v1, OPUS_UE4_v2, OPUS_EA, OPUS_X, OPUS_FSB, OPUS_WWISE, OPUS_FIXED } opus_type_t;
static size_t make_oggs_first(uint8_t *buf, int buf_size, opus_config *cfg);
static size_t make_oggs_page(uint8_t *buf, int buf_size, size_t data_size, int page_sequence, int granule);
@ -36,6 +36,9 @@ typedef struct {
int table_count;
uint16_t* frame_table;
/* fixed frame size for variations that use this */
uint16_t frame_size;
/* state */
off_t logical_offset; /* offset that corresponds to physical_offset */
off_t physical_offset; /* actual file offset */
@ -130,6 +133,10 @@ static size_t opus_io_read(STREAMFILE* sf, uint8_t *dest, off_t offset, size_t l
data_size = get_table_frame_size(data, data->sequence - 2);
skip_size = 0;
break;
case OPUS_FIXED:
data_size = data->frame_size;
skip_size = 0;
break;
default:
return 0;
}
@ -231,6 +238,10 @@ static size_t opus_io_size(STREAMFILE* sf, opus_io_data* data) {
data_size = get_table_frame_size(data, packet);
skip_size = 0x00;
break;
case OPUS_FIXED:
data_size = data->frame_size;
skip_size = 0;
break;
default:
return 0;
}
@ -295,7 +306,7 @@ static void opus_io_close(STREAMFILE* sf, opus_io_data* data) {
/* Prepares custom IO for custom Opus, that is converted to Ogg Opus on the fly */
static STREAMFILE* setup_opus_streamfile(STREAMFILE* sf, opus_config *cfg, off_t stream_offset, size_t stream_size, opus_type_t type) {
static STREAMFILE* setup_opus_streamfile(STREAMFILE* sf, opus_config* cfg, off_t stream_offset, size_t stream_size, opus_type_t type) {
STREAMFILE* new_sf = NULL;
opus_io_data io_data = {0};
@ -308,6 +319,7 @@ static STREAMFILE* setup_opus_streamfile(STREAMFILE* sf, opus_config *cfg, off_t
io_data.physical_offset = stream_offset;
io_data.table_offset = cfg->table_offset;
io_data.table_count = cfg->table_count;
io_data.frame_size = cfg->frame_size;
io_data.head_size = make_oggs_first(io_data.head_buffer, sizeof(io_data.head_buffer), cfg);
if (!io_data.head_size) goto fail;
@ -612,12 +624,16 @@ static size_t custom_opus_get_samples(off_t offset, size_t stream_size, STREAMFI
skip_size = 0x02;
break;
#if 0 // needs data* for frame table, but num_samples should exist on header
#if 0 //needs data*, num_samples should exist on header
case OPUS_X:
case OPUS_WWISE:
data_size = get_table_frame_size(data, packet);
skip_size = 0x00;
break;
case OPUS_FIXED:
data_size = data->frame_size;
skip_size = 0;
break;
#endif
default:
return 0;
@ -661,6 +677,11 @@ static size_t custom_opus_get_encoder_delay(off_t offset, STREAMFILE* sf, opus_t
case OPUS_WWISE:
skip_size = 0x00;
break;
#if 0 //should exist on header
case OPUS_FIXED:
skip_size = 0x00;
break;
#endif
default:
return 0;
}
@ -756,6 +777,9 @@ ffmpeg_codec_data* init_ffmpeg_fsb_opus(STREAMFILE* sf, off_t start_offset, size
ffmpeg_codec_data* init_ffmpeg_wwise_opus(STREAMFILE* sf, off_t data_offset, size_t data_size, opus_config* cfg) {
return init_ffmpeg_custom_opus_config(sf, data_offset, data_size, cfg, OPUS_WWISE);
}
ffmpeg_codec_data* init_ffmpeg_fixed_opus(STREAMFILE* sf, off_t data_offset, size_t data_size, opus_config* cfg) {
return init_ffmpeg_custom_opus_config(sf, data_offset, data_size, cfg, OPUS_FIXED);
}
static opus_type_t get_ue4opus_version(STREAMFILE* sf, off_t offset) {
int read_samples, calc_samples;

View file

@ -1065,9 +1065,8 @@ void decode_awc_ima(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspa
/* DVI stereo/mono with some mini header and sample output */
void decode_ubi_ima(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, int codec_config) {
void decode_ubi_ima(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) {
int i, sample_count = 0;
int has_header = (codec_config & 0x80) == 0;
int32_t hist1 = stream->adpcm_history1_32;
int step_index = stream->adpcm_step_index;
@ -1075,7 +1074,7 @@ void decode_ubi_ima(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspa
//internal interleave
//header in the beginning of the stream
if (has_header && stream->channel_start_offset == stream->offset) {
if (stream->channel_start_offset == stream->offset) {
int version, big_endian, header_samples, max_samples_to_do;
int16_t (*read_16bit)(off_t,STREAMFILE*) = NULL;
off_t offset = stream->offset;
@ -1108,16 +1107,10 @@ void decode_ubi_ima(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspa
}
}
if (has_header) {
first_sample -= 10; //todo fix hack (needed to adjust nibble offset below)
if (step_index < 0) step_index = 0;
if (step_index > 88) step_index = 88;
} else {
if (step_index < 0) step_index = 0;
if (step_index > 89) step_index = 89;
}
first_sample -= 10; //todo fix hack (needed to adjust nibble offset below)
if (step_index < 0) step_index = 0;
if (step_index > 88) step_index = 88;
for (i = first_sample; i < first_sample + samples_to_do; i++, sample_count += channelspacing) {
off_t byte_offset = channelspacing == 1 ?
@ -1131,12 +1124,39 @@ void decode_ubi_ima(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspa
outbuf[sample_count] = (short)(hist1); /* all samples are written */
}
//external interleave
stream->adpcm_history1_32 = hist1;
stream->adpcm_step_index = step_index;
}
/* standard IMA but with a tweak for Ubi's encoder bug with step index (see blocked_ubi_sce.c) */
void decode_ubi_sce_ima(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) {
int i, sample_count = 0;
int32_t hist1 = stream->adpcm_history1_32;
int step_index = stream->adpcm_step_index;
//internal interleave
if (step_index < 0) step_index = 0;
if (step_index > 89) step_index = 89;
for (i = first_sample; i < first_sample + samples_to_do; i++, sample_count += channelspacing) {
off_t byte_offset = channelspacing == 1 ?
stream->offset + i/2 : /* mono mode */
stream->offset + i; /* stereo mode */
int nibble_shift = channelspacing == 1 ?
(!(i%2) ? 4:0) : /* mono mode (high first) */
(channel==0 ? 4:0); /* stereo mode (high=L,low=R) */
std_ima_expand_nibble(stream, byte_offset,nibble_shift, &hist1, &step_index);
outbuf[sample_count] = (short)(hist1); /* all samples are written */
}
stream->adpcm_history1_32 = hist1;
stream->adpcm_step_index = step_index;
}
/* IMA with variable frame formats controlled by the block layout. The original code uses
* tables mapping all standard IMA combinations (to optimize calculations), but decodes the same.
* Based on HCS's and Nisto's reverse engineering in h4m_audio_decode. */

View file

@ -97,23 +97,23 @@ static int ealayer3_is_empty_frame_v2p(STREAMFILE* sf, off_t offset);
/* **************************************************************************** */
/* init codec from an EALayer3 frame */
int mpeg_custom_setup_init_ealayer3(STREAMFILE* streamfile, off_t start_offset, mpeg_codec_data* data, coding_t *coding_type) {
int mpeg_custom_setup_init_ealayer3(STREAMFILE* sf, off_t start_offset, mpeg_codec_data* data, coding_t *coding_type) {
int ok;
ealayer3_buffer_t ib = {0};
ealayer3_frame_t eaf;
//;VGM_LOG("init at %lx\n", start_offset);
//;VGM_LOG("init at %lx, %x\n", start_offset, read_u32be(start_offset, sf));
/* get first frame for info */
{
ib.sf = streamfile;
ib.sf = sf;
ib.offset = start_offset;
ib.is.buf = ib.buf;
ok = ealayer3_parse_frame(data, -1, &ib, &eaf);
if (!ok) goto fail;
}
;VGM_ASSERT(!eaf.mpeg1, "EAL3: mpeg2 found at 0x%lx\n", start_offset); /* rare [FIFA 08 (PS3) abk] */
VGM_ASSERT(!eaf.mpeg1, "EAL3: mpeg2 found at 0x%lx\n", start_offset); /* rare [FIFA 08 (PS3) abk] */
*coding_type = coding_MPEG_ealayer3;
data->channels_per_frame = eaf.channels;
@ -385,11 +385,16 @@ static int ealayer3_parse_frame_v2(ealayer3_buffer_t* ib, ealayer3_frame_t* eaf)
ok = ealayer3_parse_frame_common(ib, eaf);
if (!ok) goto fail;
}
else {
/* rarely frames contain PCM data only [FIFA 2014 World Cup Brazil (PS3)] */
eaf->channels = eaf->v2_stereo_flag + 1;
}
VGM_ASSERT(eaf->v2_extended_flag && eaf->v2_common_size == 0, "EA EAL3: v2 empty frame\n"); /* seen in V2S */
VGM_ASSERT(eaf->v2_extended_flag && eaf->v2_offset_samples > 0, "EA EAL3: v2_offset_mode=%x with value=0x%x\n", eaf->v2_offset_mode, eaf->v2_offset_samples);
//VGM_ASSERT(eaf->v2_pcm_samples > 0, "EA EAL3: v2_pcm_samples 0x%x\n", eaf->v2_pcm_samples);
eaf->pcm_size = (2*eaf->v2_pcm_samples * eaf->channels);
eaf->pcm_size = (eaf->v2_pcm_samples * sizeof(int16_t) * eaf->channels);
eaf->eaframe_size = eaf->pre_size + eaf->common_size + eaf->pcm_size;
@ -408,14 +413,14 @@ fail:
/* parses an EALayer3 frame (common part) */
static int ealayer3_parse_frame_common(ealayer3_buffer_t* ib, ealayer3_frame_t* eaf) {
/* index tables */
static const int versions[4] = { /* MPEG 2.5 */ 3, /* reserved */ -1, /* MPEG 2 */ 2, /* MPEG 1 */ 1 };
static const int sample_rates[4][4] = { /* [version_index][sample rate index] */
static const int version_table[4] = { /* MPEG 2.5 */ 3, /* reserved */ -1, /* MPEG 2 */ 2, /* MPEG 1 */ 1 };
static const int sample_rate_table[4][4] = { /* [version_index][sample rate index] */
{ 11025, 12000, 8000, -1}, /* MPEG2.5 */
{ -1, -1, -1, -1}, /* reserved */
{ 22050, 24000, 16000, -1}, /* MPEG2 */
{ 44100, 48000, 32000, -1}, /* MPEG1 */
};
static const int channels[4] = { 2,2,2, 1 }; /* [channel_mode] */
static const int channel_table[4] = { 2,2,2, 1 }; /* [channel_mode] */
bitstream_t* is = &ib->is;
off_t start_b_off = is->b_off;
@ -441,9 +446,9 @@ static int ealayer3_parse_frame_common(ealayer3_buffer_t* ib, ealayer3_frame_t*
/* derived */
eaf->version = versions[eaf->version_index];
eaf->channels = channels[eaf->channel_mode];
eaf->sample_rate = sample_rates[eaf->version_index][eaf->sample_rate_index];
eaf->version = version_table[eaf->version_index];
eaf->channels = channel_table[eaf->channel_mode];
eaf->sample_rate = sample_rate_table[eaf->version_index][eaf->sample_rate_index];
eaf->mpeg1 = (eaf->version == 1);
if (eaf->version == -1 || eaf->sample_rate == -1) {

View file

@ -80,7 +80,7 @@ static void oki16_expand_nibble(VGMSTREAMCHANNEL* stream, off_t byte_offset, int
}
/* Possible variation for adp_konami (Viper hardware):
* delta = ((n&7) + 0.5) * stepsize / 4; clamps 2047,-2048; nigh nibble first
* delta = ((n&7) + 0.5) * stepsize / 4; clamps 2047,-2048
*
* Results are very similar, but can't verify actual decoding, and oki4s is used in
* Jubeat (also Konami) so it makes sense they would have reused it.
@ -144,7 +144,7 @@ void decode_pcfx(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing,
}
/* OKI variation with 16-bit output (vs standard's 12-bit), found in FrontWing's PS2 games (Sweet Legacy, Hooligan).
* Reverse engineered from the ELF with help from the folks at hcs. */
* Reverse engineered from the ELF with help from the folks at hcs. Codec has no name so OKI16 is just a description. */
void decode_oki16(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) {
int i, sample_count = 0;
int32_t hist1 = stream->adpcm_history1_32;
@ -176,7 +176,7 @@ void decode_oki16(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing
}
/* OKI variation with 16-bit output (vs standard's 12-bit) and pre-adjusted tables (shifted by 4), found in Jubeat Clan (AC).
* Reverse engineered from the DLLs. */
* Reverse engineered from the DLLs (libbmsd-engine.dll). Internally code calls it "adpcm", so OKI4S is just a description. */
void decode_oki4s(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) {
int i, sample_count = 0;
int32_t hist1 = stream->adpcm_history1_32;
@ -195,7 +195,7 @@ void decode_oki4s(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing
for (i = first_sample; i < first_sample + samples_to_do; i++, sample_count += channelspacing) {
off_t byte_offset = is_stereo ?
stream->offset + i : /* stereo: one nibble per channel */
stream->offset + i/2; /* mono: consecutive nibbles (assumed) */
stream->offset + i/2; /* mono: consecutive nibbles */
int nibble_shift =
is_stereo ? ((channel&1) ? 0:4) : ((i&1) ? 0:4); /* even = high, odd = low */

View file

@ -51,6 +51,7 @@ vorbis_custom_codec_data* init_vorbis_custom(STREAMFILE* sf, off_t start_offset,
case VORBIS_OGL: ok = vorbis_custom_setup_init_ogl(sf, start_offset, data); break;
case VORBIS_SK: ok = vorbis_custom_setup_init_sk(sf, start_offset, data); break;
case VORBIS_VID1: ok = vorbis_custom_setup_init_vid1(sf, start_offset, data); break;
case VORBIS_AWC: ok = vorbis_custom_setup_init_awc(sf, start_offset, data); break;
default: goto fail;
}
if(!ok) goto fail;
@ -134,6 +135,7 @@ void decode_vorbis_custom(VGMSTREAM* vgmstream, sample_t* outbuf, int32_t sample
case VORBIS_OGL: ok = vorbis_custom_parse_packet_ogl(stream, data); break;
case VORBIS_SK: ok = vorbis_custom_parse_packet_sk(stream, data); break;
case VORBIS_VID1: ok = vorbis_custom_parse_packet_vid1(stream, data); break;
case VORBIS_AWC: ok = vorbis_custom_parse_packet_awc(stream, data); break;
default: goto decode_fail;
}
if(!ok) {

View file

@ -43,12 +43,14 @@ int vorbis_custom_setup_init_wwise(STREAMFILE* sf, off_t start_offset, vorbis_cu
int vorbis_custom_setup_init_ogl(STREAMFILE* sf, off_t start_offset, vorbis_custom_codec_data* data);
int vorbis_custom_setup_init_sk(STREAMFILE* sf, off_t start_offset, vorbis_custom_codec_data* data);
int vorbis_custom_setup_init_vid1(STREAMFILE* sf, off_t start_offset, vorbis_custom_codec_data* data);
int vorbis_custom_setup_init_awc(STREAMFILE* sf, off_t start_offset, vorbis_custom_codec_data* data);
int vorbis_custom_parse_packet_fsb(VGMSTREAMCHANNEL *stream, vorbis_custom_codec_data* data);
int vorbis_custom_parse_packet_wwise(VGMSTREAMCHANNEL *stream, vorbis_custom_codec_data* data);
int vorbis_custom_parse_packet_ogl(VGMSTREAMCHANNEL *stream, vorbis_custom_codec_data* data);
int vorbis_custom_parse_packet_sk(VGMSTREAMCHANNEL *stream, vorbis_custom_codec_data* data);
int vorbis_custom_parse_packet_vid1(VGMSTREAMCHANNEL *stream, vorbis_custom_codec_data* data);
int vorbis_custom_parse_packet_fsb(VGMSTREAMCHANNEL* stream, vorbis_custom_codec_data* data);
int vorbis_custom_parse_packet_wwise(VGMSTREAMCHANNEL* stream, vorbis_custom_codec_data* data);
int vorbis_custom_parse_packet_ogl(VGMSTREAMCHANNEL* stream, vorbis_custom_codec_data* data);
int vorbis_custom_parse_packet_sk(VGMSTREAMCHANNEL* stream, vorbis_custom_codec_data* data);
int vorbis_custom_parse_packet_vid1(VGMSTREAMCHANNEL* stream, vorbis_custom_codec_data* data);
int vorbis_custom_parse_packet_awc(VGMSTREAMCHANNEL* stream, vorbis_custom_codec_data* data);
#endif/* VGM_USE_VORBIS */
#endif/*_VORBIS_CUSTOM_DECODER_H_ */

View file

@ -0,0 +1,86 @@
#include "vorbis_custom_decoder.h"
#ifdef VGM_USE_VORBIS
#include <vorbis/codec.h>
/* **************************************************************************** */
/* EXTERNAL API */
/* **************************************************************************** */
/**
* AWC removes the Ogg layer and uses 32b frame sizes for headers and 16b frame sizes for data
*/
int vorbis_custom_setup_init_awc(STREAMFILE* sf, off_t start_offset, vorbis_custom_codec_data* data) {
off_t offset = data->config.header_offset;
size_t packet_size;
/* read 3 packets with triad (id/comment/setup), each with an AWC header */
/* normal identificacion packet */
packet_size = read_u32le(offset, sf);
if (packet_size > data->buffer_size) goto fail;
data->op.bytes = read_streamfile(data->buffer, offset + 0x04, packet_size, sf);
if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) != 0)
goto fail;
offset += 0x04 + packet_size;
/* normal comment packet */
packet_size = read_u32le(offset, sf);
if (packet_size > data->buffer_size) goto fail;
data->op.bytes = read_streamfile(data->buffer, offset + 0x04, packet_size, sf);
if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) !=0)
goto fail;
offset += 0x04 + packet_size;
/* normal setup packet */
packet_size = read_u32le(offset, sf);
if (packet_size > data->buffer_size) goto fail;
data->op.bytes = read_streamfile(data->buffer, offset + 0x04, packet_size, sf);
if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) != 0)
goto fail;
offset += 0x04 + packet_size;
/* data starts separate */
data->config.data_start_offset = start_offset;
return 1;
fail:
return 0;
}
/* as AWC was coded by wackos, frames are forced to fit 0x800 chunks and rest is padded, in both sfx and music/blocked modes
* (ex. read frame until 0x7A0 + next frame is size 0x140 > pads 0x60 and last goes to next chunk) */
static inline off_t find_padding_awc(off_t offset, vorbis_custom_codec_data* data) {
offset = offset - data->config.data_start_offset;
return 0x800 - (offset % 0x800);
}
int vorbis_custom_parse_packet_awc(VGMSTREAMCHANNEL* stream, vorbis_custom_codec_data* data) {
size_t bytes;
/* get next packet size from the AWC 16b header, except between chunks */
data->op.bytes = read_u16le(stream->offset, stream->streamfile);
if (data->op.bytes == 0) { // || (stream->offset - start & 0x800) < 0x01 //todo could pad near block end?
stream->offset += find_padding_awc(stream->offset, data);
data->op.bytes = read_u16le(stream->offset, stream->streamfile);
}
stream->offset += 0x02;
if (data->op.bytes == 0 || data->op.bytes == 0xFFFF || data->op.bytes > data->buffer_size)
goto fail; /* EOF or end padding */
/* read raw block */
bytes = read_streamfile(data->buffer, stream->offset, data->op.bytes, stream->streamfile);
stream->offset += data->op.bytes;
if (bytes != data->op.bytes) goto fail; /* wrong packet? */
return 1;
fail:
return 0;
}
#endif

View file

@ -355,6 +355,7 @@ int get_vgmstream_samples_per_frame(VGMSTREAM* vgmstream) {
case coding_SNDS_IMA:
case coding_OTNS_IMA:
case coding_UBI_IMA:
case coding_UBI_SCE_IMA:
case coding_OKI16:
case coding_OKI4S:
case coding_MTF_IMA:
@ -580,6 +581,8 @@ int get_vgmstream_frame_size(VGMSTREAM* vgmstream) {
return 0; //todo: 0x01?
case coding_UBI_IMA: /* variable (PCM then IMA) */
return 0;
case coding_UBI_SCE_IMA:
return 0;
case coding_XBOX_IMA:
//todo should be 0x48 when stereo, but blocked/interleave layout don't understand stereo codecs
return 0x24; //vgmstream->channels==1 ? 0x24 : 0x48;
@ -1145,8 +1148,13 @@ void decode_vgmstream(VGMSTREAM* vgmstream, int samples_written, int samples_to_
case coding_UBI_IMA:
for (ch = 0; ch < vgmstream->channels; ch++) {
decode_ubi_ima(&vgmstream->ch[ch], buffer+ch,
vgmstream->channels, vgmstream->samples_into_block, samples_to_do, ch,
vgmstream->codec_config);
vgmstream->channels, vgmstream->samples_into_block, samples_to_do, ch);
}
break;
case coding_UBI_SCE_IMA:
for (ch = 0; ch < vgmstream->channels; ch++) {
decode_ubi_sce_ima(&vgmstream->ch[ch], buffer+ch,
vgmstream->channels, vgmstream->samples_into_block, samples_to_do, ch);
}
break;
case coding_H4M_IMA:

View file

@ -103,7 +103,7 @@ static const char* extension_list[] = {
"bgw",
"bh2pcm",
"bik",
"bika",
"bika", //fake extension for .bik (to be removed)
"bik2",
//"bin", //common
"bk2",
@ -221,6 +221,7 @@ static const char* extension_list[] = {
"idwav",
"idx",
"idxma",
"ifs",
"ikm",
"ild",
"ilv", //txth/reserved [Star Wars Episode III (PS2)]
@ -341,7 +342,8 @@ static const char* extension_list[] = {
"n64",
"naac",
"ndp",
"nds",
"ndp", //fake extension/header id for .nds
"ngca",
"nlsd",
"nop",
@ -425,6 +427,7 @@ static const char* extension_list[] = {
"sb5",
"sb6",
"sb7",
"sbk",
"sbr",
"sbv",
"sm0",
@ -749,6 +752,7 @@ static const coding_info coding_info_list[] = {
{coding_REF_IMA, "Reflections 4-bit IMA ADPCM"},
{coding_AWC_IMA, "Rockstar AWC 4-bit IMA ADPCM"},
{coding_UBI_IMA, "Ubisoft 4-bit IMA ADPCM"},
{coding_UBI_SCE_IMA, "Ubisoft 4-bit SCE IMA ADPCM"},
{coding_H4M_IMA, "Hudson HVQM4 4-bit IMA ADPCM"},
{coding_CD_IMA, "Crystal Dynamics 4-bit IMA ADPCM"},
@ -1318,6 +1322,9 @@ static const meta_info meta_info_list[] = {
{meta_WADY, "Marble WADY header"},
{meta_DSP_SQEX, "Square Enix DSP header"},
{meta_DSP_WIIVOICE, "Koei Tecmo WiiVoice header"},
{meta_SBK, "Team17 SBK header"},
{meta_DSP_WIIADPCM, "Exient WIIADPCM header"},
{meta_DSP_CWAC, "CRI CWAC header"},
};
void get_vgmstream_coding_description(VGMSTREAM* vgmstream, char* out, size_t out_size) {

View file

@ -1,4 +1,5 @@
#include "layout.h"
#include "../coding/coding.h"
#include "../vgmstream.h"
/* weird mix of Ubi ADPCM format with Ubi IMA, found in Splinter Cell Essentials (PSP) */
@ -18,8 +19,6 @@ void block_update_ubi_sce(off_t block_offset, VGMSTREAM* vgmstream) {
* (higher bit has a special meaning)
*/
vgmstream->codec_config |= 0x80; /* flag for decoder, ugly I know */
if ((vgmstream->codec_config & 1) == 0) {
header_size = 0x34; /* read header in first subframe */
}
@ -34,8 +33,8 @@ void block_update_ubi_sce(off_t block_offset, VGMSTREAM* vgmstream) {
padding_size = 0x01;
vgmstream->current_block_offset = block_offset;
vgmstream->current_block_size = subframe_size;
vgmstream->next_block_offset = block_offset + header_size * vgmstream->channels + subframe_size + padding_size;
vgmstream->current_block_samples = ima_bytes_to_samples(subframe_size, channels);
for (i = 0; i < vgmstream->channels; i++) {
vgmstream->ch[i].offset = block_offset + header_size * channels;
@ -44,11 +43,11 @@ void block_update_ubi_sce(off_t block_offset, VGMSTREAM* vgmstream) {
vgmstream->ch[i].adpcm_step_index = read_32bitLE(block_offset + header_size * i + 0x04, sf);
vgmstream->ch[i].adpcm_history1_32 = read_32bitLE(block_offset + header_size * i + 0x08, sf);
/* First step is always 0x500, not sure if it's a bug or a feature but the game just takes it as is and
* ends up reading 0 from out-of-bounds memory area which causes a pop at the start. Yikes.
* It gets clampled later so the rest of the sound plays ok.
* We put 89 here as our special index which contains 0 to simulate this.
*/
/* First step is always 0x500, not sure if it's a bug or a feature but the game just takes it as is and
* ends up reading 0 from out-of-bounds memory area which causes a pop at the start. Yikes.
* It gets clampled later so the rest of the sound plays ok.
* We put 89 here as our special index which contains 0 to simulate this.
*/
if (vgmstream->ch[i].adpcm_step_index == 0x500) {
vgmstream->ch[i].adpcm_step_index = 89;
}

View file

@ -326,12 +326,31 @@ VGMSTREAM* init_vgmstream_aifc(STREAMFILE* sf) {
* will become apparent.
* We shouldn't have a loop point that overflows an int32_t anyway. */
loop_flag = 1;
if (loop_start==loop_end)
if (loop_start == loop_end)
loop_flag = 0;
}
}
}
if (!loop_flag && mark_offset) {
int mark_count = read_u16be(mark_offset + 0x00,sf);
/* Relic has "beg loop" "end loop" comments but no actual looping? */
/* use "beg/end" loop comments [Battle Tryst (Arcade)]
* Relic codec has 3 "beg loop" "end loop" "start offset" comments, but always begin = 0 and end = -1 */
if (mark_count == 2) {
/* per mark:
* 00(2): id
* 02(4): sample point
* 06(1): string size
* --(-): string (non-null terminated)
* --(1): null terminator */
/* simplified... */
if (read_u32be(mark_offset + 0x09,sf) == 0x62656720 && /* "beg " */
read_u32be(mark_offset + 0x19,sf) == 0x656E6420) { /* "end " */
loop_start = read_s32be(mark_offset + 0x04, sf);
loop_end = read_s32be(mark_offset + 0x14, sf);
loop_flag = 1;
}
}
}

View file

@ -1,7 +1,7 @@
#include "meta.h"
#include "../coding/coding.h"
typedef enum { ADX, HCA, VAG, RIFF, CWAV, DSP } awb_type;
typedef enum { ADX, HCA, VAG, RIFF, CWAV, DSP, CWAC } awb_type;
static void load_awb_name(STREAMFILE* sf, STREAMFILE* sf_acb, VGMSTREAM* vgmstream, int waveid);
@ -116,6 +116,10 @@ VGMSTREAM* init_vgmstream_awb_memory(STREAMFILE* sf, STREAMFILE* sf_acb) {
type = DSP;
extension = "dsp";
}
else if (is_id32be(subfile_offset,sf, "CWAC")) { /* type 13 again */
type = CWAC;
extension = "dsp";
}
else {
VGM_LOG("AWB: unknown codec\n");
goto fail;
@ -150,6 +154,10 @@ VGMSTREAM* init_vgmstream_awb_memory(STREAMFILE* sf, STREAMFILE* sf_acb) {
vgmstream = init_vgmstream_ngc_dsp_std(temp_sf);
if (!vgmstream) goto fail;
break;
case CWAC: /* Mario & Sonic at the Rio 2016 Olympic Games (WiiU) */
vgmstream = init_vgmstream_dsp_cwac(temp_sf);
if (!vgmstream) goto fail;
break;
default:
goto fail;
}

View file

@ -10,7 +10,7 @@ typedef struct {
int total_subsongs;
int channel_count;
int channels;
int sample_rate;
int codec;
int num_samples;
@ -19,27 +19,30 @@ typedef struct {
off_t stream_offset;
size_t stream_size;
off_t vorbis_offset[VGMSTREAM_MAX_CHANNELS];
} awc_header;
static int parse_awc_header(STREAMFILE* streamFile, awc_header* awc);
static int parse_awc_header(STREAMFILE* sf, awc_header* awc);
static layered_layout_data* build_layered_awc(STREAMFILE* sf, awc_header* awc);
/* AWC - from RAGE (Rockstar Advanced Game Engine) audio [Red Dead Redemption, Max Payne 3, GTA5 (multi)] */
VGMSTREAM * init_vgmstream_awc(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
VGMSTREAM* init_vgmstream_awc(STREAMFILE* sf) {
VGMSTREAM* vgmstream = NULL;
awc_header awc = {0};
/* checks */
if (!check_extensions(streamFile,"awc"))
if (!check_extensions(sf,"awc"))
goto fail;
if (!parse_awc_header(streamFile, &awc))
if (!parse_awc_header(sf, &awc))
goto fail;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(awc.channel_count, 0);
vgmstream = allocate_vgmstream(awc.channels, 0);
if (!vgmstream) goto fail;
vgmstream->sample_rate = awc.sample_rate;
@ -77,15 +80,15 @@ VGMSTREAM * init_vgmstream_awc(STREAMFILE *streamFile) {
layered_layout_data * data = NULL;
/* init layout */
data = init_layout_layered(awc.channel_count);
data = init_layout_layered(awc.channels);
if (!data) goto fail;
vgmstream->layout_data = data;
vgmstream->layout_type = layout_layered;
vgmstream->coding_type = coding_FFmpeg;
/* open each layer subfile */
for (i = 0; i < awc.channel_count; i++) {
STREAMFILE* temp_streamFile;
for (i = 0; i < awc.channels; i++) {
STREAMFILE* temp_sf = NULL;
int layer_channels = 1;
/* build the layer VGMSTREAM */
@ -99,20 +102,20 @@ VGMSTREAM * init_vgmstream_awc(STREAMFILE *streamFile) {
data->layers[i]->num_samples = awc.num_samples;
/* setup custom IO streamfile, pass to FFmpeg and hope it's fooled */
temp_streamFile = setup_awc_xma_streamfile(streamFile, awc.stream_offset, awc.stream_size, awc.block_chunk, awc.channel_count, i);
if (!temp_streamFile) goto fail;
temp_sf = setup_awc_xma_streamfile(sf, awc.stream_offset, awc.stream_size, awc.block_chunk, awc.channels, i);
if (!temp_sf) goto fail;
substream_offset = 0; /* where FFmpeg thinks data starts, which our custom streamFile will clamp */
substream_size = get_streamfile_size(temp_streamFile); /* data of one XMA substream without blocks */
substream_offset = 0; /* where FFmpeg thinks data starts, which our custom sf will clamp */
substream_size = get_streamfile_size(temp_sf); /* data of one XMA substream without blocks */
block_size = 0x8000; /* no idea */
block_count = substream_size / block_size; /* not accurate but not needed */
bytes = ffmpeg_make_riff_xma2(buf, 0x100, awc.num_samples, substream_size, layer_channels, awc.sample_rate, block_count, block_size);
data->layers[i]->codec_data = init_ffmpeg_header_offset(temp_streamFile, buf,bytes, substream_offset,substream_size);
data->layers[i]->codec_data = init_ffmpeg_header_offset(temp_sf, buf,bytes, substream_offset,substream_size);
xma_fix_raw_samples(data->layers[i], temp_streamFile, substream_offset,substream_size, 0, 0,0); /* samples are ok? */
xma_fix_raw_samples(data->layers[i], temp_sf, substream_offset,substream_size, 0, 0,0); /* samples are ok? */
close_streamfile(temp_streamFile);
close_streamfile(temp_sf);
if (!data->layers[i]->codec_data) goto fail;
}
@ -125,13 +128,13 @@ VGMSTREAM * init_vgmstream_awc(STREAMFILE *streamFile) {
block_size = 0x8000; /* no idea */
block_count = awc.stream_size / block_size; /* not accurate but not needed */
bytes = ffmpeg_make_riff_xma2(buf, 0x100, awc.num_samples, awc.stream_size, awc.channel_count, awc.sample_rate, block_count, block_size);
vgmstream->codec_data = init_ffmpeg_header_offset(streamFile, buf,bytes, awc.stream_offset,awc.stream_size);
bytes = ffmpeg_make_riff_xma2(buf, 0x100, awc.num_samples, awc.stream_size, awc.channels, awc.sample_rate, block_count, block_size);
vgmstream->codec_data = init_ffmpeg_header_offset(sf, buf,bytes, awc.stream_offset,awc.stream_size);
if (!vgmstream->codec_data) goto fail;
vgmstream->coding_type = coding_FFmpeg;
vgmstream->layout_type = layout_none;
xma_fix_raw_samples(vgmstream, streamFile, awc.stream_offset,awc.stream_size, 0, 0,0); /* samples are ok? */
xma_fix_raw_samples(vgmstream, sf, awc.stream_offset,awc.stream_size, 0, 0,0); /* samples are ok? */
}
break;
@ -146,21 +149,43 @@ VGMSTREAM * init_vgmstream_awc(STREAMFILE *streamFile) {
cfg.chunk_size = awc.block_chunk;
cfg.big_endian = awc.big_endian;
vgmstream->codec_data = init_mpeg_custom(streamFile, awc.stream_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_AWC, &cfg);
vgmstream->codec_data = init_mpeg_custom(sf, awc.stream_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_AWC, &cfg);
if (!vgmstream->codec_data) goto fail;
vgmstream->layout_type = layout_none;
break;
}
#endif
#ifdef VGM_USE_VORBIS
case 0x08: { /* Vorbis (PC) [Red Dead Redemption 2 (PC)] */
if (awc.is_music) {
vgmstream->layout_data = build_layered_awc(sf, &awc);
if (!vgmstream->layout_data) goto fail;
vgmstream->layout_type = layout_layered;
vgmstream->coding_type = coding_FFmpeg;
}
else {
vorbis_custom_config cfg = {0};
cfg.channels = awc.channels;
cfg.sample_rate = awc.sample_rate;
cfg.header_offset = awc.vorbis_offset[0];
vgmstream->codec_data = init_vorbis_custom(sf, awc.stream_offset, VORBIS_AWC, &cfg);
if (!vgmstream->codec_data) goto fail;
vgmstream->layout_type = layout_none;
vgmstream->coding_type = coding_VORBIS_custom;
}
break;
}
#endif
default:
VGM_LOG("AWC: unknown codec 0x%02x\n", awc.codec);
goto fail;
}
if (!vgmstream_open_stream(vgmstream,streamFile,awc.stream_offset))
if (!vgmstream_open_stream(vgmstream, sf, awc.stream_offset))
goto fail;
return vgmstream;
@ -172,38 +197,38 @@ fail:
/* Parse Rockstar's AWC header (much info from LibertyV: https://github.com/koolkdev/libertyv).
* Made of entries for N streams, each with a number of tags pointing to chunks (header, data, events, etc). */
static int parse_awc_header(STREAMFILE* streamFile, awc_header* awc) {
int64_t (*read_64bit)(off_t,STREAMFILE*) = NULL;
int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL;
int16_t (*read_16bit)(off_t,STREAMFILE*) = NULL;
static int parse_awc_header(STREAMFILE* sf, awc_header* awc) {
uint64_t (*read_u64)(off_t,STREAMFILE*) = NULL;
uint32_t (*read_u32)(off_t,STREAMFILE*) = NULL;
uint16_t (*read_u16)(off_t,STREAMFILE*) = NULL;
int i, ch, entries;
uint32_t flags, info_header, tag_count = 0, tags_skip = 0;
off_t off;
int target_subsong = streamFile->stream_index;
off_t offset;
int target_subsong = sf->stream_index;
/* check header */
if (read_32bitBE(0x00,streamFile) != 0x41444154 && /* "ADAT" (LE) */
read_32bitBE(0x00,streamFile) != 0x54414441) /* "TADA" (BE) */
if (read_u32be(0x00,sf) != 0x41444154 && /* "ADAT" (LE) */
read_u32be(0x00,sf) != 0x54414441) /* "TADA" (BE) */
goto fail;
awc->big_endian = read_32bitBE(0x00,streamFile) == 0x54414441;
awc->big_endian = read_u32be(0x00,sf) == 0x54414441;
if (awc->big_endian) {
read_64bit = read_64bitBE;
read_32bit = read_32bitBE;
read_16bit = read_16bitBE;
read_u64 = read_u64be;
read_u32 = read_u32be;
read_u16 = read_u16be;
} else {
read_64bit = read_64bitLE;
read_32bit = read_32bitLE;
read_16bit = read_16bitLE;
read_u64 = read_u64le;
read_u32 = read_u32le;
read_u16 = read_u16le;
}
flags = read_32bit(0x04,streamFile);
entries = read_32bit(0x08,streamFile);
//header_size = read_32bit(0x0c,streamFile); /* after to stream id/tags, not including chunks */
flags = read_u32(0x04,sf);
entries = read_u32(0x08,sf);
//header_size = read_u32(0x0c,sf); /* after to stream id/tags, not including chunks */
off = 0x10;
offset = 0x10;
if ((flags & 0xFF00FFFF) != 0xFF000001 || (flags & 0x00F00000)) {
VGM_LOG("AWC: unknown flags 0x%08x\n", flags);
@ -211,7 +236,7 @@ static int parse_awc_header(STREAMFILE* streamFile, awc_header* awc) {
}
if (flags & 0x00010000) /* some kind of mini offset table */
off += 0x2 * entries;
offset += 0x2 * entries;
//if (flags % 0x00020000) /* seems to indicate chunks are not ordered (ie. header may go after data) */
// ...
//if (flags % 0x00040000) /* music/multichannel flag? (GTA5, not seen in RDR) */
@ -227,7 +252,7 @@ static int parse_awc_header(STREAMFILE* streamFile, awc_header* awc) {
/* Music when the first id is 0 (base/fake entry with info for all channels), sfx pack otherwise.
* sfx = N single streams, music = N-1 interleaved mono channels (even for MP3/XMA).
* Music seems layered (N-1/2 stereo pairs), maybe set with events? */
awc->is_music = (read_32bit(off + 0x00,streamFile) & 0x1FFFFFFF) == 0x00000000;
awc->is_music = (read_u32(offset + 0x00,sf) & 0x1FFFFFFF) == 0x00000000;
if (awc->is_music) { /* all streams except id 0 is a channel */
awc->total_subsongs = 1;
target_subsong = 1; /* we only need id 0, though channels may have its own tags/chunks */
@ -241,65 +266,69 @@ static int parse_awc_header(STREAMFILE* streamFile, awc_header* awc) {
/* get stream base info */
for (i = 0; i < entries; i++) {
info_header = read_32bit(off + 0x04*i, streamFile);
info_header = read_u32(offset + 0x04*i, sf);
tag_count = (info_header >> 29) & 0x7; /* 3b */
//id = (info_header >> 0) & 0x1FFFFFFF; /* 29b */
if (target_subsong-1 == i)
break;
tags_skip += tag_count; /* tags to skip to reach target's tags, in the next header */
}
off += 0x04*entries;
off += 0x08*tags_skip;
offset += 0x04*entries;
offset += 0x08*tags_skip;
/* get stream tags */
for (i = 0; i < tag_count; i++) {
uint64_t tag_header;
uint8_t tag;
size_t size;
off_t offset;
uint8_t tag_type;
size_t tag_size;
off_t tag_offset;
tag_header = (uint64_t)read_64bit(off + 0x08*i,streamFile);
tag = (uint8_t)((tag_header >> 56) & 0xFF); /* 8b */
size = (size_t)((tag_header >> 28) & 0x0FFFFFFF); /* 28b */
offset = (off_t)((tag_header >> 0) & 0x0FFFFFFF); /* 28b */
tag_header = read_u64(offset + 0x08*i,sf);
tag_type = (uint8_t)((tag_header >> 56) & 0xFF); /* 8b */
tag_size = (size_t)((tag_header >> 28) & 0x0FFFFFFF); /* 28b */
tag_offset = (off_t)((tag_header >> 0) & 0x0FFFFFFF); /* 28b */
;VGM_LOG("AWC: tag%i/%i at %lx: t=%x, o=%lx, s=%x\n", i, tag_count, offset + 0x08*i, tag_type, tag_offset, tag_size);
/* Tags are apparently part of a hash derived from a word ("data", "format", etc).
* If music + 1ch, the header and data chunks can repeat for no reason (sometimes not even pointed). */
switch(tag) {
switch(tag_type) {
case 0x55: /* data */
awc->stream_offset = offset;
awc->stream_size = size;
awc->stream_offset = tag_offset;
awc->stream_size = tag_size;
break;
case 0x48: /* music header */
if (!awc->is_music) {
VGM_LOG("AWC: music header found in sfx\n");
goto fail;
}
/* 0x00(32): unknown (some count?) */
awc->block_chunk = read_32bit(offset + 0x04,streamFile);
awc->channel_count = read_32bit(offset + 0x08,streamFile);
awc->block_chunk = read_u32(tag_offset + 0x04,sf);
awc->channels = read_u32(tag_offset + 0x08,sf);
if (awc->channel_count != entries - 1) { /* not counting id-0 */
if (awc->channels != entries - 1) { /* not counting id-0 */
VGM_LOG("AWC: number of music channels doesn't match entries\n");
goto fail;
}
for (ch = 0; ch < awc->channel_count; ch++) {
for (ch = 0; ch < awc->channels; ch++) {
int num_samples, sample_rate, codec;
/* 0x00(32): stream id (not always in the header entries order) */
/* 0x08(16): headroom?, 0x0d(8): round size?, 0x0e(16): unknown (zero?) */
num_samples = read_32bit(offset + 0x0c + 0x10*ch + 0x04,streamFile);
sample_rate = (uint16_t)read_16bit(offset + 0x0c + 0x10*ch + 0x0a,streamFile);
codec = read_8bit(offset + 0x0c + 0x10*ch + 0x0c, streamFile);
/* 0x00): stream id (not always in the header entries order) */
num_samples = read_u32(tag_offset + 0x0c + 0x10*ch + 0x04,sf);
/* 0x08: headroom */
sample_rate = read_u16(tag_offset + 0x0c + 0x10*ch + 0x0a,sf);
codec = read_u8(tag_offset + 0x0c + 0x10*ch + 0x0c,sf);
/* 0x0d(8): round size? */
/* 0x0e: unknown (zero/-1) */
/* validate channels differences */
if ((awc->num_samples && !(awc->num_samples >= num_samples - 10 && awc->num_samples <= num_samples + 10)) ||
(awc->sample_rate && awc->sample_rate != sample_rate)) {
VGM_LOG("AWC: found header diffs in channel %i, ns=%i vs %i, sr=%i vs %i\n",
ch, awc->num_samples, num_samples, awc->sample_rate, sample_rate);
/* sometimes (often cutscenes in Max Payne 3 and RDR DLC) channels have bif sample diffs,
/* sometimes (often cutscenes in Max Payne 3 and RDR DLC) channels have sample diffs,
* probably one stream is simply silent after its samples end */
}
@ -313,6 +342,7 @@ static int parse_awc_header(STREAMFILE* streamFile, awc_header* awc) {
awc->sample_rate = sample_rate;
awc->codec = codec;
}
break;
case 0xFA: /* sfx header */
@ -320,16 +350,39 @@ static int parse_awc_header(STREAMFILE* streamFile, awc_header* awc) {
VGM_LOG("AWC: sfx header found in music\n");
goto fail;
}
/* 0x04(32): -1?, 0x0a(16x4): unknown x4, 0x12: null? */
awc->num_samples = read_32bit(offset + 0x00,streamFile);
awc->sample_rate = (uint16_t)read_16bit(offset + 0x08,streamFile);
awc->codec = read_8bit(offset + 0x13, streamFile);
awc->channel_count = 1;
awc->num_samples = read_u32(tag_offset + 0x00,sf);
/* 0x04: -1? */
awc->sample_rate = read_u16(tag_offset + 0x08,sf);
/* 0x0a: unknown x4 */
/* 0x12: null? */
awc->codec = read_u8(tag_offset + 0x13, sf);
awc->channels = 1;
break;
case 0x76: /* sfx header for vorbis */
if (awc->is_music) {
VGM_LOG("AWC: sfx header found in music\n");
goto fail;
}
awc->num_samples = read_u32(tag_offset + 0x00,sf);
/* 0x04: -1? */
awc->sample_rate = read_u16(tag_offset + 0x08,sf);
/* 0x0a: granule start? (negative) */
/* 0x0c: granule max? */
/* 0x10: unknown */
awc->codec = read_u8(tag_offset + 0x1c, sf); /* 16b? */
/* 0x1e: vorbis header size */
awc->channels = 1;
awc->vorbis_offset[0] = tag_offset + 0x20;
break;
case 0xA3: /* block-to-sample table (32b x number of blocks w/ num_samples at the start of each block) */
case 0xBD: /* events (32bx4): type_hash, params_hash, timestamp_ms, flags */
default: /* 0x5C=animation/RSC?, 0x68=midi?, 0x36/0x2B/0x5A/0xD9=? */
case 0x5C: /* animation/RSC config? */
default: /* 0x68=midi?, 0x36=hash thing?, 0x2B=sizes, 0x5A/0xD9=? */
//VGM_LOG("AWC: ignoring unknown tag 0x%02x\n", tag);
break;
}
@ -340,11 +393,27 @@ static int parse_awc_header(STREAMFILE* streamFile, awc_header* awc) {
goto fail;
}
/* If music, data is divided into blocks of block_chunk size with padding.
/* vorbis offset table, somehow offsets are unordered and can go before tags */
if (awc->is_music && awc->codec == 0x08) {
offset += 0x08 * tag_count;
for (ch = 0; ch < awc->channels; ch++) {
awc->vorbis_offset[ch] = read_u16(offset + 0x08*ch + 0x00, sf);
/* 0x02: always 0xB000? */
/* 0x04: always 0x00CD? */
/* 0x06: always 0x7F00? */
}
}
/* In music mode, data is divided into blocks of block_chunk size with padding.
* Each block has a header/seek table and interleaved data for all channels */
if (awc->is_music && read_32bit(awc->stream_offset, streamFile) != 0) {
VGM_LOG("AWC: music found, but block doesn't start with seek table at %x\n", (uint32_t)awc->stream_offset);
goto fail;
{
int32_t seek_start = read_u32(awc->stream_offset, sf); /* -1 in later (RDR2) versions */
if (awc->is_music && !(seek_start == 0 || seek_start == -1)) {
VGM_LOG("AWC: music found, but block doesn't start with seek table at %x\n", (uint32_t)awc->stream_offset);
goto fail;
}
}
@ -352,3 +421,164 @@ static int parse_awc_header(STREAMFILE* streamFile, awc_header* awc) {
fail:
return 0;
}
/* ************************************************************************* */
//TODO: this method won't work properly, needs internal handling of blocks.
//
// This setups a decoder per block, but seems Vorbis' uses first frame as setup so it
// returns samples (576 vs 1024), making num_samples count in each block being off + causing
// gaps. So they must be using a single encoder + setting decode_to_discard per block
// to ge the thing working.
//
// However since blocks are probably also used for seeking, maybe they aren't resetting
// the decoder when seeking? or they force first frame to be 1024?
//
// In case of Vorvis, when setting skip samples seems repeated data from last block is
// exactly last 0x800 bytes of that channel.
static VGMSTREAM* build_block_vgmstream(STREAMFILE* sf, awc_header* awc, int channel, int32_t num_samples, int32_t skip_samples, off_t block_start, size_t block_size) {
STREAMFILE* temp_sf = NULL;
VGMSTREAM* vgmstream = NULL;
int block_channels = 1;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(block_channels, 0);
if (!vgmstream) goto fail;
vgmstream->sample_rate = awc->sample_rate;
vgmstream->num_samples = num_samples - skip_samples;
vgmstream->stream_size = block_size;
vgmstream->meta_type = meta_AWC;
switch(awc->codec) {
#ifdef VGM_USE_VORBIS
case 0x08: { /* Vorbis (PC) [Red Dead Redemption 2 (PC)] */
vorbis_custom_config cfg = {0};
cfg.channels = 1;
cfg.sample_rate = awc->sample_rate;
cfg.header_offset = awc->vorbis_offset[channel];
//cfg.skip_samples = skip_samples; //todo
vgmstream->codec_data = init_vorbis_custom(sf, block_start, VORBIS_AWC, &cfg);
if (!vgmstream->codec_data) goto fail;
vgmstream->layout_type = layout_none;
vgmstream->coding_type = coding_VORBIS_custom;
}
break;
#endif
default:
goto fail;
}
if (!vgmstream_open_stream(vgmstream, sf, block_start))
goto fail;
close_streamfile(temp_sf);
return vgmstream;
fail:
close_streamfile(temp_sf);
close_vgmstream(vgmstream);
return NULL;
}
static VGMSTREAM* build_blocks_vgmstream(STREAMFILE* sf, awc_header* awc, int channel) {
VGMSTREAM* vgmstream = NULL;
segmented_layout_data* data = NULL;
int i, ch;
int blocks = awc->stream_size / awc->block_chunk + (awc->stream_size % awc->block_chunk ? 1 : 0) ;
/* init layout */
data = init_layout_segmented(blocks);
if (!data) goto fail;
/* one segment per block of this channel */
for (i = 0; i < blocks; i++) {
off_t block_offset = awc->stream_offset + i * awc->block_chunk;
int32_t num_samples = 0, skip_samples = 0;
uint32_t header_skip = 0, block_skip = 0, block_start = 0, block_data = 0;
/* read stupid block crap to get proper offsets and whatnot, format:
* - per channel: number of channel entries + skip samples + num samples
* - per channel: seek table with N entries */
for (ch = 0; ch < awc->channels; ch++) {
/* 0x00: -1 */
int entries = read_u32le(block_offset + 0x18 * ch + 0x04, sf);
int32_t entry_skip = read_u32le(block_offset + 0x18 * ch + 0x08, sf);
int32_t entry_samples = read_u32le(block_offset + 0x18 * ch + 0x0c, sf);
if (ch == channel) {
num_samples = entry_samples;
skip_samples = entry_skip;
block_start = block_offset + block_skip;
block_data = entries * 0x800;
}
header_skip += 0x18 + entries * 0x04;
block_skip += entries * 0x800;
}
if (!block_start)
goto fail;
header_skip = align_size_to_block(header_skip, 0x800);
block_start += header_skip;
//;VGM_LOG("AWC: build ch%i, block=%i at %lx, o=%x, s=%x, ns=%i, ss=%i\n", channel, i, block_offset, block_start, block_data, num_samples, skip_samples);
data->segments[i] = build_block_vgmstream(sf, awc, channel, num_samples, skip_samples, block_start, block_data);
if (!data->segments[i]) goto fail;
}
/* setup VGMSTREAMs */
if (!setup_layout_segmented(data))
goto fail;
/* build the layout VGMSTREAM */
vgmstream = allocate_segmented_vgmstream(data, 0, 0, 0);
if (!vgmstream) goto fail;
return vgmstream;
fail:
close_vgmstream(vgmstream);
if (!vgmstream)
free_layout_segmented(data);
return NULL;
}
/* ************************************************************************* */
/* Make layers per channel for AWC's abhorrent blocks.
*
* File has N channels = N streams, that use their own mono decoder.
* Each block then has header + seek table for all channels. But in each block there is
* a "skip samples" value per channel, and blocks repeat some data from last block
* for this, so PCM must be discarded. Also, channels in a block don't need to have
* the same number of samples.
*/
static layered_layout_data* build_layered_awc(STREAMFILE* sf, awc_header* awc) {
int i;
layered_layout_data* data = NULL;
/* init layout */
data = init_layout_layered(awc->channels);
if (!data) goto fail;
/* open each layer subfile */
for (i = 0; i < awc->channels; i++) {
data->layers[i] = build_blocks_vgmstream(sf, awc, i);
if (!data->layers[i]) goto fail;
}
/* setup layered VGMSTREAMs */
if (!setup_layout_layered(data))
goto fail;
return data;
fail:
free_layout_layered(data);
return NULL;
}

View file

@ -1,44 +1,43 @@
#include "meta.h"
/* BMP - from Jubeat series (AC) */
VGMSTREAM * init_vgmstream_bmp_konami(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
/* BMP - from Konami arcade games [drummania (AC), GITADORA (AC), Jubeat (AC)] */
VGMSTREAM* init_vgmstream_bmp_konami(STREAMFILE* sf) {
VGMSTREAM* vgmstream = NULL;
off_t start_offset;
int loop_flag, channel_count;
int loop_flag, channels;
/* checks */
/* .bin: actual extension
* .lbin: for plugins */
if (!check_extensions(streamFile, "bin,lbin"))
if (!check_extensions(sf, "bin,lbin"))
goto fail;
if (read_32bitBE(0x00,streamFile) != 0x424D5000) /* "BMP\0" "*/
if (!is_id32be(0x00,sf, "BMP\0"))
goto fail;
channel_count = read_8bit(0x10,streamFile); /* assumed */
if (channel_count != 2) goto fail;
channels = read_u8(0x10,sf); /* assumed */
if (channels != 2) goto fail;
loop_flag = 0;
start_offset = 0x20;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count, loop_flag);
vgmstream = allocate_vgmstream(channels, loop_flag);
if (!vgmstream) goto fail;
vgmstream->meta_type = meta_BMP_KONAMI;
vgmstream->num_samples = read_32bitBE(0x04,streamFile);
vgmstream->sample_rate = read_32bitBE(0x14, streamFile);
vgmstream->num_samples = read_u32be(0x04,sf);
vgmstream->sample_rate = read_u32be(0x14, sf);
vgmstream->coding_type = coding_OKI4S;
vgmstream->layout_type = layout_none;
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
if (!vgmstream_open_stream(vgmstream, sf, start_offset))
goto fail;
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;

View file

@ -806,29 +806,31 @@ fail:
/* EA Harmony Sample Bank - used in 8th gen EA Sports games */
VGMSTREAM * init_vgmstream_ea_sbr_harmony(STREAMFILE *sf) {
uint32_t num_dsets, set_sounds, chunk_id, data_offset, table_offset, dset_offset, base_offset, sound_table_offset, sound_offset;
uint32_t i, j;
uint64_t set_sounds, base_offset, sound_offset;
uint32_t chunk_id, data_offset, table_offset, dset_offset, sound_table_offset;
uint16_t num_dsets;
uint8_t set_type, flag, offset_size;
uint32_t i, j;
char sound_name[STREAM_NAME_SIZE];
STREAMFILE *sbsFile = NULL, *sf_data = NULL;
STREAMFILE *sf_sbs = NULL, *sf_data = NULL;
VGMSTREAM *vgmstream = NULL;
int target_stream = sf->stream_index, total_sounds, local_target, is_streamed = 0;
uint64_t(*read_u64)(off_t, STREAMFILE *);
uint32_t(*read_u32)(off_t, STREAMFILE*);
uint16_t(*read_u16)(off_t, STREAMFILE*);
if (!check_extensions(sf, "sbr"))
goto fail;
/* Logically, big endian version starts with SBbe. However, this format is
* only used on 8th gen systems so far so big endian version probably doesn't exist. */
if (read_32bitBE(0x00, sf) == 0x53426C65) { /* "SBle" */
/* check header */
if (read_u32be(0x00, sf) == 0x53426C65) { /* "SBle" */
read_u64 = read_u64le;
read_u32 = read_u32le;
read_u16 = read_u16le;
#if 0
} else if (read_32bitBE(0x00, sf) == 0x53426265) { /* "SBbe" */
read_32bit = read_u32be;
read_16bit = read_u16be;
#endif
} else if (read_u32be(0x00, sf) == 0x53426265) { /* "SBbe" */
read_u64 = read_u64be;
read_u32 = read_u32be;
read_u16 = read_u16be;
} else {
goto fail;
}
@ -880,13 +882,13 @@ VGMSTREAM * init_vgmstream_ea_sbr_harmony(STREAMFILE *sf) {
if (local_target < 0 || local_target > 0)
continue;
sound_offset = read_u32(dset_offset + 0x08, sf);
sound_offset = read_u64(dset_offset + 0x08, sf);
} else if (set_type == 0x01) {
total_sounds += 2;
if (local_target < 0 || local_target > 1)
continue;
base_offset = read_u32(dset_offset + 0x08, sf);
base_offset = read_u64(dset_offset + 0x08, sf);
if (local_target == 0) {
sound_offset = base_offset;
@ -894,9 +896,9 @@ VGMSTREAM * init_vgmstream_ea_sbr_harmony(STREAMFILE *sf) {
sound_offset = base_offset + read_u16(dset_offset + 0x06, sf);
}
} else if (set_type == 0x02) {
flag = read_u8(dset_offset + 0x06, sf);
offset_size = read_u8(dset_offset + 0x07, sf);
base_offset = read_u32(dset_offset + 0x08, sf);
flag = (read_u16(dset_offset + 0x06, sf) >> 0) & 0xFF;
offset_size = (read_u16(dset_offset + 0x06, sf) >> 8) & 0xFF;
base_offset = read_u64(dset_offset + 0x08, sf);
sound_table_offset = read_u32(dset_offset + 0x10, sf);
total_sounds += set_sounds;
@ -911,12 +913,14 @@ VGMSTREAM * init_vgmstream_ea_sbr_harmony(STREAMFILE *sf) {
for (j = 0; j < flag; j++) sound_offset *= 2;
} else if (offset_size == 0x04) {
sound_offset = read_u32(sound_table_offset + 0x04 * local_target, sf);
} else {
goto fail;
}
sound_offset += base_offset;
} else if (set_type == 0x03) {
offset_size = read_u8(dset_offset + 0x07, sf);
set_sounds = read_u32(dset_offset + 0x08, sf);
offset_size = (read_u16(dset_offset + 0x06, sf) >> 8) & 0xFF;
set_sounds = read_u64(dset_offset + 0x08, sf);
sound_table_offset = read_u32(dset_offset + 0x10, sf);
total_sounds += set_sounds;
@ -929,6 +933,8 @@ VGMSTREAM * init_vgmstream_ea_sbr_harmony(STREAMFILE *sf) {
sound_offset = read_u16(sound_table_offset + 0x02 * local_target, sf);
} else if (offset_size == 0x04) {
sound_offset = read_u32(sound_table_offset + 0x04 * local_target, sf);
} else {
goto fail;
}
} else if (set_type == 0x04) {
total_sounds += set_sounds;
@ -955,23 +961,25 @@ VGMSTREAM * init_vgmstream_ea_sbr_harmony(STREAMFILE *sf) {
if (!is_streamed) {
/* RAM asset */
if (read_32bitBE(data_offset, sf) != 0x64617461) /* "data" */
if (read_u32be(data_offset, sf) != 0x64617461 && /* "data" */
read_u32be(data_offset, sf) != 0x44415441) /* "DATA" */
goto fail;
sf_data = sf;
sound_offset += data_offset;
} else {
/* streamed asset */
sbsFile = open_streamfile_by_ext(sf, "sbs");
if (!sbsFile)
sf_sbs = open_streamfile_by_ext(sf, "sbs");
if (!sf_sbs)
goto fail;
if (read_32bitBE(0x00, sbsFile) != 0x64617461) /* "data" */
if (read_u32be(0x00, sf_sbs) != 0x64617461 && /* "data" */
read_u32be(0x00, sf_sbs) != 0x44415441) /* "DATA" */
goto fail;
sf_data = sbsFile;
sf_data = sf_sbs;
if (read_32bitBE(sound_offset, sf_data) == 0x736C6F74) {
if (read_u32be(sound_offset, sf_data) == 0x736C6F74) {
/* skip "slot" section */
sound_offset += 0x30;
}
@ -983,11 +991,11 @@ VGMSTREAM * init_vgmstream_ea_sbr_harmony(STREAMFILE *sf) {
vgmstream->num_streams = total_sounds;
strncpy(vgmstream->stream_name, sound_name, STREAM_NAME_SIZE);
close_streamfile(sbsFile);
close_streamfile(sf_sbs);
return vgmstream;
fail:
close_streamfile(sbsFile);
close_streamfile(sf_sbs);
return NULL;
}

View file

@ -6,7 +6,20 @@
static int read_pos_file(uint8_t* buf, size_t bufsize, STREAMFILE* sf);
static int find_ogg_loops(ffmpeg_codec_data* data, int32_t* p_loop_start, int32_t* p_loop_end);
/* parses any file supported by FFmpeg and not handled elsewhere (mainly: MP4/AAC, MP3, MPC, FLAC) */
/* parses any format supported by FFmpeg and not handled elsewhere:
* - MP3 (.mp3, .mus): Marc Ecko's Getting Up (PC)
* - MPC (.mpc): Moonshine Runners (PC), Asphalt 7 (PC)
* - FLAC (.flac): Warcraft 3 Reforged (PC), Call of Duty: Ghosts (PC)
* - DUCK (.wav): Sonic Jam (SAT), Virtua Fighter 2 (SAT)
* - ALAC/AAC (.caf): Chrono Trigger (iOS)
* - ATRAC3 (.oma, .aa3): demuxed PSP/PS3 videos
* - WMA/WMAPRO (.wma): Castlevania Symphony of the Night (Xbox)
* - AC3 (.ac3): some PS2 games
*
* May also catch files that are supported elsewhere but rejected due to bugs,
* but those should be fixed in their parser for proper loops/etc support
* (catch-all behavior may be disabled later).
*/
VGMSTREAM* init_vgmstream_ffmpeg(STREAMFILE* sf) {
VGMSTREAM* vgmstream = NULL;
ffmpeg_codec_data* data = NULL;

View file

@ -382,6 +382,9 @@ static const hcakey_info hcakey_list[] = {
/* Readyyy! (Android) */
{1234567890987654321}, // 112210F4B16C1CB1
/* HoneyWorks Premium Live (Android) */
{20200401000000}, // 0000125F45B9D640
/* Dragalia Lost (iOS/Android) */
{2967411924141, subkeys_dgl, sizeof(subkeys_dgl) / sizeof(subkeys_dgl[0]) }, // 000002B2E7889CAD

View file

@ -0,0 +1,67 @@
#include "meta.h"
#include "../coding/coding.h"
/* .ifs - Konami arcade games container [drummania (AC), GITADORA (AC)] */
VGMSTREAM* init_vgmstream_ifs(STREAMFILE* sf) {
VGMSTREAM* vgmstream = NULL;
STREAMFILE* temp_sf = NULL;
off_t subfile_offset, subfile_size;
int total_subsongs, target_subsong = sf->stream_index;
/* checks */
if (!check_extensions(sf, "ifs"))
goto fail;
if (read_u32be(0x00,sf) != 0x6CAD8F89)
goto fail;
if (read_u16be(0x04,sf) != 0x0003)
goto fail;
/* .ifs format is a binary XML thing with types/fields/nodes/etc, that sometimes
* contains Konami's BMP as subsongs (may differ in num_samples). This is an
* abridged version of the whole thing, see:
* - https://github.com/mon/ifstools
* - https://github.com/mon/kbinxml
* - https://bitbucket.org/ahigerd/gitadora2wav/
*/
{
off_t offset, size, subfile_start;
subfile_start = read_u32be(0x10,sf);
/* skip root section and point to childs */
offset = 0x28 + 0x04 + read_u32be(0x28,sf);
size = read_u32be(offset + 0x00,sf);
offset += 0x04;
/* point to subfile offsets */
size = size - 0x04 - read_u32be(offset + 0x00,sf) - 0x04;
offset = offset + 0x04 + read_u32be(offset + 0x00,sf) + 0x04;
total_subsongs = size / 0x0c;
if (target_subsong == 0) target_subsong = 1;
if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail;
subfile_offset = read_u32be(offset + (target_subsong-1)*0x0c + 0x00,sf) + subfile_start;
subfile_size = read_u32be(offset + (target_subsong-1)*0x0c + 0x04,sf);
}
temp_sf = setup_subfile_streamfile(sf, subfile_offset, subfile_size, "bin");
if (!temp_sf) goto fail;
vgmstream = init_vgmstream_bmp_konami(temp_sf);
if (!vgmstream) goto fail;
vgmstream->num_streams = total_subsongs;
/* subsongs have names but are packed in six-bit strings */
close_streamfile(temp_sf);
return vgmstream;
fail:
close_streamfile(temp_sf);
close_vgmstream(vgmstream);
return NULL;
}

View file

@ -42,7 +42,7 @@ VGMSTREAM* init_vgmstream_wii_was(STREAMFILE* sf);
VGMSTREAM* init_vgmstream_dsp_str_ig(STREAMFILE* sf);
VGMSTREAM* init_vgmstream_dsp_xiii(STREAMFILE* sf);
VGMSTREAM* init_vgmstream_dsp_cabelas(STREAMFILE* sf);
VGMSTREAM* init_vgmstream_wii_ndp(STREAMFILE* sf);
VGMSTREAM* init_vgmstream_dsp_ndp(STREAMFILE* sf);
VGMSTREAM* init_vgmstream_ngc_dsp_aaap(STREAMFILE* sf);
VGMSTREAM* init_vgmstream_dsp_dspw(STREAMFILE* sf);
VGMSTREAM* init_vgmstream_ngc_dsp_iadp(STREAMFILE* sf);
@ -56,6 +56,8 @@ VGMSTREAM* init_vgmstream_dsp_ds2(STREAMFILE* sf);
VGMSTREAM* init_vgmstream_dsp_itl(STREAMFILE* sf);
VGMSTREAM* init_vgmstream_dsp_sqex(STREAMFILE* sf);
VGMSTREAM* init_vgmstream_dsp_wiivoice(STREAMFILE* sf);
VGMSTREAM* init_vgmstream_dsp_wiiadpcm(STREAMFILE* sf);
VGMSTREAM* init_vgmstream_dsp_cwac(STREAMFILE* sf);
VGMSTREAM * init_vgmstream_csmp(STREAMFILE *streamFile);
@ -928,4 +930,8 @@ VGMSTREAM* init_vgmstream_wady(STREAMFILE* sf);
VGMSTREAM* init_vgmstream_cpk(STREAMFILE* sf);
VGMSTREAM* init_vgmstream_cpk_memory(STREAMFILE* sf, STREAMFILE* sf_acb);
VGMSTREAM *init_vgmstream_sbk(STREAMFILE *sf);
VGMSTREAM* init_vgmstream_ifs(STREAMFILE* sf);
#endif /*_META_H*/

View file

@ -1,51 +1,56 @@
#include "meta.h"
#include "../coding/coding.h"
/* .NAAC - from Namco 3DS games (Ace Combat - Assault Horizon Legacy, Taiko no Tatsujin Don to Katsu no Jikuu Daibouken) */
VGMSTREAM* init_vgmstream_naac(STREAMFILE* sf) {
VGMSTREAM* vgmstream = NULL;
off_t start_offset;
int loop_flag, channel_count;
int loop_flag, channels;
size_t data_size;
/* check extension */
if ( !check_extensions(sf,"naac") )
/* checks */
if (!check_extensions(sf,"naac"))
goto fail;
/* check header */
if (read_32bitBE(0x00,sf) != 0x41414320) /* "AAC " */
if (read_u32be(0x00,sf) != 0x41414320) /* "AAC " */
goto fail;
if (read_32bitLE(0x04,sf) != 0x01) /* version? */
if (read_u32le(0x04,sf) != 0x01) /* version? */
goto fail;
start_offset = 0x1000;
loop_flag = (read_32bitLE(0x18,sf) != 0);
channel_count = read_32bitLE(0x08,sf);
loop_flag = (read_s32le(0x18,sf) != 0);
channels = read_s32le(0x08,sf);
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
vgmstream = allocate_vgmstream(channels, loop_flag);
if (!vgmstream) goto fail;
vgmstream->sample_rate = read_32bitLE(0x0c,sf);
vgmstream->num_samples = read_32bitLE(0x10,sf); /* without skip_samples */
vgmstream->loop_start_sample = read_32bitLE(0x14,sf); /* with skip_samples */
vgmstream->loop_end_sample = read_32bitLE(0x18,sf);
/* 0x1c: loop start offset, 0x20: loop end offset (within data) */
data_size = read_32bitLE(0x24,sf);
/* 0x28: unknown; 0x2c: table start offset?; 0x30: seek table (always 0xFD0, padded) */
vgmstream->sample_rate = read_s32le(0x0c,sf);
vgmstream->num_samples = read_s32le(0x10,sf); /* without skip_samples */
vgmstream->loop_start_sample = read_s32le(0x14,sf); /* with skip_samples */
vgmstream->loop_end_sample = read_s32le(0x18,sf) + 1; /* without skip_samples */
/* 0x1c: loop start offset */
/* 0x20: loop end offset (within data) */
data_size = read_u32le(0x24,sf);
/* 0x28: unknown */
/* 0x2c: table start offset? */
/* 0x30: seek table (always 0xFD0, padded) */
vgmstream->meta_type = meta_NAAC;
#ifdef VGM_USE_FFMPEG
{
vgmstream->codec_data = init_ffmpeg_offset(sf, start_offset,data_size);
vgmstream->codec_data = init_ffmpeg_offset(sf, start_offset, data_size);
if (!vgmstream->codec_data) goto fail;
vgmstream->coding_type = coding_FFmpeg;
vgmstream->layout_type = layout_none;
/* observed default, some files start without silence though seems correct when loop_start=0 */
ffmpeg_set_skip_samples(vgmstream->codec_data, 1024); /* raw AAC doesn't set this */
vgmstream->num_samples -= 1024;
vgmstream->num_samples -= 1024; /* may end with 1024 of silence? */
vgmstream->loop_end_sample -= 1024;
}
#else
goto fail;

View file

@ -7,19 +7,19 @@
* then this is actually how they are laid out in the file, albeit big-endian */
struct dsp_header {
uint32_t sample_count; /* 0x00 */
uint32_t nibble_count; /* 0x04 */
uint32_t sample_rate; /* 0x08 */
uint32_t nibble_count; /* 0x04 (includes frame headers) */
uint32_t sample_rate; /* 0x08 (generally 32/48kz but games like Wario World set 32028hz to adjust for GC's rate) */
uint16_t loop_flag; /* 0x0c */
uint16_t format; /* 0x0e */
uint16_t format; /* 0x0e (always 0 for ADPCM) */
uint32_t loop_start_offset; /* 0x10 */
uint32_t loop_end_offset; /* 0x14 */
uint32_t ca; /* 0x18 */
int16_t coef[16]; /* 0x1c (really 8x2) */
uint16_t gain; /* 0x3c */
uint16_t initial_ps; /* 0x3e */
uint32_t ca; /* 0x18 (always 0) */
int16_t coef[16]; /* 0x1c (eight pairs) */
uint16_t gain; /* 0x3c (always 0 for ADPCM) */
uint16_t initial_ps; /* 0x3e (predictor/scale in frame header) */
int16_t initial_hist1; /* 0x40 */
int16_t initial_hist2; /* 0x42 */
uint16_t loop_ps; /* 0x44 */
uint16_t loop_ps; /* 0x44 (predictor/scale in loop frame header) */
int16_t loop_hist1; /* 0x46 */
int16_t loop_hist2; /* 0x48 */
int16_t channel_count; /* 0x4a (DSPADPCM.exe ~v2.7 extension) */
@ -29,34 +29,35 @@ struct dsp_header {
/* read the above struct; returns nonzero on failure */
static int read_dsp_header_endian(struct dsp_header *header, off_t offset, STREAMFILE* sf, int big_endian) {
int32_t (*get_32bit)(const uint8_t *) = big_endian ? get_32bitBE : get_32bitLE;
int16_t (*get_16bit)(const uint8_t *) = big_endian ? get_16bitBE : get_16bitLE;
uint32_t (*get_u32)(const uint8_t*) = big_endian ? get_u32be : get_u32le;
uint16_t (*get_u16)(const uint8_t*) = big_endian ? get_u16be : get_u16le;
int16_t (*get_s16)(const uint8_t*) = big_endian ? get_s16be : get_s16le;
int i;
uint8_t buf[0x4e];
uint8_t buf[0x60];
if (offset > get_streamfile_size(sf))
return 1;
if (read_streamfile(buf, offset, 0x4e, sf) != 0x4e)
if (read_streamfile(buf, offset, 0x60, sf) != 0x60)
return 1;
header->sample_count = get_32bit(buf+0x00);
header->nibble_count = get_32bit(buf+0x04);
header->sample_rate = get_32bit(buf+0x08);
header->loop_flag = get_16bit(buf+0x0c);
header->format = get_16bit(buf+0x0e);
header->loop_start_offset = get_32bit(buf+0x10);
header->loop_end_offset = get_32bit(buf+0x14);
header->ca = get_32bit(buf+0x18);
header->sample_count = get_u32(buf+0x00);
header->nibble_count = get_u32(buf+0x04);
header->sample_rate = get_u32(buf+0x08);
header->loop_flag = get_u16(buf+0x0c);
header->format = get_u16(buf+0x0e);
header->loop_start_offset = get_u32(buf+0x10);
header->loop_end_offset = get_u32(buf+0x14);
header->ca = get_u32(buf+0x18);
for (i=0; i < 16; i++)
header->coef[i] = get_16bit(buf+0x1c+i*0x02);
header->gain = get_16bit(buf+0x3c);
header->initial_ps = get_16bit(buf+0x3e);
header->initial_hist1 = get_16bit(buf+0x40);
header->initial_hist2 = get_16bit(buf+0x42);
header->loop_ps = get_16bit(buf+0x44);
header->loop_hist1 = get_16bit(buf+0x46);
header->loop_hist2 = get_16bit(buf+0x48);
header->channel_count = get_16bit(buf+0x4a);
header->block_size = get_16bit(buf+0x4c);
header->coef[i] = get_s16(buf+0x1c+i*0x02);
header->gain = get_u16(buf+0x3c);
header->initial_ps = get_u16(buf+0x3e);
header->initial_hist1 = get_s16(buf+0x40);
header->initial_hist2 = get_s16(buf+0x42);
header->loop_ps = get_u16(buf+0x44);
header->loop_hist1 = get_s16(buf+0x46);
header->loop_hist2 = get_s16(buf+0x48);
header->channel_count = get_s16(buf+0x4a);
header->block_size = get_s16(buf+0x4c);
return 0;
}
static int read_dsp_header(struct dsp_header *header, off_t offset, STREAMFILE* file) {
@ -91,6 +92,7 @@ typedef struct {
int fix_loop_start; /* weird files with bad loop start */
int single_header; /* all channels share header, thus totals are off */
int ignore_header_agreement; /* sometimes there are minor differences between headers */
int ignore_loop_ps; /* sometimes has bad loop start ps */
} dsp_meta;
#define COMMON_DSP_MAX_CHANNELS 6
@ -113,8 +115,10 @@ static VGMSTREAM* init_vgmstream_dsp_common(STREAMFILE* sf, dsp_meta *dspm) {
/* load standard DSP header per channel */
{
for (i = 0; i < dspm->channel_count; i++) {
if (read_dsp_header_endian(&ch_header[i], dspm->header_offset + i*dspm->header_spacing, sf, !dspm->little_endian))
if (read_dsp_header_endian(&ch_header[i], dspm->header_offset + i*dspm->header_spacing, sf, !dspm->little_endian)) {
//;VGM_LOG("DSP: bad header\n");
goto fail;
}
}
}
@ -129,8 +133,10 @@ static VGMSTREAM* init_vgmstream_dsp_common(STREAMFILE* sf, dsp_meta *dspm) {
/* check type==0 and gain==0 */
{
for (i = 0; i < dspm->channel_count; i++) {
if (ch_header[i].format || ch_header[i].gain)
if (ch_header[i].format || ch_header[i].gain) {
//;VGM_LOG("DSP: bad type/gain\n");
goto fail;
}
}
}
@ -156,26 +162,31 @@ static VGMSTREAM* init_vgmstream_dsp_common(STREAMFILE* sf, dsp_meta *dspm) {
for (i = 0; i < channels; i++) {
off_t channel_offset = dspm->start_offset + i*dspm->interleave;
if (ch_header[i].initial_ps != (uint8_t)read_8bit(channel_offset, sf))
if (ch_header[i].initial_ps != read_u8(channel_offset, sf)) {
//;VGM_LOG("DSP: bad initial ps\n");
goto fail;
}
}
}
/* check expected loop predictor/scale */
if (ch_header[0].loop_flag) {
if (ch_header[0].loop_flag && !dspm->ignore_loop_ps) {
int channels = dspm->channel_count;
if (dspm->single_header)
channels = 1;
for (i = 0; i < channels; i++) {
off_t loop_offset = ch_header[i].loop_start_offset;
loop_offset = loop_offset / 0x8 * 0x8; /* loop points to a nibble, but we need closest frame header */
if (dspm->interleave) {
loop_offset = loop_offset / 16 * 8;
loop_offset = (loop_offset / dspm->interleave * dspm->interleave * channels) + (loop_offset % dspm->interleave);
}
if (ch_header[i].loop_ps != (uint8_t)read_8bit(dspm->start_offset + i*dspm->interleave + loop_offset,sf))
if (ch_header[i].loop_ps != read_u8(dspm->start_offset + i*dspm->interleave + loop_offset,sf)) {
//;VGM_LOG("DSP: bad loop ps: %x vs at %lx\n", ch_header[i].loop_ps, dspm->start_offset + i*dspm->interleave + loop_offset);
goto fail;
}
}
}
@ -199,7 +210,7 @@ static VGMSTREAM* init_vgmstream_dsp_common(STREAMFILE* sf, dsp_meta *dspm) {
vgmstream->sample_rate = ch_header[0].sample_rate;
vgmstream->num_samples = ch_header[0].sample_count;
vgmstream->loop_start_sample = dsp_nibbles_to_samples(ch_header[0].loop_start_offset);
vgmstream->loop_end_sample = dsp_nibbles_to_samples(ch_header[0].loop_end_offset)+1;
vgmstream->loop_end_sample = dsp_nibbles_to_samples(ch_header[0].loop_end_offset) + 1;
vgmstream->meta_type = dspm->meta_type;
vgmstream->coding_type = coding_NGC_DSP;
@ -224,7 +235,7 @@ static VGMSTREAM* init_vgmstream_dsp_common(STREAMFILE* sf, dsp_meta *dspm) {
}
}
/* don't know why, but it does happen*/
/* don't know why, but it does happen */
if (dspm->fix_looping && vgmstream->loop_end_sample > vgmstream->num_samples)
vgmstream->loop_end_sample = vgmstream->num_samples;
@ -235,7 +246,7 @@ static VGMSTREAM* init_vgmstream_dsp_common(STREAMFILE* sf, dsp_meta *dspm) {
}
if (!vgmstream_open_stream(vgmstream,sf,dspm->start_offset))
if (!vgmstream_open_stream(vgmstream, sf, dspm->start_offset))
goto fail;
return vgmstream;
@ -867,25 +878,28 @@ fail:
}
/* NPD - Icon Games header + subinterleaved DSPs [Vertigo (Wii), Build n' Race (Wii)] */
VGMSTREAM* init_vgmstream_wii_ndp(STREAMFILE* sf) {
VGMSTREAM* init_vgmstream_dsp_ndp(STREAMFILE* sf) {
dsp_meta dspm = {0};
/* checks */
if (!check_extensions(sf, "ndp"))
/* .nds: standard
* .ndp: header id */
if (!check_extensions(sf, "nds,ndp"))
goto fail;
if (read_32bitBE(0x00,sf) != 0x4E445000) /* "NDP\0" */
if (!is_id32be(0x00,sf, "NDP\0"))
goto fail;
if (read_32bitLE(0x08,sf) + 0x18 != get_streamfile_size(sf))
if (read_u32le(0x08,sf) + 0x18 != get_streamfile_size(sf))
goto fail;
/* 0x0c: sample rate */
dspm.channel_count = read_32bitLE(0x10,sf);
dspm.channel_count = read_u32le(0x10,sf);
dspm.max_channels = 2;
dspm.header_offset = 0x18;
dspm.header_spacing = 0x60;
dspm.start_offset = dspm.header_offset + dspm.channel_count*dspm.header_spacing;
dspm.interleave = 0x04;
dspm.ignore_loop_ps = 1; /* some files loops from 0 but loop ps is null */
dspm.meta_type = meta_WII_NDP;
return init_vgmstream_dsp_common(sf, &dspm);
@ -1094,8 +1108,6 @@ VGMSTREAM* init_vgmstream_dsp_sps_n1(STREAMFILE* sf) {
dspm.start_offset = dspm.header_offset + dspm.header_spacing*dspm.channel_count;
dspm.interleave = 0;
dspm.fix_loop_start = 1;
dspm.meta_type = meta_DSP_VAG;
return init_vgmstream_dsp_common(sf, &dspm);
fail:
@ -1306,3 +1318,60 @@ VGMSTREAM* init_vgmstream_dsp_wiivoice(STREAMFILE* sf) {
fail:
return NULL;
}
/* WIIADPCM - Exient wrapper [Need for Speed: Hot Pursuit (Wii)] */
VGMSTREAM* init_vgmstream_dsp_wiiadpcm(STREAMFILE* sf) {
dsp_meta dspm = {0};
/* checks */
if (!check_extensions(sf, "adpcm"))
goto fail;
if (!is_id32be(0x00,sf, "WIIA") && !is_id32be(0x00,sf, "DPCM"))
goto fail;
dspm.interleave = read_u32be(0x08,sf); /* interleave offset */
if (dspm.interleave) {
dspm.interleave -= 0x10;
}
/* 0x0c: 0 when RAM (2 DSP headers), interleave size when stream (2 WIIADPCM headers) */
dspm.channel_count = (dspm.interleave ? 2 : 1);
dspm.max_channels = 2;
dspm.header_offset = 0x10;
dspm.header_spacing = dspm.interleave;
dspm.start_offset = dspm.header_offset + 0x60;
dspm.meta_type = meta_DSP_WIIADPCM;
return init_vgmstream_dsp_common(sf, &dspm);
fail:
return NULL;
}
/* CWAC - CRI wrapper [Mario & Sonic at the Rio 2016 Olympic Games (WiiU)] */
VGMSTREAM* init_vgmstream_dsp_cwac(STREAMFILE* sf) {
dsp_meta dspm = {0};
/* checks */
/* .dsp: assumed */
if (!check_extensions(sf, "dsp"))
goto fail;
if (!is_id32be(0x00,sf, "CWAC"))
goto fail;
dspm.channel_count = read_u16be(0x04,sf);
dspm.header_offset = read_u32be(0x08,sf);
dspm.interleave = read_u32be(0x0c,sf) - dspm.header_offset;
dspm.max_channels = 2;
dspm.header_spacing = dspm.interleave;
dspm.start_offset = dspm.header_offset + 0x60;
dspm.ignore_loop_ps = 1; /* loop offset seems relative to CWAC? also interleave affects it */
dspm.meta_type = meta_DSP_CWAC;
return init_vgmstream_dsp_common(sf, &dspm);
fail:
return NULL;
}

View file

@ -2,56 +2,77 @@
#include "../coding/coding.h"
/* Entergram NXA Opus [Higurashi no Naku Koro ni Hou (Switch)] */
VGMSTREAM * init_vgmstream_opus_nxa(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
/* Entergram NXA Opus [Higurashi no Naku Koro ni Hou (Switch), Gensou Rougoku no Kaleidoscope (Switch)] */
VGMSTREAM* init_vgmstream_opus_nxa(STREAMFILE* sf) {
VGMSTREAM* vgmstream = NULL;
off_t start_offset;
int loop_flag = 0, channel_count;
size_t data_size, skip = 0;
int loop_flag, channels, type, sample_rate;
int32_t num_samples, loop_start, loop_end, skip;
size_t data_size, frame_size;
/* checks */
if (!check_extensions(streamFile, "nxa"))
if (!check_extensions(sf, "nxa"))
goto fail;
if (read_32bitBE(0x00, streamFile) != 0x4E584131) /* "NXA1" */
if (!is_id32be(0x00, sf, "NXA1"))
goto fail;
channel_count = read_16bitLE(0x10, streamFile);
skip = read_16bitLE(0x16, streamFile);
data_size = read_32bitLE(0x08, streamFile)-0x30;
start_offset = 0x30;
type = read_u32le(0x04, sf);
data_size = read_u32le(0x08, sf) - start_offset;
sample_rate = read_u32le(0x0C, sf);
channels = read_s16le(0x10, sf);
frame_size = read_u16le(0x12, sf);
/* 0x14: frame samples */
skip = read_s16le(0x16, sf);
num_samples = read_s32le(0x18, sf);
loop_start = read_s32le(0x1c, sf);
loop_end = read_s32le(0x20, sf);
/* rest: null */
/* TODO: Determine if loop points are stored externally. No visible loop points in header */
loop_flag = 0;
loop_flag = (loop_start > 0);
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count, loop_flag);
vgmstream = allocate_vgmstream(channels, loop_flag);
if (!vgmstream) goto fail;
vgmstream->meta_type = meta_NXA;
vgmstream->num_samples = read_32bitLE(0x20, streamFile);
vgmstream->sample_rate = read_32bitLE(0x0C, streamFile);
vgmstream->sample_rate = sample_rate;
vgmstream->num_samples = num_samples;
vgmstream->loop_start_sample = loop_start;
vgmstream->loop_end_sample = loop_end;
switch(type) {
#ifdef VGM_USE_FFMPEG
{
vgmstream->codec_data = init_ffmpeg_switch_opus(streamFile, start_offset, data_size, vgmstream->channels, skip, vgmstream->sample_rate);
if (!vgmstream->codec_data) goto fail;
vgmstream->coding_type = coding_FFmpeg;
vgmstream->layout_type = layout_none;
if (vgmstream->num_samples == 0) {
vgmstream->num_samples = switch_opus_get_samples(start_offset, data_size, streamFile) - skip;
case 1: { /* Higurashi no Naku Koro ni Hou (Switch) */
vgmstream->codec_data = init_ffmpeg_switch_opus(sf, start_offset, data_size, vgmstream->channels, skip, vgmstream->sample_rate);
if (!vgmstream->codec_data) goto fail;
vgmstream->coding_type = coding_FFmpeg;
vgmstream->layout_type = layout_none;
break;
}
}
#else
goto fail;
#endif
/* open the file for reading */
if (!vgmstream_open_stream(vgmstream, streamFile, start_offset))
case 2: { /* Gensou Rougoku no Kaleidoscope (Switch) */
opus_config cfg = {0};
cfg.channels = channels;
cfg.skip = skip;
cfg.frame_size = frame_size;
vgmstream->codec_data = init_ffmpeg_fixed_opus(sf, start_offset, data_size, &cfg);
if (!vgmstream->codec_data) goto fail;
vgmstream->coding_type = coding_FFmpeg;
vgmstream->layout_type = layout_none;
break;
}
#endif
default:
goto fail;
}
if (!vgmstream_open_stream(vgmstream, sf, start_offset))
goto fail;
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;

View file

@ -0,0 +1,128 @@
#include "meta.h"
#include "../coding/coding.h"
/* .SBK - from Addiction Pinball (PC) */
VGMSTREAM *init_vgmstream_sbk(STREAMFILE *sf) {
VGMSTREAM *vgmstream = NULL;
uint32_t sound_offset, sound_size, padding_size, sample_rate;
uint16_t format, channels, block_size, bps;
off_t table_offset, data_offset, entry_offset, cfg_fmt_offset;
size_t table_size, data_size, cfg_entry_size;
int target_subsong = sf->stream_index, total_subsongs, loop_flag, is_streamed;
/* checks */
if (!check_extensions(sf, "sbk"))
goto fail;
/* check header */
if (read_u32be(0x00, sf) != 0x52494646) /* "RIFF" */
goto fail;
if (read_u32be(0x08, sf) != 0x53424E4B) /* "SBNK" */
goto fail;
if (!find_chunk_le(sf, 0x57415649, 0x0c, 0, &table_offset, &table_size)) /* "WAVI" */
goto fail;
if (find_chunk_le(sf, 0x53574156, 0x0c, 0, &data_offset, &data_size)) { /* "SWAV" */
cfg_entry_size = 0x38;
cfg_fmt_offset = 0x1c;
} else {
/* 1997 demo version with sound names and no streamed section */
cfg_entry_size = 0x24;
cfg_fmt_offset = 0x0c;
}
total_subsongs = table_size / cfg_entry_size;
if (target_subsong == 0) target_subsong = 1;
if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1)
goto fail;
entry_offset = table_offset + cfg_entry_size * (target_subsong - 1);
sound_offset = read_u32le(entry_offset + 0x04, sf);
sound_size = read_u32le(entry_offset + 0x00, sf);
if (cfg_entry_size == 0x38) {
padding_size = read_u32le(entry_offset + 0x10, sf);
sound_offset += padding_size;
sound_size -= padding_size;
is_streamed = read_u8(entry_offset + 0x36, sf);
} else {
is_streamed = 0;
}
if (!is_streamed) {
if (!find_chunk_le(sf, 0x57415644, 0x0c, 0, &data_offset, &data_size)) /* "WAVD" */
goto fail;
} else {
if (!find_chunk_le(sf, 0x53574156, 0x0c, 0, &data_offset, &data_size)) /* "SWAV" */
goto fail;
}
sound_offset += data_offset;
/* read fmt chunk */
format = read_u16le(entry_offset + cfg_fmt_offset + 0x00, sf);
channels = read_u16le(entry_offset + cfg_fmt_offset + 0x02, sf);
sample_rate = read_u32le(entry_offset + cfg_fmt_offset + 0x04, sf);
block_size = read_u16le(entry_offset + cfg_fmt_offset + 0x0c, sf);
bps = read_u16le(entry_offset + cfg_fmt_offset + 0x0e, sf);
loop_flag = 0;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channels, loop_flag);
if (!vgmstream) goto fail;
/* fill in the vital statistics */
vgmstream->meta_type = meta_SBK;
vgmstream->sample_rate = sample_rate;
vgmstream->stream_size = sound_size;
vgmstream->num_streams = total_subsongs;
if (cfg_entry_size == 0x24) {
uint32_t num_entries, i;
if (!find_chunk_le(sf, 0x544F4320, 0x0c, 0, &table_offset, &table_size)) /* "TOC " */
goto fail;
num_entries = table_size / 0x10;
for (i = 0; i < num_entries; i++) {
entry_offset = table_offset + 0x10 * i;
if ((read_u8(entry_offset + 0x01, sf) & 0x80) &&
read_u8(entry_offset + 0x00, sf) == (target_subsong - 1)) {
read_string(vgmstream->stream_name, 0x0c, entry_offset + 0x04, sf);
break;
}
}
}
switch (format) {
case 0x01: /* PCM */
if (bps != 8 && bps != 16)
goto fail;
vgmstream->coding_type = (bps == 8) ? coding_PCM8_U : coding_PCM16LE;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = (bps == 8) ? 0x01 : 0x02;
vgmstream->num_samples = pcm_bytes_to_samples(sound_size, channels, bps);
break;
case 0x11: /* Microsoft IMA */
vgmstream->coding_type = coding_MS_IMA;
vgmstream->layout_type = layout_none;
vgmstream->interleave_block_size = block_size;
vgmstream->num_samples = ms_ima_bytes_to_samples(sound_size, block_size, channels);
break;
default:
goto fail;
}
if (!vgmstream_open_stream(vgmstream, sf, sound_offset))
goto fail;
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;
}

View file

@ -3,42 +3,52 @@
/* .smp - Terminal Reality's Infernal Engine 'samples' [Ghostbusters: The Video Game (PS2/PS3/X360/PC/PSP), Chandragupta (PS2/PSP)] */
VGMSTREAM * init_vgmstream_smp(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
off_t start_offset;
int loop_flag, channel_count, sample_rate, codec, version, num_samples, bps;
VGMSTREAM* init_vgmstream_smp(STREAMFILE* sf) {
VGMSTREAM* vgmstream = NULL;
off_t start_offset, extra_offset;
int loop_flag, channels, sample_rate, codec, version, num_samples, bps;
size_t data_size;
/* checks */
if (!check_extensions(streamFile, "smp"))
if (!check_extensions(sf, "smp"))
goto fail;
version = read_32bitLE(0x00,streamFile);
version = read_u32le(0x00,sf);
if (version != 0x05 && /* Ghostbusters (PS2), Mushroom Men (Wii) */
version != 0x06 && /* Ghostbusters (PS3/X360/PC) */
version != 0x07 && /* Ghostbusters (PSP) */
version != 0x08) /* Chandragupta (PS2/PSP), Street Cricket Champions 1/2 (PSP) */
version != 0x08) /* Chandragupta (PS2/PSP), Street Cricket Champions 1/2 (PSP), Guilty Party (Wii) */
goto fail;
/* 0x04~14: guid? */
if (read_32bitLE(0x14,streamFile) != 0) /* reserved? */
if (read_u32le(0x14,sf) != 0) /* reserved? */
goto fail;
num_samples = read_32bitLE(0x18,streamFile);
start_offset = read_32bitLE(0x1c,streamFile);
data_size = read_32bitLE(0x20,streamFile);
codec = read_32bitLE(0x24,streamFile);
channel_count = read_32bitLE(0x28,streamFile);
bps = read_32bitLE(0x2c,streamFile);
sample_rate = read_32bitLE(0x30,streamFile);
num_samples = read_s32le(0x18,sf);
start_offset = read_u32le(0x1c,sf);
data_size = read_u32le(0x20,sf);
codec = read_u32le(0x24,sf);
/* smaller header found in Guilty Party (Wii) */
if (version == 0x08 && start_offset == 0x80) {
channels = read_u8(0x28,sf);
bps = read_u8(0x29,sf);
sample_rate = read_u16le(0x2a,sf);
extra_offset = 0x2c; /* coefs only */
}
else {
channels = read_u32le(0x28,sf);
bps = read_u32le(0x2c,sf);
sample_rate = read_u32le(0x30,sf);
extra_offset = 0x34 + 0x1c; /* standard DSP header, but LE */
}
loop_flag = 0;
if (start_offset + data_size != get_streamfile_size(streamFile))
if (start_offset + data_size != get_streamfile_size(sf))
goto fail;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count, loop_flag);
vgmstream = allocate_vgmstream(channels, loop_flag);
if (!vgmstream) goto fail;
vgmstream->meta_type = meta_SMP;
@ -54,7 +64,7 @@ VGMSTREAM * init_vgmstream_smp(STREAMFILE *streamFile) {
block_align = 0x98 * vgmstream->channels;
encoder_delay = 0; /* 1024 looks ok, but num_samples needs to be adjusted too */
vgmstream->codec_data = init_ffmpeg_atrac3_raw(streamFile, start_offset,data_size, vgmstream->num_samples,vgmstream->channels,vgmstream->sample_rate, block_align, encoder_delay);
vgmstream->codec_data = init_ffmpeg_atrac3_raw(sf, start_offset,data_size, vgmstream->num_samples,vgmstream->channels,vgmstream->sample_rate, block_align, encoder_delay);
if (!vgmstream->codec_data) goto fail;
vgmstream->coding_type = coding_FFmpeg;
vgmstream->layout_type = layout_none;
@ -64,27 +74,27 @@ VGMSTREAM * init_vgmstream_smp(STREAMFILE *streamFile) {
case 0x02:
if (bps != 4) goto fail;
if (channel_count > 1) goto fail; /* not known */
/* 0x34: standard DSP header, but LE */
if (channels > 1) goto fail; /* not known */
vgmstream->coding_type = coding_NGC_DSP;
vgmstream->layout_type = layout_none;
dsp_read_coefs_le(vgmstream,streamFile,0x50,0x00);
dsp_read_coefs_le(vgmstream, sf, extra_offset, 0x00);
//todo adpcm hist
break;
case 0x04:
if (bps != 4) goto fail;
if (!msadpcm_check_coefs(streamFile, 0x36))
if (!msadpcm_check_coefs(sf, 0x36))
goto fail;
vgmstream->coding_type = coding_MSADPCM;
vgmstream->layout_type = layout_none;
vgmstream->frame_size = 0x86*channel_count;
vgmstream->frame_size = 0x86*channels;
break;
case 0x06:
if (bps != 4) goto fail;
if (channel_count > 1) goto fail; /* not known */
if (channels > 1) goto fail; /* not known */
vgmstream->coding_type = coding_PSX;
vgmstream->layout_type = layout_none;
@ -102,12 +112,12 @@ VGMSTREAM * init_vgmstream_smp(STREAMFILE *streamFile) {
block_count = data_size / block_size + (data_size % block_size ? 1 : 0); /* @0x54(2)? */
bytes = ffmpeg_make_riff_xma2(buf,0x100, vgmstream->num_samples, data_size, vgmstream->channels, vgmstream->sample_rate, block_count, block_size);
vgmstream->codec_data = init_ffmpeg_header_offset(streamFile, buf,bytes, start_offset,data_size);
vgmstream->codec_data = init_ffmpeg_header_offset(sf, buf,bytes, start_offset,data_size);
if (!vgmstream->codec_data) goto fail;
vgmstream->coding_type = coding_FFmpeg;
vgmstream->layout_type = layout_none;
//xma_fix_raw_samples(vgmstream, streamFile, start_offset,data_size, 0, ); //todo
//xma_fix_raw_samples(vgmstream, sf, start_offset,data_size, 0, ); //todo
break;
}
#endif
@ -117,7 +127,7 @@ VGMSTREAM * init_vgmstream_smp(STREAMFILE *streamFile) {
}
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
if (!vgmstream_open_stream(vgmstream, sf, start_offset))
goto fail;
return vgmstream;

View file

@ -610,10 +610,22 @@ static VGMSTREAM* init_subfile(txth_header* txth) {
sf_sub = setup_subfile_streamfile(txth->sf_body, txth->subfile_offset, txth->subfile_size, extension);
if (!sf_sub) goto fail;
sf_sub->stream_index = txth->sf->stream_index; /* in case of subfiles with subsongs */
sf_sub->stream_index = txth->sf->stream_index;
vgmstream = init_vgmstream_from_STREAMFILE(sf_sub);
if (!vgmstream) goto fail;
if (!vgmstream) {
/* In case of subfiles with subsongs pass subsong N by default (ex. subfile is a .fsb with N subsongs).
* But if the subfile is a single-subsong subfile (ex. subfile is a .fsb with 1 subsong) try again
* without passing index (as it would fail first trying to open subsong N). */
if (sf_sub->stream_index > 1) {
sf_sub->stream_index = 0;
vgmstream = init_vgmstream_from_STREAMFILE(sf_sub);
if (!vgmstream) goto fail;
}
else {
goto fail;
}
}
/* apply some fields */
if (txth->sample_rate)

View file

@ -1888,8 +1888,13 @@ static txtp_header* parse_txtp(STREAMFILE* sf) {
/* skip BOM if needed */
if (file_size > 0 &&
((uint16_t)read_16bitLE(0x00, sf) == 0xFFFE || (uint16_t)read_16bitLE(0x00, sf) == 0xFEFF))
(read_u16le(0x00, sf) == 0xFFFE || read_u16le(0x00, sf) == 0xFEFF)) {
txt_offset = 0x02;
}
else if ((read_u32be(0x00, sf) & 0xFFFFFF00) == 0xEFBBBF00) {
txt_offset = 0x03;
}
/* read and parse lines */
while (txt_offset < file_size) {

View file

@ -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;
@ -14,6 +17,7 @@ typedef enum { UBI_NONE = 0, UBI_AUDIO, UBI_LAYER, UBI_SEQUENCE, UBI_SILENCE } u
typedef struct {
int map_version;
size_t map_entry_size;
off_t map_name;
size_t section1_entry_size;
size_t section2_entry_size;
size_t section3_entry_size;
@ -30,6 +34,7 @@ typedef struct {
off_t audio_loop_flag;
off_t audio_loc_flag;
off_t audio_stereo_flag;
off_t audio_ram_streamed_flag;
off_t audio_internal_flag;
off_t audio_num_samples;
off_t audio_num_samples2;
@ -45,9 +50,11 @@ typedef struct {
int audio_subblock_and;
int audio_loc_and;
int audio_stereo_and;
int audio_ram_streamed_and;
int audio_has_internal_names;
size_t audio_interleave;
int audio_fix_psx_samples;
int has_rs_files;
off_t sequence_extra_offset;
off_t sequence_sequence_loop;
@ -86,7 +93,6 @@ typedef struct {
int is_padded_sectionX_offset;
int is_padded_sounds_offset;
int ignore_layer_error;
int default_codec_for_subblock0;
} ubi_sb_config;
typedef struct {
@ -161,8 +167,6 @@ typedef struct {
uint32_t stream_type; /* rough codec value */
uint32_t subblock_id; /* internal id to reference in section3 */
uint8_t subbank_index; /* ID of the entry in DC bank */
int is_localized;
int is_stereo;
int loop_flag; /* stream loops (normally internal sfx, but also external music) */
int loop_start; /* usually 0 */
@ -184,7 +188,9 @@ typedef struct {
int is_streamed; /* sound is streamed from storage */
int is_cd_streamed; /* found in PS2 BNM */
int is_ram_streamed; /* found in some PS2 games */
int is_external; /* sound is in an external file */
int is_localized; /* found in old PS2 games, determines which file the sound is in */
char resource_name[0x28]; /* filename to the external stream, or internal stream info for some games */
char readable_name[255]; /* final subsong name */
@ -345,7 +351,7 @@ VGMSTREAM* init_vgmstream_ubi_sm(STREAMFILE* sf) {
sb.map_zero = read_32bit(offset + 0x04, sf);
sb.map_offset = read_32bit(offset + 0x08, sf);
sb.map_size = read_32bit(offset + 0x0c, sf); /* includes sbX header, but not internal streams */
read_string(sb.map_name, sizeof(sb.map_name), offset + 0x10, sf); /* null-terminated and may contain garbage after null */
read_string(sb.map_name, sizeof(sb.map_name), offset + sb.cfg.map_name, sf); /* null-terminated and may contain garbage after null */
if (sb.cfg.map_version >= 3)
sb.map_unknown = read_32bit(offset + 0x30, sf); /* uncommon, id/config? longer name? mem garbage? */
@ -984,7 +990,7 @@ static VGMSTREAM* init_vgmstream_ubi_sb_base(ubi_sb_header* sb, STREAMFILE* sf_h
break;
case UBI_IMA_SCE:
vgmstream->coding_type = coding_UBI_IMA;
vgmstream->coding_type = coding_UBI_SCE_IMA;
vgmstream->layout_type = layout_blocked_ubi_sce;
vgmstream->full_block_size = read_32bitLE(0x18, sf_data);
@ -995,10 +1001,12 @@ static VGMSTREAM* init_vgmstream_ubi_sb_base(ubi_sb_header* sb, STREAMFILE* sf_h
case UBI_ADPCM:
/* custom Ubi 4/6-bit ADPCM used in early games:
* - Splinter Cell (PC): 4-bit w/ 2ch (music), 6-bit w/ 1ch (sfx)
* - Batman: Vengeance (PC): 4-bit w/ 2ch (music), 6-bit w/ 1ch (sfx)
* - Myst IV (PC/Xbox): 4bit-1ch (amb), some files only (ex. sfx_si_puzzle_stream.sb2)
* - possibly others */
* - Splinter Cell (PC): 4-bit w/ 1ch/2ch (all streams + menu music)
* - Batman: Vengeance (PC): 4-bit w/ 1ch/2ch (all streams)
* - Myst IV (PC/Xbox): 4-bit w/ 1ch (amb), some files only (ex. sfx_si_puzzle_stream.sb2)
* - The Jungle Book: Rhythm N'Groove (PC): 4-bit w/ 2ch (music/amb), 6-bit w/ 1ch (speech)
* - possibly others
* internal extension is .adp, maybe this can be called FMT_ADP */
/* skip extra header (some kind of id?) found in Myst IV */
if (read_32bitBE(start_offset + 0x00, sf_data) != 0x08000000 &&
@ -1027,6 +1035,13 @@ static VGMSTREAM* init_vgmstream_ubi_sb_base(ubi_sb_header* sb, STREAMFILE* sf_h
case RAW_PSX:
vgmstream->coding_type = coding_PSX;
vgmstream->layout_type = layout_interleave;
if (sb->cfg.has_rs_files) {
/* SC:PT PS2 has extra 0x30 bytes, presumably from (missing) VAG header */
sb->stream_size -= 0x30;
vgmstream->stream_size -= 0x30;
}
if (sb->is_ps2_bnm) {
vgmstream->interleave_block_size = (sb->is_cd_streamed) ?
sb->cfg.audio_interleave :
@ -1039,7 +1054,12 @@ static VGMSTREAM* init_vgmstream_ubi_sb_base(ubi_sb_header* sb, STREAMFILE* sf_h
if (vgmstream->num_samples == 0) { /* early PS2 games may not set it for internal streams */
vgmstream->num_samples = ps_bytes_to_samples(sb->stream_size, sb->channels);
vgmstream->loop_end_sample = vgmstream->num_samples;
if (sb->loop_start == 0) {
ps_find_loop_offsets(sf_data, sb->stream_offset, sb->stream_size,
sb->channels, vgmstream->interleave_block_size,
&vgmstream->loop_start_sample, &vgmstream->loop_end_sample);
}
}
/* late PS3 SBs have double sample count here for who knows why
@ -1108,7 +1128,7 @@ static VGMSTREAM* init_vgmstream_ubi_sb_base(ubi_sb_header* sb, STREAMFILE* sf_h
break;
}
//TODO: Ubi XMA1 (raw or fmt) is a bit strange, FFmpeg decodes some frames slightly wrong
// TODO: Ubi XMA1 (raw or fmt) is a bit strange, FFmpeg decodes some frames slightly wrong
// XMA1 normally has a frame counter in the first nibble but Ubi's is always set to 0.
// Probably a beta/custom encoder that creates some buggy frames, that a real X360 handles ok, but trips FFmpeg
// xmaencode decodes correctly if counters are fixed (otherwise has clicks on every frame).
@ -1751,6 +1771,7 @@ static int parse_type_audio_ps2_old(ubi_sb_header* sb, off_t offset, STREAMFILE*
int32_t(*read_32bit)(off_t, STREAMFILE*) = sb->big_endian ? read_32bitBE : read_32bitLE;
uint32_t pitch;
uint32_t test_sample_rate;
int is_stereo;
sb->stream_size = read_32bit(offset + sb->cfg.audio_stream_size, sf);
sb->stream_offset = read_32bit(offset + sb->cfg.audio_stream_offset, sf);
@ -1768,10 +1789,10 @@ static int parse_type_audio_ps2_old(ubi_sb_header* sb, off_t offset, STREAMFILE*
sb->is_streamed = read_32bit(offset + sb->cfg.audio_streamed_flag, sf) & sb->cfg.audio_streamed_and;
sb->loop_flag = read_32bit(offset + sb->cfg.audio_loop_flag, sf) & sb->cfg.audio_loop_and;
sb->is_localized = read_32bit(offset + sb->cfg.audio_loc_flag, sf) & sb->cfg.audio_loc_and;
sb->is_stereo = read_32bit(offset + sb->cfg.audio_stereo_flag, sf) & sb->cfg.audio_stereo_and;
is_stereo = read_32bit(offset + sb->cfg.audio_stereo_flag, sf) & sb->cfg.audio_stereo_and;
sb->num_samples = 0; /* calculate from size */
sb->channels = sb->is_stereo ? 2 : 1;
sb->channels = is_stereo ? 2 : 1;
sb->stream_size *= sb->channels;
sb->subblock_id = 0;
@ -1791,6 +1812,7 @@ fail:
static int parse_type_layer_ps2_old(ubi_sb_header* sb, off_t offset, STREAMFILE* sf) {
int32_t(*read_32bit)(off_t, STREAMFILE*) = sb->big_endian ? read_32bitBE : read_32bitLE;
uint32_t pitch;
/* much simpler than later iteration */
sb->layer_count = read_32bit(offset + sb->cfg.layer_layer_count, sf);
@ -1807,7 +1829,8 @@ static int parse_type_layer_ps2_old(ubi_sb_header* sb, off_t offset, STREAMFILE*
goto fail;
}
sb->sample_rate = ubi_ps2_pitch_to_freq(read_32bit(offset + sb->cfg.layer_pitch, sf));
pitch = read_32bit(offset + sb->cfg.layer_pitch, sf);
sb->sample_rate = ubi_ps2_pitch_to_freq(pitch);
sb->is_localized = read_32bit(offset + sb->cfg.layer_loc_flag, sf) & sb->cfg.layer_loc_and;
sb->num_samples = 0; /* calculate from size */
@ -1865,12 +1888,24 @@ static int parse_type_audio(ubi_sb_header* sb, off_t offset, STREAMFILE* sf) {
/* apparently, there may also be other subblocks based on various flags but they were not seen so far */
if (sb->cfg.audio_subblock_flag && sb->cfg.audio_subblock_and) {
/* flag probably means "software decoded" */
int subblock_flag = read_32bit(offset + sb->cfg.audio_subblock_flag, sf) & sb->cfg.audio_subblock_and;
sb->subblock_id = (!subblock_flag) ? 0 : 1;
/* stream_type field is not used if the flag is not set (it even contains garbage in some versions)
* except for PS3 which has two hardware codecs (PSX and AT3) */
if (!subblock_flag && sb->platform != UBI_PS3)
sb->stream_type = 0x00;
} else {
sb->subblock_id = (sb->stream_type == 0x01) ? 0 : 1;
}
if (sb->cfg.has_rs_files && !sb->is_external) {
/* found in Splinter Cell: Pandora Tomorrow (PS2) */
sb->is_ram_streamed = read_32bit(offset + sb->cfg.audio_ram_streamed_flag, sf) & sb->cfg.audio_ram_streamed_and;
sb->is_external = sb->is_ram_streamed;
}
sb->loop_flag = read_32bit(offset + sb->cfg.audio_loop_flag, sf) & sb->cfg.audio_loop_and;
if (sb->loop_flag) {
@ -1896,7 +1931,9 @@ static int parse_type_audio(ubi_sb_header* sb, off_t offset, STREAMFILE* sf) {
if (sb->cfg.audio_stream_name) {
if (sb->is_dat && !sb->is_external) {
sb->subbank_index = read_8bit(offset + sb->cfg.audio_stream_name + 0x01, sf);
} else {
} else if (sb->cfg.has_rs_files && sb->is_ram_streamed) {
strcpy(sb->resource_name, "MAPS.RS1");
} else if (sb->is_external || sb->cfg.audio_has_internal_names) {
read_string(sb->resource_name, sb->cfg.resource_name_size, offset + sb->cfg.audio_stream_name, sf);
}
}
@ -1955,8 +1992,7 @@ static int parse_type_sequence(ubi_sb_header* sb, off_t offset, STREAMFILE* sf)
if (!sb->sequence_multibank) {
sb->sequence_multibank = is_other_bank(sb, sf, bank_number);
}
}
else {
} else {
entry_number = entry_number & 0x3FFFFFFF;
if (entry_number > sb->section2_num) {
VGM_LOG("UBI SB: chain with wrong entry %i vs %i at %x\n", entry_number, sb->section2_num, (uint32_t)sb->extra_offset);
@ -2032,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;
}
@ -2122,6 +2160,12 @@ static int parse_type_random(ubi_sb_header* sb, off_t offset, STREAMFILE* sf) {
VGM_LOG("UBI SB: random in other bank\n");
goto fail;
}
} else {
entry_number = entry_number & 0x3FFFFFFF;
if (entry_number > sb->section2_num) {
VGM_LOG("UBI SB: random with wrong entry %i vs %i at %x\n", entry_number, sb->section2_num, (uint32_t)sb->extra_offset);
goto fail;
}
}
//todo make rand or stuff (old chance: int from 0 to 0x10000, new: float from 0.0 to 1.0)
@ -2138,7 +2182,7 @@ fail:
return 0;
}
static int set_default_codec_for_platform(ubi_sb_header *sb) {
static int set_hardware_codec_for_platform(ubi_sb_header *sb) {
switch (sb->platform) {
case UBI_PC:
sb->codec = RAW_PCM;
@ -2162,16 +2206,11 @@ static int set_default_codec_for_platform(ubi_sb_header *sb) {
case UBI_X360:
sb->codec = RAW_XMA1;
break;
#if 0
case UBI_PS3: /* assumed, but no games seem to use it */
sb->codec = RAW_AT3;
break;
#endif
case UBI_3DS:
sb->codec = FMT_CWAV;
break;
default:
VGM_LOG("UBI SB: unknown internal format\n");
VGM_LOG("UBI SB: unknown hardware codec\n");
return 0;
}
@ -2199,7 +2238,9 @@ static int parse_stream_codec(ubi_sb_header* sb) {
if (sb->is_bnm || sb->version < 0x00000007) { /* bnm is ~v0 but some games have wonky versions */
switch (sb->stream_type) {
case 0x01:
if (!set_default_codec_for_platform(sb))
if (sb->is_streamed)
sb->codec = RAW_PCM;
else if (!set_hardware_codec_for_platform(sb))
goto fail;
break;
@ -2211,10 +2252,14 @@ static int parse_stream_codec(ubi_sb_header* sb) {
sb->codec = FMT_APM;
break;
case 0x06: /* The Jungle Book (internal extension is .adp, maybe Ubi ADPCM can be considered FMT_ADP) */
case 0x06:
sb->codec = UBI_ADPCM;
break;
#if 0
case 0x07:
sb->codec = FMT_PFK; /* not seen yet, some MPEG based codec, referred to as "PFK" in the code */
break;
#endif
case 0x08:
sb->codec = UBI_IMA; /* Ubi IMA v2/v3 */
break;
@ -2226,7 +2271,9 @@ static int parse_stream_codec(ubi_sb_header* sb) {
} else if (sb->version < 0x000A0000) {
switch (sb->stream_type) {
case 0x01:
if (!set_default_codec_for_platform(sb))
if (sb->is_streamed)
sb->codec = RAW_PCM;
else if (!set_hardware_codec_for_platform(sb))
goto fail;
break;
@ -2235,7 +2282,7 @@ static int parse_stream_codec(ubi_sb_header* sb) {
break;
#if 0
case 0x03:
sb->codec = FMT_MPDX; /* not seen yet, some MPEG based codec */
sb->codec = FMT_PFK; /* not seen yet, some MPEG based codec, referred to as "PFK" in the code */
break;
#endif
case 0x04:
@ -2251,14 +2298,9 @@ static int parse_stream_codec(ubi_sb_header* sb) {
goto fail;
}
} else {
/* some Xbox games default to codec 0 if subblock flag isn't set while the actual field contains garbage */
if (sb->cfg.default_codec_for_subblock0 && sb->type == UBI_AUDIO && sb->subblock_id == 0) {
sb->stream_type = 0x00;
}
switch (sb->stream_type) {
case 0x00:
if (!set_default_codec_for_platform(sb))
if (!set_hardware_codec_for_platform(sb))
goto fail;
break;
@ -2358,9 +2400,6 @@ static int parse_offsets(ubi_sb_header* sb, STREAMFILE* sf) {
VGM_ASSERT(!sb->is_map && sb->section3_num > 2, "UBI SB: section3 > 2 found\n");
if (sb->is_external)
return 1;
/* Internal sounds are split into subblocks, with their offsets being relative to subblock start.
* A table contains sizes of each subblock, so we adjust offsets based on the subblock ID of our sound.
* Headers normally only use 0 or 1, and section3 may only define id1 (which the internal sound would use).
@ -2384,6 +2423,9 @@ static int parse_offsets(ubi_sb_header* sb, STREAMFILE* sf) {
* 0x0c - absolute subblock offset
*/
if (sb->is_external && !sb->is_ram_streamed)
return 1;
for (i = 0; i < sb->section3_num; i++) {
off_t offset = sb->section3_offset + 0x14 * i;
off_t table_offset = read_32bit(offset + 0x04, sf) + sb->section3_offset;
@ -2396,6 +2438,9 @@ static int parse_offsets(ubi_sb_header* sb, STREAMFILE* sf) {
if (index == sb->header_index) {
sb->stream_offset = read_32bit(table_offset + 0x08 * j + 0x04, sf);
if (sb->is_ram_streamed)
break;
for (k = 0; k < table2_num; k++) {
uint32_t id = read_32bit(table2_offset + 0x10 * k + 0x00, sf);
@ -2423,21 +2468,23 @@ 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;
off_t sounds_offset = sb->section3_offset + sb->cfg.section3_entry_size*sb->section3_num;
if (sb->is_external)
return 1;
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;
if (sb->section3_num > 1) { /* maybe should always test this? */
for (i = 0; i < sb->section3_num; i++) {
off_t offset = sb->section3_offset + sb->cfg.section3_entry_size * i;
for (i = 0; i < sb->section3_num; i++) {
off_t offset = sb->section3_offset + sb->cfg.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, sf) == sb->subblock_id)
break;
sb->stream_offset += read_32bit(offset + 0x04, sf);
}
/* table has unordered ids+size, so if our id doesn't match current data offset must be beyond */
if (read_32bit(offset + 0x00, sf) == sb->subblock_id)
break;
sb->stream_offset += read_32bit(offset + 0x04, sf);
}
}
@ -2639,28 +2686,6 @@ static void config_sb_audio_fb(ubi_sb_header* sb, off_t flag_bits, int streamed_
sb->cfg.audio_subblock_and = subblock_and;
sb->cfg.audio_loop_and = loop_and;
}
static void config_sb_audio_fb_ps2_bnm(ubi_sb_header *sb, off_t flag_bits, int streamed_and, int cd_streamed_and, int loop_and) {
/* audio header with standard flags */
sb->cfg.audio_streamed_flag = flag_bits;
sb->cfg.audio_cd_streamed_flag = flag_bits;
sb->cfg.audio_loop_flag = flag_bits;
sb->cfg.audio_streamed_and = streamed_and;
sb->cfg.audio_cd_streamed_and = cd_streamed_and;
sb->cfg.audio_loop_and = loop_and;
}
static void config_sb_audio_ps2_old(ubi_sb_header* sb, off_t flag_bits, int streamed_and, int loop_and, int loc_and, int stereo_and, off_t pitch, off_t sample_rate) {
/* sample rate only, bit flags */
sb->cfg.audio_streamed_flag = flag_bits;
sb->cfg.audio_loop_flag = flag_bits;
sb->cfg.audio_loc_flag = flag_bits;
sb->cfg.audio_stereo_flag = flag_bits;
sb->cfg.audio_streamed_and = streamed_and;
sb->cfg.audio_loop_and = loop_and;
sb->cfg.audio_loc_and = loc_and;
sb->cfg.audio_stereo_and = stereo_and;
sb->cfg.audio_pitch = pitch;
sb->cfg.audio_sample_rate = sample_rate;
}
static void config_sb_audio_hs(ubi_sb_header* sb, off_t channels, off_t sample_rate, off_t num_samples, off_t num_samples2, off_t stream_name, off_t stream_type) {
/* audio header with stream name */
sb->cfg.audio_channels = channels;
@ -2679,6 +2704,30 @@ static void config_sb_audio_he(ubi_sb_header* sb, off_t channels, off_t sample_r
sb->cfg.audio_extra_name = extra_name;
sb->cfg.audio_stream_type = stream_type;
}
static void config_sb_audio_ps2_bnm(ubi_sb_header *sb, off_t flag_bits, int streamed_and, int cd_streamed_and, int loop_and, off_t channels, off_t sample_rate) {
/* bit flags, channels and sample rate */
sb->cfg.audio_streamed_flag = flag_bits;
sb->cfg.audio_cd_streamed_flag = flag_bits;
sb->cfg.audio_loop_flag = flag_bits;
sb->cfg.audio_streamed_and = streamed_and;
sb->cfg.audio_cd_streamed_and = cd_streamed_and;
sb->cfg.audio_loop_and = loop_and;
sb->cfg.audio_channels = channels;
sb->cfg.audio_sample_rate = sample_rate;
}
static void config_sb_audio_ps2_old(ubi_sb_header *sb, off_t flag_bits, int streamed_and, int loop_and, int loc_and, int stereo_and, off_t pitch, off_t sample_rate) {
/* bit flags, sample rate only */
sb->cfg.audio_streamed_flag = flag_bits;
sb->cfg.audio_loop_flag = flag_bits;
sb->cfg.audio_loc_flag = flag_bits;
sb->cfg.audio_stereo_flag = flag_bits;
sb->cfg.audio_streamed_and = streamed_and;
sb->cfg.audio_loop_and = loop_and;
sb->cfg.audio_loc_and = loc_and;
sb->cfg.audio_stereo_and = stereo_and;
sb->cfg.audio_pitch = pitch;
sb->cfg.audio_sample_rate = sample_rate;
}
static void config_sb_sequence(ubi_sb_header* sb, off_t sequence_count, off_t entry_size) {
/* sequence header and chain table */
sb->cfg.sequence_sequence_loop = sequence_count - 0x10;
@ -2762,6 +2811,7 @@ static int config_sb_version(ubi_sb_header* sb, STREAMFILE* sf) {
int is_bia_ps2 = 0, is_biadd_psp = 0;
int is_sc2_ps2_gc = 0;
int is_sc4_pc_online = 0;
int is_myst4_pc = 0;
/* Most of the format varies with almost every game + platform (struct serialization?).
* Support is configured case-by-case as offsets/order/fields only change slightly,
@ -2879,6 +2929,7 @@ static int config_sb_version(ubi_sb_header* sb, STREAMFILE* sf) {
sb->cfg.map_version = 3;
sb->cfg.map_entry_size = (sb->cfg.map_version < 2) ? 0x30 : 0x34;
sb->cfg.map_name = 0x10;
if (sb->is_blk) {
sb->cfg.map_entry_size = 0x30;
}
@ -2936,7 +2987,6 @@ static int config_sb_version(ubi_sb_header* sb, STREAMFILE* sf) {
if (sb->is_bnm || sb->is_dat || sb->is_ps2_bnm) {
//sb->allowed_types[0x0a] = 1; /* only needed inside sequences */
sb->allowed_types[0x0b] = 1;
sb->allowed_types[0x09] = 1;
}
#if 0
@ -2966,7 +3016,6 @@ static int config_sb_version(ubi_sb_header* sb, STREAMFILE* sf) {
sb->version = 0x00000000;
}
/* Tonic Touble beta has garbage instead of version */
if (sb->is_bnm && sb->version > 0x00000000 && sb->platform == UBI_PC) {
if (check_project_file(sf, "ED_MAIN.LCB", 0)) {
@ -3028,7 +3077,6 @@ static int config_sb_version(ubi_sb_header* sb, STREAMFILE* sf) {
config_sb_sequence(sb, 0x2c, 0x1c);
/* no layers */
return 1;
}
@ -3053,7 +3101,7 @@ static int config_sb_version(ubi_sb_header* sb, STREAMFILE* sf) {
config_sb_audio_fs(sb, 0x2c, 0x00, 0x30);
config_sb_audio_hs(sb, 0x42, 0x3c, 0x34, 0x34, 0x48, 0x44);
/* has internal names but they're partially overwritten by sound index */
/* has internal names but they're partially overwritten by subbank index */
config_sb_sequence(sb, 0x24, 0x18);
@ -3066,12 +3114,11 @@ static int config_sb_version(ubi_sb_header* sb, STREAMFILE* sf) {
/* Rayman 2: Revolution (2000)(PS2)-bnm */
/* Disney's Dinosaur (2000)(PS2)-bnm */
/* Hype: The Time Quest (2001)(PS2)-bnm */
if (sb->version == 0x32787370 && sb->platform == UBI_PS2 && sb->is_ps2_bnm) {
if (sb->version == 0x32787370 && sb->platform == UBI_PS2) {
sb->version = 0x00000000; /* for convenience */
config_sb_entry(sb, 0x1c, 0x44);
config_sb_audio_fb_ps2_bnm(sb, 0x18, (1 << 5), (1 << 6), (1 << 7));
config_sb_audio_hs(sb, 0x20, 0x22, 0x00, 0x00, 0x00, 0x1c);
config_sb_audio_ps2_bnm(sb, 0x18, (1 << 5), (1 << 6), (1 << 7), 0x20, 0x22);
sb->cfg.audio_interleave = 0x400;
config_sb_sequence(sb, 0x24, 0x14);
@ -3143,20 +3190,16 @@ static int config_sb_version(ubi_sb_header* sb, STREAMFILE* sf) {
return 1;
}
#if 0
//todo group flags and maybe num_samples for sfx are off
/* Myst III: Exile (2001)(PS2)-map */
if (sb->version == 0x00000004 && sb->platform == UBI_PS2) {
config_sb_entry(sb, 0x34, 0x70);
config_sb_audio_fb(sb, 0x1c, (1 << 3), (1 << 6), (1 << 4)); //???
config_sb_audio_hs(sb, 0x24, 0x28, 0x2c, 0x34, 0x44, 0x6c);
sb->cfg.audio_streamed_flag = 0x6c; /* no streamed flag? use codec as flag */
config_sb_audio_fb(sb, 0x1c, (1 << 4), 0, (1 << 5));
config_sb_audio_hs(sb, 0x24, 0x28, 0x34, 0x3c, 0x44, 0x6c);
config_sb_sequence(sb, 0x2c, 0x24);
return 1;
}
#endif
/* Splinter Cell (2002)(PC)-map */
/* Splinter Cell: Pandora Tomorrow (2004)(PC)-map */
@ -3207,7 +3250,7 @@ static int config_sb_version(ubi_sb_header* sb, STREAMFILE* sf) {
if (sb->version == 0x00000007 && sb->platform == UBI_PS2) {
config_sb_entry(sb, 0x40, 0x70);
config_sb_audio_fb(sb, 0x1c, (1 << 2), 0, (1 << 4));
config_sb_audio_fb(sb, 0x1c, (1 << 2), 0, (1 << 3));
config_sb_audio_hs(sb, 0x24, 0x28, 0x34, 0x3c, 0x44, 0x6c); /* num_samples may be null */
config_sb_sequence(sb, 0x2c, 0x30);
@ -3217,8 +3260,13 @@ static int config_sb_version(ubi_sb_header* sb, STREAMFILE* sf) {
if (is_sc2_ps2_gc) {
sb->cfg.map_entry_size = 0x38;
/* some amb .ss2 have bad sizes with mixed random data, bad extraction/unused crap? */
/* Pandora Tomorrow voices have bad offsets too */
sb->cfg.map_name = 0x18;
sb->cfg.has_rs_files = 1;
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 (ex #252, #10874) */
sb->cfg.layer_hijack = LAYER_HIJACK_SCPT_PS2; /* some amb .ss1 layers (ex. #226, not #1927) have mixed garbage */
}
return 1;
}
@ -3238,6 +3286,7 @@ static int config_sb_version(ubi_sb_header* sb, STREAMFILE* sf) {
if (is_sc2_ps2_gc) {
sb->cfg.map_entry_size = 0x38;
sb->cfg.map_name = 0x18;
sb->cfg.audio_streamed_and = 0x01000000; /* did somebody forget about BE? */
}
return 1;
@ -3313,7 +3362,7 @@ static int config_sb_version(ubi_sb_header* sb, STREAMFILE* sf) {
/* Prince of Persia: The Sands of Time (2003)(PS2)-bank 0x000A0004 / 0x000A0002 (POP1 port/Demo) */
/* Tom Clancy's Rainbow Six 3 (2003)(PS2)-bank 0x000A0007 */
/* Tom Clancy's Ghost Recon 2 (2004)(PS2)-bank 0x000A0007 */
/* Splinter Cell: Pandora Tomorrow (2004)(PS2)-bank 0x000A0008 (separate banks from main map) */
/* Splinter Cell: Pandora Tomorrow-online (2004)(PS2)-bank 0x000A0008 */
/* Prince of Persia: Warrior Within (Demo)(2004)(PS2)-bank 0x00100000 */
/* Prince of Persia: Warrior Within (2004)(PS2)-bank 0x00120009 */
if ((sb->version == 0x000A0002 && sb->platform == UBI_PS2) ||
@ -3364,7 +3413,6 @@ static int config_sb_version(ubi_sb_header* sb, STREAMFILE* sf) {
config_sb_audio_fs(sb, 0x24, 0x28, 0x34);
config_sb_audio_hs(sb, 0x52, 0x4c, 0x38, 0x40, 0x58, 0x54);
sb->cfg.audio_has_internal_names = 1;
sb->cfg.default_codec_for_subblock0 = 1;
config_sb_sequence(sb, 0x28, 0x14);
@ -3381,8 +3429,6 @@ static int config_sb_version(ubi_sb_header* sb, STREAMFILE* sf) {
config_sb_audio_fs(sb, 0x24, 0x28, 0x2c);
config_sb_audio_hs(sb, 0x4a, 0x44, 0x30, 0x38, 0x50, 0x4c);
sb->cfg.audio_has_internal_names = 1;
sb->cfg.default_codec_for_subblock0 = 1;
config_sb_sequence(sb, 0x28, 0x14);
@ -3418,7 +3464,6 @@ static int config_sb_version(ubi_sb_header* sb, STREAMFILE* sf) {
config_sb_audio_fs(sb, 0x24, 0x28, 0x40);
config_sb_audio_hs(sb, 0x5e, 0x58, 0x44, 0x4c, 0x64, 0x60);
sb->cfg.audio_has_internal_names = 1;
sb->cfg.default_codec_for_subblock0 = 1;
config_sb_sequence(sb, 0x28, 0x14);
@ -3429,7 +3474,7 @@ static int config_sb_version(ubi_sb_header* sb, STREAMFILE* sf) {
return 1;
}
/* Myst IV (Demo)(2004)(PC)-bank */
/* Myst IV: Revelation (Demo)(2004)(PC)-bank */
if (sb->version == 0x00100000 && sb->platform == UBI_PC) {
config_sb_entry(sb, 0x68, 0xa4);
@ -3439,11 +3484,21 @@ static int config_sb_version(ubi_sb_header* sb, STREAMFILE* sf) {
return 1;
}
/* two configs with same id; use project file as identifier */
if (sb->version == 0x00120006 && sb->platform == UBI_PC) {
if (check_project_file(sf, "gamesnd_myst4.sp0", 1)) {
is_myst4_pc = 1;
}
}
/* Myst IV: Revelation (2004)(PC)-bank 0x00120006 */
/* Prince of Persia: Warrior Within (Demo)(2004)(PC)-bank 0x00120006 */
/* Prince of Persia: Warrior Within (2004)(PC)-bank 0x00120009 */
if ((sb->version == 0x00120006 && sb->platform == UBI_PC) ||
(sb->version == 0x00120009 && sb->platform == UBI_PC)) {
config_sb_entry(sb, 0x6c, 0x84);
if (is_myst4_pc)
config_sb_entry(sb, 0x6c, 0xa4);
config_sb_audio_fs(sb, 0x24, 0x2c, 0x28);
config_sb_audio_hs(sb, 0x4c, 0x44, 0x30, 0x38, 0x54, 0x50);
@ -3460,7 +3515,6 @@ static int config_sb_version(ubi_sb_header* sb, STREAMFILE* sf) {
config_sb_audio_fs(sb, 0x24, 0x28, 0x40);
config_sb_audio_hs(sb, 0x60, 0x58, 0x44, 0x4c, 0x68, 0x64);
sb->cfg.audio_has_internal_names = 1;
sb->cfg.default_codec_for_subblock0 = 1;
config_sb_sequence(sb, 0x28, 0x14);
return 1;
@ -3489,6 +3543,8 @@ static int config_sb_version(ubi_sb_header* sb, STREAMFILE* sf) {
/* Beowulf: The Game (2007)(PSP)-map */
if (sb->version == 0x0012000C && sb->platform == UBI_PSP && !is_biadd_psp) {
config_sb_entry(sb, 0x68, 0x84);
if (is_biadd_psp)
config_sb_entry(sb, 0x80, 0x94);
config_sb_audio_fs(sb, 0x24, 0x2c, 0x28);
config_sb_audio_hs(sb, 0x4c, 0x44, 0x30, 0x38, 0x54, 0x50);
@ -3498,17 +3554,7 @@ static int config_sb_version(ubi_sb_header* sb, STREAMFILE* sf) {
config_sb_layer_hs(sb, 0x1c, 0x60, 0x64, 0x30);
config_sb_layer_sh(sb, 0x18, 0x00, 0x08, 0x0c, 0x14);
return 1;
}
//todo some .sbX have bad external stream offsets, but not all (ex. offset 0xE3641 but should be 0x0A26)
/* Brothers in Arms: D-Day (2006)(PSP)-bank */
if (sb->version == 0x0012000C && sb->platform == UBI_PSP && is_biadd_psp) {
config_sb_entry(sb, 0x80, 0x94);
config_sb_audio_fs(sb, 0x24, 0x2c, 0x28);
config_sb_audio_hs(sb, 0x4c, 0x44, 0x30, 0x38, 0x54, 0x50);
sb->cfg.audio_has_internal_names = 1;
//todo some .sbX in BiA:DD have bad external stream offsets, but not all (ex. offset 0xE3641 but should be 0x0A26)
return 1;
}
@ -3523,7 +3569,7 @@ static int config_sb_version(ubi_sb_header* sb, STREAMFILE* sf) {
return 1;
}
/* Myst IV: Revelation (2005)(PC)-bank */
/* Myst IV: Revelation (2005)(Xbox)-bank */
/* Splinter Cell: Chaos Theory (2005)(Xbox)-map */
if (sb->version == 0x00120012 && sb->platform == UBI_XBOX) {
config_sb_entry(sb, 0x48, 0x4c);
@ -3535,6 +3581,20 @@ static int config_sb_version(ubi_sb_header* sb, STREAMFILE* sf) {
return 1;
}
/* Splinter Cell: Chaos Theory (2005)(PS2)-map */
if (sb->version == 0x00130001 && sb->platform == UBI_PS2) {
config_sb_entry(sb, 0x48, 0x4c);
config_sb_audio_fb(sb, 0x18, (1 << 2), (1 << 3), (1 << 4));
config_sb_audio_he(sb, 0x20, 0x24, 0x30, 0x38, 0x40, 0x44);
config_sb_sequence(sb, 0x28, 0x10);
//config_sb_layer_he(sb, 0x1c, 0x28, 0x30, 0x34);
//config_sb_layer_sh(sb, 0x18, 0x00, 0x08, 0x0c, 0x14);
return 1;
}
/* Splinter Cell: Chaos Theory (2005)(GC)-map */
if (sb->version == 0x00130001 && sb->platform == UBI_GC) {
config_sb_entry(sb, 0x68, 0x54);
@ -3689,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;
}
@ -3749,23 +3809,11 @@ static int config_sb_version(ubi_sb_header* sb, STREAMFILE* sf) {
}
}
/* Splinter Cell: Double Agent (2006)(PC)-map (offline) */
if (sb->version == 0x00180006 && sb->platform == UBI_PC && !is_sc4_pc_online) {
/* Splinter Cell: Double Agent (2006)(PC)-map */
if (sb->version == 0x00180006 && sb->platform == UBI_PC) {
config_sb_entry(sb, 0x68, 0x7c);
config_sb_audio_fs(sb, 0x2c, 0x34, 0x30);
config_sb_audio_he(sb, 0x5c, 0x54, 0x40, 0x48, 0x64, 0x60);
config_sb_sequence(sb, 0x2c, 0x14);
config_sb_layer_he(sb, 0x20, 0x38, 0x3c, 0x44);
config_sb_layer_sh(sb, 0x34, 0x00, 0x08, 0x0c, 0x14);
return 1;
}
/* Splinter Cell: Double Agent (2006)(PC)-map (online) */
if (sb->version == 0x00180006 && sb->platform == UBI_PC && is_sc4_pc_online) {
config_sb_entry(sb, 0x68, 0x78);
if (is_sc4_pc_online)
config_sb_entry(sb, 0x68, 0x78);
config_sb_audio_fs(sb, 0x2c, 0x34, 0x30);
config_sb_audio_he(sb, 0x5c, 0x54, 0x40, 0x48, 0x64, 0x60);
@ -3819,7 +3867,6 @@ static int config_sb_version(ubi_sb_header* sb, STREAMFILE* sf) {
sb->cfg.audio_xma_offset = 0x68;
config_sb_sequence(sb, 0x2c, 0x14);
return 1;
}
@ -3899,7 +3946,6 @@ static int config_sb_version(ubi_sb_header* sb, STREAMFILE* sf) {
config_sb_layer_he(sb, 0x20, 0x34, 0x38, 0x40);
config_sb_layer_sh(sb, 0x30, 0x00, 0x04, 0x08, 0x10);
return 1;
}
@ -3939,7 +3985,6 @@ static int config_sb_version(ubi_sb_header* sb, STREAMFILE* sf) {
config_sb_audio_fs(sb, 0x2c, 0x30, 0x34);
config_sb_audio_he(sb, 0x40, 0x44, 0x4c, 0x54, 0x5c, 0x60);
return 1;
}

View 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_ */

View file

@ -1,6 +1,7 @@
#ifndef _UBI_SB_STREAMFILE_H_
#define _UBI_SB_STREAMFILE_H_
#include "../streamfile.h"
#include "ubi_sb_garbage_streamfile.h"
typedef struct {
@ -34,8 +35,8 @@ typedef struct {
} 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;
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;
@ -49,14 +50,14 @@ static size_t ubi_sb_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset
/* 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);
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_32bit(data->physical_offset + data->header_sizes_start + i*0x04, streamfile);
data->skip_size += read_u32(data->physical_offset + data->header_sizes_start + i*0x04, sf);
}
data->data_size = read_32bit(data->physical_offset + data->header_sizes_start + data->layer_number*0x04, streamfile);
data->data_size = read_u32(data->physical_offset + data->header_sizes_start + data->layer_number*0x04, sf);
}
if (data->data_size == 0) {
@ -78,13 +79,13 @@ static size_t ubi_sb_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset
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->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_32bit(data->physical_offset + data->block_sizes_start + i*0x04, streamfile);
data->skip_size += read_u32(data->physical_offset + data->block_sizes_start + i*0x04, sf);
}
data->data_size = read_32bit(data->physical_offset + data->block_sizes_start + data->layer_number*0x04, streamfile);
data->data_size = read_u32(data->physical_offset + data->block_sizes_start + data->layer_number*0x04, sf);
}
/* move to next block */
@ -105,7 +106,7 @@ static size_t ubi_sb_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t 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);
bytes_done = read_streamfile(dest, data->physical_offset + data->skip_size + bytes_consumed, to_read, sf);
total_read += bytes_done;
dest += bytes_done;
@ -121,34 +122,34 @@ static size_t ubi_sb_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset
return total_read;
}
static size_t ubi_sb_io_size(STREAMFILE *streamfile, ubi_sb_io_data* data) {
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(streamfile, buf, 0x7FFFFFFF, 1, data);
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 *streamfile, ubi_sb_io_data* data) {
int32_t (*read_32bit)(off_t,STREAMFILE*) = data->big_endian ? read_32bitBE : read_32bitLE;
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(streamfile)) {
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 = (uint32_t)read_32bit(offset+0x00, streamfile);
version = read_u32(offset+0x00, sf);
/* it was bound to happen... orz */
if (data->layer_hijack == 1 && version == 0x000B0008)
@ -167,7 +168,7 @@ static int ubi_sb_io_init(STREAMFILE *streamfile, ubi_sb_io_data* data) {
* 0x04: block offset
* 0x08+(04*N): layer size per layer
* 0xNN: layer data per layer */
data->layer_max = read_32bit(offset+0x04, streamfile);
data->layer_max = read_u32(offset+0x04, sf);
data->header_next_start = 0x10;
data->header_sizes_start = 0;
@ -192,7 +193,7 @@ static int ubi_sb_io_init(STREAMFILE *streamfile, ubi_sb_io_data* data) {
* 0x04: block offset
* 0x08+(04*N): layer size per layer
* 0xNN: layer data per layer */
data->layer_max = read_32bit(offset+0x04, streamfile);
data->layer_max = read_u32(offset+0x04, sf);
data->header_next_start = 0x10;
data->header_sizes_start = 0x1c;
@ -219,7 +220,7 @@ static int ubi_sb_io_init(STREAMFILE *streamfile, ubi_sb_io_data* data) {
* 0x08: always 0x03
* 0x0c+(04*N): layer size per layer
* 0xNN: layer data per layer */
data->layer_max = read_32bit(offset+0x04, streamfile);
data->layer_max = read_u32(offset+0x04, sf);
data->header_next_start = 0x14;
data->header_sizes_start = 0x20;
@ -248,7 +249,7 @@ static int ubi_sb_io_init(STREAMFILE *streamfile, ubi_sb_io_data* data) {
* 0x08: always 0x03
* 0x0c+(04*N): layer size per layer
* 0xNN: layer data per layer */
data->layer_max = read_32bit(offset+0x08, streamfile);
data->layer_max = read_u32(offset+0x08, sf);
data->header_next_start = 0x18;
data->header_sizes_start = 0x40;
@ -277,7 +278,7 @@ static int ubi_sb_io_init(STREAMFILE *streamfile, ubi_sb_io_data* data) {
* 0x08: always 0x03
* 0x0c+(04*N): layer size per layer
* 0xNN: layer data per layer */
data->layer_max = read_32bit(offset+0x08, streamfile);
data->layer_max = read_u32(offset+0x08, sf);
data->header_next_start = 0x18;
data->header_sizes_start = 0x4c;
@ -306,7 +307,7 @@ static int ubi_sb_io_init(STREAMFILE *streamfile, ubi_sb_io_data* data) {
* 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->layer_max = read_u32(offset+0x08, sf);
data->header_next_start = 0x18;
data->header_sizes_start = 0x1c;
@ -333,7 +334,7 @@ static int ubi_sb_io_init(STREAMFILE *streamfile, ubi_sb_io_data* data) {
* 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->layer_max = read_u32(offset+0x08, sf);
data->header_next_start = 0x18;
data->header_sizes_start = 0x5c;
@ -353,7 +354,7 @@ static int ubi_sb_io_init(STREAMFILE *streamfile, ubi_sb_io_data* data) {
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);
data->header_size += read_u32(offset + data->header_sizes_start + i*0x04, sf);
}
}
@ -386,10 +387,9 @@ fail:
/* 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;
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};
size_t io_data_size = sizeof(ubi_sb_io_data);
io_data.stream_offset = stream_offset;
io_data.stream_size = stream_size;
@ -398,26 +398,19 @@ static STREAMFILE* setup_ubi_sb_streamfile(STREAMFILE *streamFile, off_t stream_
io_data.big_endian = big_endian;
io_data.layer_hijack = layer_hijack;
if (!ubi_sb_io_init(streamFile, &io_data))
if (!ubi_sb_io_init(sf, &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;
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:
close_streamfile(temp_streamFile);
return NULL;
}

View file

@ -3,72 +3,76 @@
#include "../util.h"
/* BNS - Wii "Banner Sound" disc jingle */
VGMSTREAM * init_vgmstream_wii_bns(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
char filename[PATH_LIMIT];
off_t BNS_offset;
uint32_t info_offset=0,data_offset=0;
VGMSTREAM* init_vgmstream_wii_bns(STREAMFILE* sf) {
VGMSTREAM* vgmstream = NULL;
off_t bns_offset;
uint32_t info_offset = 0, data_offset = 0;
uint32_t channel_info_offset_list_offset;
int channel_count;
int loop_flag;
uint16_t sample_rate;
int channels, loop_flag, sample_rate;
uint32_t sample_count, loop_start;
/* check extension, case insensitive */
streamFile->get_name(streamFile,filename,sizeof(filename));
if (strcasecmp("bns",filename_extension(filename))) goto fail;
/* checks */
/* .bin: actual extension
* .bns: header id */
if (!check_extensions(sf, "bin,lbin,bns"))
goto fail;
// check header
BNS_offset = 0;
if (read_32bitBE(BNS_offset,streamFile) == 0x494D4435) // IMD5
{
// Skip IMD5 header if present
BNS_offset = 0x20;
bns_offset = 0;
if (is_id32be(bns_offset + 0x40, sf, "IMET")) {
/* regular .bnr, find sound.bin offset */
bns_offset = read_u32be(bns_offset + 0x44,sf);
/* tables, probably not ok for all cases */
bns_offset += read_u32be(bns_offset + 0x54,sf);
}
if (read_32bitBE(BNS_offset+0x00,streamFile) != 0x424E5320) goto fail; // "BNS "
if ((uint32_t)read_32bitBE(BNS_offset+0x04,streamFile) != 0xFEFF0100u) goto fail;
if (is_id32be(bns_offset + 0x00, sf, "IMD5")) {
/* skip IMD5 header if present */
bns_offset += 0x20;
}
// find chunks, verify
if (!is_id32be(bns_offset + 0x00,sf, "BNS "))
goto fail;
if (read_u32be(bns_offset + 0x04,sf) != 0xFEFF0100u)
goto fail;
/* find chunks */
{
// file size as claimed by header
uint32_t header_file_size = read_32bitBE(BNS_offset+0x08,streamFile);
uint32_t header_size = read_16bitBE(BNS_offset+0xc,streamFile);
uint16_t chunk_count = read_16bitBE(BNS_offset+0xe,streamFile);
uint32_t file_size = read_u32be(bns_offset+0x08,sf);
uint32_t header_size = read_u16be(bns_offset+0x0c,sf);
uint16_t chunk_count = read_u16be(bns_offset+0x0e,sf);
int i;
// assume BNS is the last thing in the file
if (header_file_size + BNS_offset != get_streamfile_size(streamFile))
/* assume BNS is the last thing in the file */
if (file_size + bns_offset != get_streamfile_size(sf))
goto fail;
for (i = 0; i < chunk_count; i++) {
uint32_t chunk_info_offset = BNS_offset+0x10+i*8;
uint32_t chunk_info_offset = bns_offset+0x10+i*0x08;
uint32_t chunk_offset, chunk_size;
// ensure chunk info is within header
if (chunk_info_offset+8 > BNS_offset+header_size) goto fail;
if (chunk_info_offset+8 > bns_offset+header_size) goto fail;
chunk_offset = BNS_offset + (uint32_t)read_32bitBE(chunk_info_offset,streamFile);
chunk_size = (uint32_t)read_32bitBE(chunk_info_offset+4,streamFile);
chunk_offset = bns_offset + (uint32_t)read_32bitBE(chunk_info_offset,sf);
chunk_size = (uint32_t)read_32bitBE(chunk_info_offset+4,sf);
// ensure chunk is within file
if (chunk_offset < BNS_offset+header_size ||
chunk_offset+chunk_size > BNS_offset+header_file_size) goto fail;
if (chunk_offset < bns_offset+header_size ||
chunk_offset+chunk_size > bns_offset+file_size) goto fail;
// ensure chunk size in header matches that listed in chunk
// Note: disabled for now, as the Homebrew Channel BNS has a DATA
// chunk that doesn't include the header size
//if ((uint32_t)read_32bitBE(chunk_offset+4,streamFile) != chunk_size) goto fail;
//if ((uint32_t)read_32bitBE(chunk_offset+4,sf) != chunk_size) goto fail;
// handle each chunk type
switch (read_32bitBE(chunk_offset,streamFile)) {
switch (read_32bitBE(chunk_offset,sf)) {
case 0x494E464F: // INFO
info_offset = chunk_offset+8;
info_offset = chunk_offset+0x08;
break;
case 0x44415441: // DATA
data_offset = chunk_offset+8;
data_offset = chunk_offset+0x08;
break;
default:
goto fail;
@ -82,72 +86,65 @@ VGMSTREAM * init_vgmstream_wii_bns(STREAMFILE *streamFile) {
/* parse out basic stuff in INFO */
{
/* only seen this zero, specifies DSP format? */
if (read_8bit(info_offset+0x00,streamFile) != 0) goto fail;
if (read_8bit(info_offset+0x00,sf) != 0) goto fail;
loop_flag = read_8bit(info_offset+0x01,streamFile);
channel_count = read_8bit(info_offset+0x02,streamFile);
loop_flag = read_8bit(info_offset+0x01,sf);
channels = read_8bit(info_offset+0x02,sf);
/* only seen zero, padding? */
if (read_8bit(info_offset+0x03,streamFile) != 0) goto fail;
if (read_8bit(info_offset+0x03,sf) != 0) goto fail;
sample_rate = (uint16_t)read_16bitBE(info_offset+0x04,streamFile);
sample_rate = read_u16be(info_offset+0x04,sf);
/* only seen this zero, padding? */
if (read_16bitBE(info_offset+0x06,streamFile) != 0) goto fail;
if (read_u16be(info_offset+0x06,sf) != 0) goto fail;
loop_start = read_32bitBE(info_offset+0x08,streamFile);
loop_start = read_s32be(info_offset+0x08,sf);
sample_count = read_32bitBE(info_offset+0x0c,streamFile);
sample_count = read_s32be(info_offset+0x0c,sf);
channel_info_offset_list_offset = info_offset + (uint32_t)read_32bitBE(info_offset+0x10,streamFile);
channel_info_offset_list_offset = info_offset + read_u32be(info_offset+0x10,sf);
}
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
vgmstream = allocate_vgmstream(channels, loop_flag);
if (!vgmstream) goto fail;
/* fill in the vital statistics */
vgmstream->channels = channel_count;
vgmstream->sample_rate = sample_rate;
vgmstream->coding_type = coding_NGC_DSP;
vgmstream->num_samples = sample_count;
vgmstream->layout_type = layout_none;
vgmstream->meta_type = meta_WII_BNS;
vgmstream->sample_rate = sample_rate;
vgmstream->num_samples = sample_count;
vgmstream->loop_start_sample = loop_start;
vgmstream->loop_end_sample = sample_count;
if (loop_flag)
{
vgmstream->loop_start_sample = loop_start;
vgmstream->loop_end_sample = sample_count;
}
vgmstream->coding_type = coding_NGC_DSP;
vgmstream->layout_type = layout_none;
//todo cleanup
/* open the file for reading */
{
int i;
for (i=0;i<channel_count;i++) {
uint32_t channel_info_offset = info_offset + read_32bitBE(channel_info_offset_list_offset+4*i,streamFile);
uint32_t channel_data_offset = data_offset + (uint32_t)read_32bitBE(channel_info_offset+0,streamFile);
uint32_t channel_dsp_offset = info_offset + (uint32_t)read_32bitBE(channel_info_offset+4,streamFile);
int j;
int i, j;
for (i = 0; i < channels; i++) {
uint32_t channel_info_offset = info_offset + read_u32be(channel_info_offset_list_offset + 0x04*i,sf);
uint32_t channel_data_offset = data_offset + read_u32be(channel_info_offset + 0x00,sf);
uint32_t channel_dsp_offset = info_offset + read_u32be(channel_info_offset + 0x04,sf);
/* always been 0... */
if (read_32bitBE(channel_info_offset+8,streamFile) != 0) goto fail;
if (read_u32be(channel_info_offset + 0x8,sf) != 0) goto fail;
vgmstream->ch[i].streamfile = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE);
vgmstream->ch[i].streamfile = reopen_streamfile(sf, 0);
if (!vgmstream->ch[i].streamfile) goto fail;
vgmstream->ch[i].channel_start_offset = vgmstream->ch[i].offset = channel_data_offset;
vgmstream->ch[i].channel_start_offset=
vgmstream->ch[i].offset=channel_data_offset;
for (j=0;j<16;j++)
vgmstream->ch[i].adpcm_coef[j] =
read_16bitBE(channel_dsp_offset+j*2,streamFile);
for (j = 0; j < 16; j++) {
vgmstream->ch[i].adpcm_coef[j] = read_s16be(channel_dsp_offset + j*0x02,sf);
}
}
}
//if (!vgmstream_open_stream(vgmstream, sf, start_offset))
// goto fail;
return vgmstream;
/* clean up anything we may have opened */
fail:
if (vgmstream) close_vgmstream(vgmstream);
close_vgmstream(vgmstream);
return NULL;
}

View file

@ -9,7 +9,7 @@
* Some info: https://www.audiokinetic.com/en/library/edge/
* .bnk (dynamic music/loop) info: https://github.com/bnnm/wwiser
*/
typedef enum { PCM, IMA, VORBIS, DSP, XMA2, XWMA, AAC, HEVAG, ATRAC9, OPUSNX, OPUS, OPUSWW, PTADPCM } wwise_codec;
typedef enum { PCM, IMA, VORBIS, DSP, XMA2, XWMA, AAC, HEVAG, ATRAC9, OPUSNX, OPUS, OPUSCPR, OPUSWW, PTADPCM } wwise_codec;
typedef struct {
int big_endian;
size_t file_size;
@ -492,6 +492,22 @@ VGMSTREAM* init_vgmstream_wwise(STREAMFILE* sf) {
break;
}
case OPUSCPR: { /* CD Project RED's Ogg Opus masquerading as PCMEX [Cyberpunk 2077 (PC)] */
if (ww.bits_per_sample != 16) goto fail;
/* original data_size doesn't look related to samples or anything */
ww.data_size = ww.file_size - ww.data_offset;
vgmstream->codec_data = init_ffmpeg_offset(sf, ww.data_offset, ww.data_size);
if (!vgmstream->codec_data) goto fail;
vgmstream->coding_type = coding_FFmpeg;
vgmstream->layout_type = layout_none;
/* FFmpeg's samples seem correct, otherwise see ogg_opus.c for getting samples. */
vgmstream->num_samples = (int32_t)((ffmpeg_codec_data*)vgmstream->codec_data)->totalSamples;
break;
}
case OPUSWW: { /* updated Opus [Assassin's Creed Valhalla (PC)] */
int mapping;
opus_config cfg = {0};
@ -884,6 +900,16 @@ static int parse_wwise(STREAMFILE* sf, wwise_header* ww) {
}
}
/* Cyberpunk 2077 has some mutant .wem, with proper Wwise header and PCMEX but data is standard OPUS.
* From init bank and CAkSound's sources, those may be piped through their plugins. They come in
* .opuspak (no names), have wrong riff/data sizes and only seem used for sfx (other audio is Vorbis). */
if (ww->format == 0xFFFE && ww->truncated) {
if (read_u32be(ww->data_offset + 0x00, sf) == 0x4F676753) {
ww->codec = OPUSCPR;
}
}
return 1;
fail:
return 0;

View file

@ -178,7 +178,7 @@ VGMSTREAM *init_vgmstream_xmv_valve(STREAMFILE *streamFile) {
break;
}
#endif
case 0x02: /* ADPCM (not actually implemented, was probably supposed to be Microsoft ADPCM) */
case 0x02: /* ADPCM (not actually implemented, was probably supposed to be Microsoft ADPCM or Xbox IMA) */
default:
goto fail;
}

View file

@ -664,7 +664,7 @@ void mixing_push_fade(VGMSTREAM* vgmstream, int ch_dst, double vol_start, double
* pre1 start1 end1 post1
* - when pre and post are set nothing is done (fade is exact and multiple fades may overlap)
* - when previous fade's post or current fade's pre are negative (meaning file end/start)
* they should cancel each other (to allow chaning fade-in + fade-out + fade-in + etc):
* they should cancel each other (to allow chaining fade-in + fade-out + fade-in + etc):
* <----------|----------|----------| |----------|----------|---------->
* pre1 start1 end1 post1 pre2 start2 end2 post2
* - other cases (previous fade is actually after/in-between current fade) are ignored

View file

@ -81,7 +81,7 @@ void vgmstream_get_title(char* buf, int buf_len, const char* filename, VGMSTREAM
strncpy(buf, pos, buf_len);
/* name without extension */
if (cfg->remove_extension) {
if (cfg && cfg->remove_extension) {
pos2 = strrchr(buf, '.');
if (pos2 && strlen(pos2) < 15) /* too big extension = file name probably has a dot in the middle */
pos2[0] = '\0';
@ -167,14 +167,14 @@ static void load_default_config(play_config_t* def, play_config_t* tcfg) {
def->fade_time_set = 1;
}
/* loop priority: #i > #e > #E */
/* loop priority: #i > #e > #E (respect player's ignore too) */
if (tcfg->really_force_loop) {
def->ignore_loop = 0;
//def->ignore_loop = 0;
def->force_loop = 0;
def->really_force_loop = 1;
}
if (tcfg->force_loop) {
def->ignore_loop = 0;
//def->ignore_loop = 0;
def->force_loop = 1;
def->really_force_loop = 0;
}

View file

@ -307,6 +307,10 @@ static inline int min_s32(int32_t a, int32_t b) { return a < b ? a : b; }
//align32, align16, clamp16, etc
#endif
static inline const int is_id32be(off_t offset, STREAMFILE* sf, const char* s) {
return read_u32be(offset, sf) == get_id32be(s);
}
//TODO: maybe move to streamfile.c
/* guess byte endianness from a given value, return true if big endian and false if little endian */
static inline int guess_endianness16bit(off_t offset, STREAMFILE* sf) {

View file

@ -98,6 +98,10 @@ static inline int clamp16(int32_t val) {
else return val;
}
static inline const uint32_t get_id32be(const char* s) {
return (uint32_t)(s[0] << 24) | (s[1] << 16) | (s[2] << 8) | (s[3] << 0);
}
/* less common functions, no need to inline */
int round10(int val);

View file

@ -200,7 +200,7 @@ VGMSTREAM* (*init_vgmstream_functions[])(STREAMFILE* sf) = {
init_vgmstream_redspark,
init_vgmstream_ivaud,
init_vgmstream_wii_wsd,
init_vgmstream_wii_ndp,
init_vgmstream_dsp_ndp,
init_vgmstream_ps2_sps,
init_vgmstream_ps2_xa2_rrp,
init_vgmstream_nds_hwas,
@ -512,6 +512,10 @@ VGMSTREAM* (*init_vgmstream_functions[])(STREAMFILE* sf) = {
init_vgmstream_xws,
init_vgmstream_cpk,
init_vgmstream_opus_nsopus,
init_vgmstream_sbk,
init_vgmstream_dsp_wiiadpcm,
init_vgmstream_dsp_cwac,
init_vgmstream_ifs,
/* lowest priority metas (should go after all metas, and TXTH should go before raw formats) */
init_vgmstream_txth, /* proper parsers should supersede TXTH, once added */

View file

@ -146,6 +146,7 @@ typedef enum {
coding_REF_IMA, /* Reflections IMA ADPCM */
coding_AWC_IMA, /* Rockstar AWC IMA ADPCM */
coding_UBI_IMA, /* Ubisoft IMA ADPCM */
coding_UBI_SCE_IMA, /* Ubisoft SCE IMA ADPCM */
coding_H4M_IMA, /* H4M IMA ADPCM (stereo or mono, high nibble first) */
coding_MTF_IMA, /* Capcom MT Framework IMA ADPCM */
coding_CD_IMA, /* Crystal Dynamics IMA ADPCM */
@ -745,6 +746,9 @@ typedef enum {
meta_WADY,
meta_DSP_SQEX,
meta_DSP_WIIVOICE,
meta_SBK,
meta_DSP_WIIADPCM,
meta_DSP_CWAC,
} meta_t;
/* standard WAVEFORMATEXTENSIBLE speaker positions */