diff --git a/doc/FORMATS.md b/doc/FORMATS.md index 32631210..dc1a2662 100644 --- a/doc/FORMATS.md +++ b/doc/FORMATS.md @@ -185,7 +185,7 @@ different internally (encrypted, different versions, etc) and not always can be - Sony VAG header (custom) [*VAG_custom*] - Sony VAG header [*VAG*] - Acclaim Austin AAAp header [*AAAP*] - - *vag*: `.vag .swag .str .vig .l .r .vas .xa2 .snd` + - *vag*: `.vag .swag .str .vig .l .r .vas .xa2 .snd .svg` - *vag_aaap*: `.vag` - Codecs: PSX HEVAG - **ild.c** @@ -201,7 +201,7 @@ different internally (encrypted, different versions, etc) and not always can be - Electronic Arts SCHl header [*EA_SCHL*] - *ea_schl*: `.asf .lasf .str .chk .eam .exa .sng .aud .sx .xa .strm .stm .hab .xsf .gsf .(extensionless)` - *ea_schl_video*: `.uv .dct .mad .wve .vp6` - - *ea_bnk*: `.bnk .sdt .hdt .ldt .abk .ast` + - *ea_bnk*: `.bnk .sdt .hdt .ldt .abk .ast .cat` - *ea_abk*: `.abk + .ast` - *ea_hdr_dat*: `.hdr + .dat` - Subfiles: *vag* @@ -1299,7 +1299,7 @@ different internally (encrypted, different versions, etc) and not always can be - Sony BNK header [*BNK_SONY*] - *bnk_sony*: `.bnk` - Subfiles: *riff* - - Codecs: ATRAC9 PCM16BE PCM16LE PSX HEVAG + - Codecs: ATRAC9 MPEG PCM16BE PCM16LE PSX HEVAG - **nus3bank.c** - (container) - *nus3bank*: `.nub2 .nus3bank` @@ -1798,9 +1798,13 @@ different internally (encrypted, different versions, etc) and not always can be - Sony SNDS header [*SNDS*] - Codecs: ATRAC9 - **nxof.c** - - Nihon Falcom FDK Opus Header [*NXOF*] + - Nihon Falcom FDK header [*NXOF*] - *nxof*: `.nxopus` - Codecs: Opus +- **gwb_gwd.c** + - Ubisoft GWB+GWD header [*GWB_GWD*] + - *gwb_gwd*: `.gwb + .gwd` + - Codecs: NGC_DSP - **scd_pcm.c** - Lunar: Eternal Blue .PCM header [*SCD_PCM*] - *scd_pcm*: `.pcm` @@ -1851,7 +1855,7 @@ different internally (encrypted, different versions, etc) and not always can be - Codecs: NGC_DTK - **mpeg.c** - MPEG header [*MPEG*] - - *mpeg*: `.mp3 .mp2 .lmp3 .lmp2 .mus .imf .aix .(extensionless)` + - *mpeg*: `.mp3 .mp2 .lmp3 .lmp2 .mus .imf .aix .wav .lwav .(extensionless)` - Codecs: MPEG - **btsnd.c** - Nintendo Wii U Menu Boot Sound header [*BTSND*] diff --git a/src/formats.c b/src/formats.c index 12bcaa15..18995664 100644 --- a/src/formats.c +++ b/src/formats.c @@ -212,6 +212,7 @@ static const char* extension_list[] = { "gsf", "gsp", "gtd", + "gwb", "gwm", "h4m", @@ -1422,7 +1423,8 @@ static const meta_info meta_info_list[] = { {meta_SQUEAKSTREAM, "Torus SqueakStream header"}, {meta_SQUEAKSAMPLE, "Torus SqueakSample header"}, {meta_SNDS, "Sony SNDS header"}, - {meta_NXOF, "Nihon Falcom FDK Opus Header"}, + {meta_NXOF, "Nihon Falcom FDK header"}, + {meta_GWB_GWD, "Ubisoft GWB+GWD header"}, }; void get_vgmstream_coding_description(VGMSTREAM* vgmstream, char* out, size_t out_size) { diff --git a/src/libvgmstream.vcxproj b/src/libvgmstream.vcxproj index f2c6d3bd..209a982f 100644 --- a/src/libvgmstream.vcxproj +++ b/src/libvgmstream.vcxproj @@ -446,6 +446,7 @@ + diff --git a/src/libvgmstream.vcxproj.filters b/src/libvgmstream.vcxproj.filters index bc571366..502cf5b9 100644 --- a/src/libvgmstream.vcxproj.filters +++ b/src/libvgmstream.vcxproj.filters @@ -1159,6 +1159,9 @@ meta\Source Files + + meta\Source Files + meta\Source Files diff --git a/src/meta/gwb_gwd.c b/src/meta/gwb_gwd.c new file mode 100644 index 00000000..7cc95777 --- /dev/null +++ b/src/meta/gwb_gwd.c @@ -0,0 +1,149 @@ +#include "meta.h" +#include "../coding/coding.h" + + +/* GWB+GWD - Ubisoft bank [Monster 4x4: World Circuit (Wii)] */ +VGMSTREAM* init_vgmstream_gwb_gwd(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + STREAMFILE* sf_body = NULL; + uint32_t stream_offset = 0, stream_size = 0, coef_offset; + int loop_flag, channels, sample_rate, interleave = 0; + uint32_t loop_start, loop_end; + int total_subsongs, target_subsong = sf->stream_index; + + + /* checks */ + int version = read_u8(0x00, sf); + if (version != 6 && version != 7) + return NULL; + if (read_u32be(0x01, sf) > 0x0400) /* ID, max seen */ + return NULL; + if (get_streamfile_size(sf) > 0x2000) /* arbitrary max */ + return NULL; + if (!check_extensions(sf,"gwb")) + return NULL; + + /* format (vaguely similar to ubi's hx and such banks) + * common + * 00: version (06/07, both found in the same game) + * 01: file ID (low number: 0x0001, 0x0342...) + * v6: + * 05: subsongs + * v7 + * 05: null + * 09: subsongs + * + * per subsong: + * - 00: flags: (v6: 09=stereo, 02=mono; v7: 0a=stereo) + * - 01: id (ex. 0x0002, 0x0343...) + * v6 + * - 05: 0x4a header * channels + * v7 + * - 05: always 0x02? + * - 09: stream offset + * - 0d: stream size + * - 11: always 5 + * - 15: 0x4a header * channels + * + * per header: + * - 00: loop flag + * - 04: sample rate + * - 08: loop start nibbles + * - 0c: loop end nibbles + * - 10: end nibble + * - 14: start nibble (after DSP frame header, so uses 0x02 at file start) + * - 18: null + * - 1c: coefs + gain + initial ps/hists + loop ps/hists + * Data in .gwd is N headerless DSPs. All nibble values are absolute within the file. */ + + uint32_t offset = version == 6 ? 0x05 : 0x09; + + total_subsongs = read_s32be(offset, sf); + if (target_subsong == 0) target_subsong = 1; + if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) return NULL; + offset += 0x04; + + /* find target header */ + for (int i = 0; i < total_subsongs; i++) { + if (i + 1 == target_subsong) + break; + + uint8_t type = read_u8(offset + 0x00, sf); + if (type != 0x0a && type != 0x09 && type != 0x02) + goto fail; + + offset += 0x05 + (version == 7 ? 0x10 : 0); + offset += 0x4a * (type & 0x08 ? 2 : 1); + } + + /* header */ + { + uint32_t st_nibble, ed_nibble, ls_nibble, le_nibble; + uint8_t type = read_u8(offset + 0x00, sf); + channels = (type & 0x08 ? 2 : 1); + + offset += 0x05; + if (version == 7) { + stream_offset = read_u32be(offset + 0x04, sf); + stream_size = read_u32be(offset + 0x08, sf); + interleave = 0x4000; + offset += 0x10; + } + loop_flag = read_u32be(offset + 0x00, sf) == 1; + sample_rate = read_u32be(offset + 0x04, sf); + ls_nibble = read_u32be(offset + 0x08, sf); + le_nibble = read_u32be(offset + 0x0c, sf); + ed_nibble = read_u32be(offset + 0x10, sf); + st_nibble = read_u32be(offset + 0x14, sf); + coef_offset = offset + 0x1c; + + if (version == 6) { + stream_offset = ((st_nibble - 2) / 2); + stream_size = ((ed_nibble - st_nibble - 2) / 2) * channels; + + /* stereo repeats loop flag/sample rate/offsets/etc but simplify */ + if (channels == 2) { + uint32_t s2_nibble = read_u32be(offset + 0x4a + 0x14, sf); + interleave = (s2_nibble - st_nibble) / 2; + } + } + loop_start = ((ls_nibble - 2) / 2 - stream_offset); + loop_end = ((le_nibble) / 2 - stream_offset) * channels; + } + + /* files also have an optional companion .gsb with volume/etc config that seems to be adapted from Xbox's .xsb */ + sf_body = open_streamfile_by_ext(sf, "gwd"); + if (!sf_body) goto fail; + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channels, loop_flag); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_GWB_GWD; + vgmstream->sample_rate = sample_rate; + vgmstream->num_samples = dsp_bytes_to_samples(stream_size, channels); + vgmstream->loop_start_sample = dsp_bytes_to_samples(loop_start, channels); + vgmstream->loop_end_sample = dsp_bytes_to_samples(loop_end, channels);; + + vgmstream->num_streams = total_subsongs; + vgmstream->stream_size = stream_size; + + vgmstream->coding_type = coding_NGC_DSP; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = interleave; + + dsp_read_coefs_be(vgmstream, sf, coef_offset + 0x00, 0x4a); + dsp_read_hist_be (vgmstream, sf, coef_offset + 0x24, 0x4a); + + + if (!vgmstream_open_stream(vgmstream, sf_body, stream_offset)) + goto fail; + close_streamfile(sf_body); + return vgmstream; + +fail: + close_streamfile(sf_body); + close_vgmstream(vgmstream); + return NULL; +} diff --git a/src/meta/meta.h b/src/meta/meta.h index ba6e6ce0..9259d3a1 100644 --- a/src/meta/meta.h +++ b/src/meta/meta.h @@ -988,4 +988,6 @@ VGMSTREAM* init_vgmstream_snds(STREAMFILE* sf); VGMSTREAM* init_vgmstream_nxof(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_gwb_gwd(STREAMFILE* sf); + #endif /*_META_H*/ diff --git a/src/vgmstream.c b/src/vgmstream.c index 45ed678b..49b58574 100644 --- a/src/vgmstream.c +++ b/src/vgmstream.c @@ -523,6 +523,7 @@ init_vgmstream_t init_vgmstream_functions[] = { init_vgmstream_snds, init_vgmstream_adm2, init_vgmstream_nxof, + init_vgmstream_gwb_gwd, /* lower priority metas (no clean header identity, somewhat ambiguous, or need extension/companion file to identify) */ init_vgmstream_scd_pcm, diff --git a/src/vgmstream_types.h b/src/vgmstream_types.h index a3c48af0..648687a2 100644 --- a/src/vgmstream_types.h +++ b/src/vgmstream_types.h @@ -703,6 +703,7 @@ typedef enum { meta_SQUEAKSAMPLE, meta_SNDS, meta_NXOF, + meta_GWB_GWD, } meta_t;