diff --git a/src/formats.c b/src/formats.c index 25fb3af8..a9e4a88e 100644 --- a/src/formats.c +++ b/src/formats.c @@ -118,6 +118,7 @@ static const char* extension_list[] = { "bo2", "brstm", "brstmspm", + "bsnd", "btsnd", "bvg", "bwav", @@ -1336,6 +1337,7 @@ static const meta_info meta_info_list[] = { {meta_COMPRESSWAVE, "CompressWave .cwav header"}, {meta_KTAC, "Koei Tecmo KTAC header"}, {meta_MJB_MJH, "Sony MultiStream MJH+MJB header"}, + {meta_BSNF, "id Software BSNF header"}, }; void get_vgmstream_coding_description(VGMSTREAM* vgmstream, char* out, size_t out_size) { diff --git a/src/meta/idtech.c b/src/meta/idtech.c index 75096a4c..4db55770 100644 --- a/src/meta/idtech.c +++ b/src/meta/idtech.c @@ -1,42 +1,45 @@ #include "meta.h" #include "../coding/coding.h" #include "idtech_streamfile.h" +#include -/* mzrt - idTech "4.5" audio found in .resource bigfiles (w/ internal filenames) [Doom 3 BFG edition (PC/PS3/X360)] */ -VGMSTREAM * init_vgmstream_mzrt(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; +/* mzrt - id Tech 4.5 audio found in .resource bigfiles (w/ internal filenames) [Doom 3 BFG edition (PC/PS3/X360)] */ +VGMSTREAM* init_vgmstream_mzrt_v0(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; off_t start_offset; - int loop_flag = 0, channel_count, codec, sample_rate, block_size = 0, bps = 0, num_samples; - STREAMFILE *temp_streamFile = NULL; + int loop_flag = 0, channels, codec, sample_rate, block_size = 0, bps = 0, num_samples; + STREAMFILE* temp_sf = NULL; /* checks */ - if (!check_extensions(streamFile, "idwav,idmsf,idxma")) + if (!check_extensions(sf, "idwav,idmsf,idxma")) goto fail; - if (read_32bitBE(0x00,streamFile) != 0x6D7A7274) /* "mzrt" */ + if (!is_id32be(0x00,sf, "mzrt")) /* null-terminated string */ + goto fail; + if (read_u32be(0x05, sf) != 0x00000000) /* version */ goto fail; /* this format is bizarrely mis-aligned (and mis-designed too) */ - num_samples = read_32bitBE(0x11,streamFile); - codec = read_16bitLE(0x15,streamFile); + num_samples = read_s32le(0x11,sf); + codec = read_u16le(0x15,sf); switch(codec) { case 0x0001: case 0x0002: case 0x0166: - channel_count = read_16bitLE(0x17,streamFile); - sample_rate = read_32bitLE(0x19, streamFile); - block_size = read_16bitLE(0x21, streamFile); - bps = read_16bitLE(0x23,streamFile); + channels = read_u16le(0x17,sf); + sample_rate = read_u32le(0x19, sf); + block_size = read_u16le(0x21, sf); + bps = read_u16le(0x23,sf); start_offset = 0x25; break; case 0x0000: - sample_rate = read_32bitBE(0x1D, streamFile); - channel_count = read_32bitBE(0x21, streamFile); + sample_rate = read_u32be(0x1D, sf); + channels = read_u32be(0x21, sf); start_offset = 0x29; break; @@ -47,24 +50,24 @@ VGMSTREAM * init_vgmstream_mzrt(STREAMFILE *streamFile) { /* skip MSADPCM data */ if (codec == 0x0002) { - if (!msadpcm_check_coefs(streamFile, start_offset + 0x02 + 0x02)) + if (!msadpcm_check_coefs(sf, start_offset + 0x02 + 0x02)) goto fail; - start_offset += 0x02 + read_16bitLE(start_offset, streamFile); + start_offset += 0x02 + read_u16le(start_offset, sf); } /* skip extra data */ if (codec == 0x0166) { - start_offset += 0x02 + read_16bitLE(start_offset, streamFile); + start_offset += 0x02 + read_u16le(start_offset, sf); } /* skip unknown table */ if (codec == 0x0000) { - start_offset += 0x04 + read_32bitBE(start_offset, streamFile) * 0x04; + start_offset += 0x04 + read_u32be(start_offset, sf) * 0x04; } /* skip unknown table */ - start_offset += 0x04 + read_32bitBE(start_offset, streamFile); + start_offset += 0x04 + read_u32be(start_offset, sf); /* skip block info */ if (codec != 0x0000) { @@ -73,8 +76,8 @@ VGMSTREAM * init_vgmstream_mzrt(STREAMFILE *streamFile) { start_offset += 0x08; /* idwav only uses 1 super-block though */ - temp_streamFile = setup_mzrt_streamfile(streamFile, start_offset); - if (!temp_streamFile) goto fail; + temp_sf = setup_mzrt_streamfile(sf, start_offset); + if (!temp_sf) goto fail; } else { /* 0x00: de-blocked size */ @@ -83,7 +86,7 @@ VGMSTREAM * init_vgmstream_mzrt(STREAMFILE *streamFile) { /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count, loop_flag); + vgmstream = allocate_vgmstream(channels, loop_flag); if (!vgmstream) goto fail; vgmstream->meta_type = meta_MZRT; @@ -95,7 +98,7 @@ VGMSTREAM * init_vgmstream_mzrt(STREAMFILE *streamFile) { if (bps != 16) goto fail; vgmstream->coding_type = coding_PCM16LE; vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = block_size / channel_count; + vgmstream->interleave_block_size = block_size / channels; break; case 0x0002: @@ -109,15 +112,15 @@ VGMSTREAM * init_vgmstream_mzrt(STREAMFILE *streamFile) { case 0x0166: { uint8_t buf[0x100]; int bytes; - size_t stream_size = get_streamfile_size(temp_streamFile); + size_t stream_size = get_streamfile_size(temp_sf); - bytes = ffmpeg_make_riff_xma_from_fmt_chunk(buf,sizeof(buf), 0x15,0x34, stream_size, streamFile, 0); - vgmstream->codec_data = init_ffmpeg_header_offset(temp_streamFile, buf,bytes, 0x00,stream_size); + bytes = ffmpeg_make_riff_xma_from_fmt_chunk(buf,sizeof(buf), 0x15,0x34, stream_size, sf, 0); + vgmstream->codec_data = init_ffmpeg_header_offset(temp_sf, buf,bytes, 0x00,stream_size); if (!vgmstream->codec_data) goto fail; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; - xma_fix_raw_samples_hb(vgmstream, streamFile, temp_streamFile, 0x00,stream_size, 0x15, 0,0); + xma_fix_raw_samples_hb(vgmstream, sf, temp_sf, 0x00, stream_size, 0x15, 0,0); break; } #endif @@ -128,7 +131,7 @@ VGMSTREAM * init_vgmstream_mzrt(STREAMFILE *streamFile) { cfg.skip_samples = 576; /* assumed */ - vgmstream->codec_data = init_mpeg_custom(streamFile, start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_STANDARD, &cfg); + vgmstream->codec_data = init_mpeg_custom(sf, start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_STANDARD, &cfg); if (!vgmstream->codec_data) goto fail; vgmstream->layout_type = layout_none; break; @@ -139,13 +142,346 @@ VGMSTREAM * init_vgmstream_mzrt(STREAMFILE *streamFile) { goto fail; } - if (!vgmstream_open_stream(vgmstream,temp_streamFile == NULL ? streamFile : temp_streamFile,temp_streamFile == NULL ? start_offset : 0x00)) + if (!vgmstream_open_stream(vgmstream,temp_sf == NULL ? sf : temp_sf, temp_sf == NULL ? start_offset : 0x00)) goto fail; - close_streamfile(temp_streamFile); + close_streamfile(temp_sf); return vgmstream; fail: - close_streamfile(temp_streamFile); + close_streamfile(temp_sf); + close_vgmstream(vgmstream); + return NULL; +} + +/* mzrt - id Tech 5 audio [Rage (PS3), The Evil Within (PS3)] */ +VGMSTREAM* init_vgmstream_mzrt_v1(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + off_t start_offset, offset; + size_t stream_size; + int loop_flag, channels, codec, type, sample_rate, block_size = 0, bps = 0; + int32_t num_samples, loop_start = 0; + STREAMFILE* sb = NULL; + const char* extension = NULL; + + + /* checks */ + if (!check_extensions(sf, "idmsf")) //idmsa: untested + goto fail; + + if (!is_id32be(0x00,sf, "mzrt")) /* null-terminated string */ + goto fail; + if (read_u32be(0x05, sf) != 0x00000100) /* version */ + goto fail; + + type = read_s32be(0x09,sf); + if (type == 0) { /* Rage */ + /* 0x0d: null */ + /* 0x11: crc? */ + /* 0x15: flag? */ + offset = 0x19; + } + else { /* TEW */ + offset = 0x0D; + } + + stream_size = read_u32be(offset + 0x00,sf); + offset = read_u32be(offset + 0x04,sf); /* absolute but typically right after this */ + + /* 0x00: crc? */ + codec = read_u8(offset + 0x04,sf); /* assumed */ + switch(codec) { + case 0x00: { + /* 0x05: null? */ + num_samples = read_s32be(offset + 0x09,sf); + /* 0x0D: null? */ + /* 0x11: loop related? */ + /* 0x1d: stream size? */ + /* others: ? */ + + /* fmt at 0x31 (codec, avg bitrate, etc) */ + channels = read_u16le(offset + 0x33, sf); + sample_rate = read_u32le(offset + 0x35, sf); + block_size = read_u16le(offset + 0x3d, sf); + bps = read_u16le(offset + 0x3f, sf); + + /* 0x41: MSADPCM fmt extra */ + if (!msadpcm_check_coefs(sf, offset + 0x41 + 0x02 + 0x02)) + goto fail; + + extension = "msadpcm"; + break; + } + + case 0x01: { + uint32_t table_entries; + + /* 0x05: stream size? */ + num_samples = read_s32be(offset + 0x09,sf); + /* 0x0D: 0x40? */ + /* 0x11: loop related? */ + loop_start = read_s32be(offset + 0x15,sf); + /* 0x19: null */ + table_entries = read_u32be(offset + 0x1d,sf); + + /* skip seek table, format: frame size (16b) + frame samples (16b) + * (first entry may be 0 then next entry x2 samples to mimic encoder latency?) */ + offset += 0x21 + table_entries * 0x04; + + sample_rate = read_u32be(offset + 0x00, sf); + channels = read_u32be(offset + 0x04, sf); + /* 0x0c: MSF codec */ + + extension = type == 0 ? "msadpcm" : "msf"; + break; + } + default: + goto fail; + } + + sb = open_streamfile_by_ext(sf, extension); + if (!sb) goto fail; + + if (stream_size != get_streamfile_size(sb)) + goto fail; + + + loop_flag = (loop_start > 0); + start_offset = 0x00; + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channels, loop_flag); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_MZRT; + vgmstream->sample_rate = sample_rate; + vgmstream->num_samples = num_samples; + vgmstream->loop_start_sample = loop_start; + vgmstream->loop_end_sample = num_samples; + + switch(codec) { + case 0x0002: + if (bps != 4) goto fail; + vgmstream->coding_type = coding_MSADPCM; + vgmstream->layout_type = layout_none; + vgmstream->frame_size = block_size; + break; + +#ifdef VGM_USE_MPEG + case 0x01: { + mpeg_custom_config cfg = {0}; + + cfg.skip_samples = 1152; /* seems ok */ + + vgmstream->codec_data = init_mpeg_custom(sb, start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_STANDARD, &cfg); + if (!vgmstream->codec_data) goto fail; + vgmstream->layout_type = layout_none; + + vgmstream->num_samples -= cfg.skip_samples; + vgmstream->loop_start_sample -= cfg.skip_samples; + vgmstream->loop_end_sample -= cfg.skip_samples; + break; + } +#endif + + default: + goto fail; + } + + if (!vgmstream_open_stream(vgmstream, sb, start_offset)) + goto fail; + close_streamfile(sb); + return vgmstream; + +fail: + close_streamfile(sb); + close_vgmstream(vgmstream); + return NULL; +} + +/* bsnf - id Tech 5 audio [Wolfenstein: The New Order (multi), Wolfenstein: The Old Blood (PS4)] */ +VGMSTREAM* init_vgmstream_bsnf(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + off_t start_offset, offset, extra_offset; + size_t stream_size; + int loop_flag, channels, codec, sample_rate; //, block_size = 0, bps = 0; + int32_t num_samples, loop_start = 0; + STREAMFILE* sb = NULL; + const char* suffix = NULL; + const char* extension = NULL; + + + /* checks */ + if (!check_extensions(sf, "bsnd")) + goto fail; + + if (!is_id32be(0x00,sf, "bsnf")) /* null-terminated string */ + goto fail; + if (read_u32be(0x05, sf) != 0x00000100) /* version */ + goto fail; + + offset = 0x18; + + stream_size = read_u32be(offset + 0x00,sf); + offset = read_u32be(offset + 0x04,sf); /* absolute but typically right after this */ + + /* 0x00: crc? */ + /* 0x04: CBR samples or 0 if VBR */ + num_samples = read_s32be(offset + 0x08,sf); + loop_start = read_s32be(offset + 0x0c,sf); + /* 0x10: stream size? */ + + codec = read_u16le(offset + 0x14,sf); + channels = read_u16le(offset + 0x16, sf); + sample_rate = read_u32le(offset + 0x18, sf); + //block_size = read_u16le(offset + 0x20, sf); + //bps = read_u16le(offset + 0x22, sf); + + extra_offset = offset + 0x24; + extension = "ogg"; /* same for all codecs */ + switch(codec) { + case 0x0055: /* msf */ + /* 0x00: table entries */ + /* 0x04: seek table, format: frame size (16b) + frame samples (16b) */ + suffix = "_msf.bsnd"; + break; + + case 0x0166: /* xma */ + /* 0x00: extra size */ + /* 0x02: xma config and block table */ + suffix = "_xma.bsnd"; + break; + + case 0x674F: /* vorbis */ + /* 0x00: extra size */ + /* 0x02: num samples */ + suffix = "_vorbis.bsnd"; + goto fail; //untested + //break; + + case 0x42D2: /* at9 */ + /* 0x00: extra size */ + /* 0x02: encoder delay */ + /* 0x04: channel config */ + /* 0x08: ATRAC9 GUID */ + /* 0x1c: ATRAC9 config */ + suffix = "_at9.bsnd"; + break; + + default: + goto fail; + } + + { + int suffix_len = strlen(suffix); + int filename_len; + char filename[PATH_LIMIT]; + + get_streamfile_filename(sf, filename, sizeof(filename)); + filename_len = strlen(filename); + + if (filename_len < suffix_len) + goto fail; + filename[filename_len - suffix_len + 0] = '.'; + filename[filename_len - suffix_len + 1] = '\0'; + strcat(filename, extension); + + sb = open_streamfile_by_filename(sf, filename); + if (!sb) goto fail; + } + + if (stream_size != get_streamfile_size(sb)) + goto fail; + + loop_flag = (loop_start > 0); + start_offset = 0x00; + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channels, loop_flag); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_BSNF; + vgmstream->sample_rate = sample_rate; + vgmstream->num_samples = num_samples; + vgmstream->loop_start_sample = loop_start; + vgmstream->loop_end_sample = num_samples; + + switch(codec) { + +#ifdef VGM_USE_MPEG + case 0x0055: { + mpeg_custom_config cfg = {0}; + + cfg.skip_samples = 1152; /* seems ok */ + + vgmstream->codec_data = init_mpeg_custom(sb, start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_STANDARD, &cfg); + if (!vgmstream->codec_data) goto fail; + vgmstream->layout_type = layout_none; + + vgmstream->num_samples -= cfg.skip_samples; + vgmstream->loop_start_sample -= cfg.skip_samples; + vgmstream->loop_end_sample -= cfg.skip_samples; + break; + } +#endif + +#ifdef VGM_USE_FFMPEG + case 0x0166: { + uint8_t buf[0x100]; + size_t bytes, block_size, block_count; + + block_size = 0x800; + block_count = stream_size / block_size; + + bytes = ffmpeg_make_riff_xma2(buf, sizeof(buf), num_samples, stream_size, channels, sample_rate, block_count, block_size); + vgmstream->codec_data = init_ffmpeg_header_offset(sb, buf, bytes, start_offset, stream_size); + if (!vgmstream->codec_data) goto fail; + vgmstream->coding_type = coding_FFmpeg; + vgmstream->layout_type = layout_none; + + xma_fix_raw_samples(vgmstream, sb, start_offset, stream_size, 0x00, 1,1); + break; + } +#endif + +#ifdef VGM_USE_VORBIS + case 0x674F: { + vgmstream->codec_data = init_ogg_vorbis(sb, start_offset, stream_size, NULL); + if (!vgmstream->codec_data) goto fail; + vgmstream->coding_type = coding_OGG_VORBIS; + vgmstream->layout_type = layout_none; + break; + } +#endif + +#ifdef VGM_USE_ATRAC9 + case 0x42D2: { + atrac9_config cfg = {0}; + + cfg.channels = vgmstream->channels; + cfg.encoder_delay = read_u16le(extra_offset + 0x02,sf); + cfg.config_data = read_u32be(extra_offset + 0x1c,sf); + + vgmstream->codec_data = init_atrac9(&cfg); + if (!vgmstream->codec_data) goto fail; + vgmstream->coding_type = coding_ATRAC9; + vgmstream->layout_type = layout_none; + break; + } +#endif + + default: + goto fail; + } + + if (!vgmstream_open_stream(vgmstream, sb, start_offset)) + goto fail; + close_streamfile(sb); + return vgmstream; + +fail: + close_streamfile(sb); close_vgmstream(vgmstream); return NULL; } diff --git a/src/meta/meta.h b/src/meta/meta.h index ecbe94c0..77f259f7 100644 --- a/src/meta/meta.h +++ b/src/meta/meta.h @@ -851,7 +851,9 @@ VGMSTREAM * init_vgmstream_rad(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_smk(STREAMFILE * streamFile); -VGMSTREAM * init_vgmstream_mzrt(STREAMFILE * streamFile); +VGMSTREAM* init_vgmstream_mzrt_v0(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_mzrt_v1(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_bsnf(STREAMFILE* sf); VGMSTREAM * init_vgmstream_xavs(STREAMFILE * streamFile); diff --git a/src/vgmstream.c b/src/vgmstream.c index 031a4945..0e52a00f 100644 --- a/src/vgmstream.c +++ b/src/vgmstream.c @@ -463,7 +463,7 @@ VGMSTREAM* (*init_vgmstream_functions[])(STREAMFILE* sf) = { init_vgmstream_acb, init_vgmstream_rad, init_vgmstream_smk, - init_vgmstream_mzrt, + init_vgmstream_mzrt_v0, init_vgmstream_xavs, init_vgmstream_psf_single, init_vgmstream_psf_segmented, @@ -520,6 +520,8 @@ VGMSTREAM* (*init_vgmstream_functions[])(STREAMFILE* sf) = { init_vgmstream_compresswave, init_vgmstream_ktac, init_vgmstream_mjb_mjh, + init_vgmstream_mzrt_v1, + init_vgmstream_bsnf, /* 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 */ diff --git a/src/vgmstream.h b/src/vgmstream.h index 9e401296..86975a01 100644 --- a/src/vgmstream.h +++ b/src/vgmstream.h @@ -753,6 +753,7 @@ typedef enum { meta_COMPRESSWAVE, meta_KTAC, meta_MJB_MJH, + meta_BSNF, } meta_t; /* standard WAVEFORMATEXTENSIBLE speaker positions */