From 69ab3d9161e7a96aa20b99559e6569a5f8ee2896 Mon Sep 17 00:00:00 2001 From: EdnessP <55930127+EdnessP@users.noreply.github.com> Date: Fri, 24 May 2024 17:43:16 +0300 Subject: [PATCH] DSP: Asura engine variants --- src/formats.c | 1 + src/libvgmstream.vcxproj | 1 + src/libvgmstream.vcxproj.filters | 3 + src/meta/meta.h | 6 +- src/meta/ngc_dsp_asura.c | 77 ++++++++++++++++++++++ src/meta/ngc_dsp_std.c | 110 +++++++++++++++++++++++++++---- src/meta/vag.c | 5 +- src/vgmstream.c | 5 +- src/vgmstream_types.h | 1 + 9 files changed, 193 insertions(+), 16 deletions(-) create mode 100644 src/meta/ngc_dsp_asura.c diff --git a/src/formats.c b/src/formats.c index 8d12e9be..2c3680a4 100644 --- a/src/formats.c +++ b/src/formats.c @@ -1436,6 +1436,7 @@ static const meta_info meta_info_list[] = { {meta_CBX, "Traveller's Tales CBX header"}, {meta_VAS_ROCKSTAR, "Rockstar .VAS header"}, {meta_EA_SBK, "Electronic Arts SBK header"}, + {meta_DSP_ASURA, "Rebellion DSP 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 42b3ea4e..499c736e 100644 --- a/src/libvgmstream.vcxproj +++ b/src/libvgmstream.vcxproj @@ -537,6 +537,7 @@ + diff --git a/src/libvgmstream.vcxproj.filters b/src/libvgmstream.vcxproj.filters index 6c4201d1..90f8367d 100644 --- a/src/libvgmstream.vcxproj.filters +++ b/src/libvgmstream.vcxproj.filters @@ -1432,6 +1432,9 @@ meta\Source Files + + meta\Source Files + meta\Source Files diff --git a/src/meta/meta.h b/src/meta/meta.h index 1454a2f1..b2444b9b 100644 --- a/src/meta/meta.h +++ b/src/meta/meta.h @@ -60,7 +60,7 @@ VGMSTREAM* init_vgmstream_dsp_sps_n1(STREAMFILE* sf); VGMSTREAM* init_vgmstream_dsp_itl_ch(STREAMFILE* sf); VGMSTREAM* init_vgmstream_dsp_adpy(STREAMFILE* sf); VGMSTREAM* init_vgmstream_dsp_adpx(STREAMFILE* sf); -VGMSTREAM* init_vgmstream_dsp_ds2(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_dsp_lucasarts_ds2(STREAMFILE* sf); VGMSTREAM* init_vgmstream_dsp_itl(STREAMFILE* sf); VGMSTREAM* init_vgmstream_dsp_sqex(STREAMFILE* sf); VGMSTREAM* init_vgmstream_dsp_wiivoice(STREAMFILE* sf); @@ -69,6 +69,8 @@ VGMSTREAM* init_vgmstream_dsp_cwac(STREAMFILE* sf); VGMSTREAM* init_vgmstream_idsp_tose(STREAMFILE* sf); VGMSTREAM* init_vgmstream_dsp_kwa(STREAMFILE* sf); VGMSTREAM* init_vgmstream_dsp_apex(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_dsp_asura(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_dsp_asura_ds2(STREAMFILE* sf); VGMSTREAM * init_vgmstream_csmp(STREAMFILE *streamFile); @@ -1010,4 +1012,6 @@ VGMSTREAM* init_vgmstream_vas_rockstar(STREAMFILE* sf); VGMSTREAM* init_vgmstream_ea_sbk(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_dsp_asura_sfx(STREAMFILE* sf); + #endif /*_META_H*/ diff --git a/src/meta/ngc_dsp_asura.c b/src/meta/ngc_dsp_asura.c new file mode 100644 index 00000000..cd0c8143 --- /dev/null +++ b/src/meta/ngc_dsp_asura.c @@ -0,0 +1,77 @@ +#include "meta.h" +#include "../coding/coding.h" + + +/* .sfx - Rebellion (Asura engine) games [Sniper Elite (Wii)] */ +/* Despite what the extension implies it's used for music too */ +/* Rebellion's other DSP variants can be found in ngc_dsp_std */ +VGMSTREAM* init_vgmstream_dsp_asura_sfx(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + int channels, interleave, loop_flag; + uint32_t nibble_count, sample_rate; + off_t ch1_offset, ch2_offset; + + + /* checks */ + if (!check_extensions(sf, "sfx")) + return NULL; + + /* no clear header id, but this is how they all start */ + /* the 0x02s are likely channels and codec (DSPADPCM) */ + if (read_u32be(0x00, sf) != 0x00 && + read_u32be(0x04, sf) != 0x02 && + read_u32be(0x08, sf) != 0x02) + return NULL; + + + nibble_count = read_u32be(0x0C, sf); + sample_rate = read_u32be(0x10, sf); /* always 44100? */ + + /* this would likely be an array, but always 2ch so */ + ch1_offset = read_u32be(0x14, sf); /* always 0x20? */ + ch2_offset = read_u32be(0x18, sf); /* 0x10 aligned */ + + interleave = ch2_offset - ch1_offset; + + /* channel header: + * 0x00: coefs + * 0x20: gain (0) + * 0x22: initial ps + * 0x30: stream start + */ + + channels = 2; + loop_flag = 0; + + /* more safety checks */ + if (interleave < 0 || + interleave < nibble_count / 2 || + interleave > get_streamfile_size(sf) / channels) + goto fail; + + if (read_u16be(ch1_offset + 0x22, sf) != read_u8(ch1_offset + 0x30, sf) || + read_u16be(ch2_offset + 0x22, sf) != read_u8(ch2_offset + 0x30, sf)) + goto fail; + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channels, loop_flag); + if (!vgmstream) goto fail; + + vgmstream->sample_rate = sample_rate; + vgmstream->meta_type = meta_DSP_ASURA; + vgmstream->coding_type = coding_NGC_DSP; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = interleave; + dsp_read_coefs_be(vgmstream, sf, ch1_offset, interleave); + vgmstream->num_samples = dsp_nibbles_to_samples(nibble_count); + + if (!vgmstream_open_stream(vgmstream, sf, ch1_offset + 0x30)) + goto fail; + + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} diff --git a/src/meta/ngc_dsp_std.c b/src/meta/ngc_dsp_std.c index 8554ff47..fcdab3a9 100644 --- a/src/meta/ngc_dsp_std.c +++ b/src/meta/ngc_dsp_std.c @@ -56,8 +56,10 @@ static bool read_dsp_header_endian(struct dsp_header *header, off_t offset, STRE goto fail; header->sample_rate = get_u32(buf+0x08); - if (header->sample_rate < 8000 || header->sample_rate > 48000) - goto fail; /* validated later but fail faster (unsure of min) */ + if (header->sample_rate < 5000 || header->sample_rate > 48000) + /* validated later but fail faster (unsure of min) */ + /* lowest known so far is 5000 in Judge Dredd (GC) */ + goto fail; /* context */ header->loop_flag = get_u16(buf+0x0c); @@ -315,8 +317,10 @@ VGMSTREAM* init_vgmstream_ngc_dsp_std(STREAMFILE* sf) { /* .dsp: standard * .adp: Dr. Muto/Battalion Wars (GC), Tale of Despereaux (Wii) - * (extensionless): Tony Hawk's Downhill Jam (Wii) */ - if (!check_extensions(sf, "dsp,adp,")) + * (extensionless): Tony Hawk's Downhill Jam (Wii) + * .wav: PDC World Championship Darts 2009 & Pro Tour (Wii) + * .dat: The Sims: Bustin' Out (GC) (rarely, most are extensionless) */ + if (!check_extensions(sf, "dsp,adp,,wav,lwav,dat,ldat")) return NULL; channels = 1; @@ -358,7 +362,7 @@ VGMSTREAM* init_vgmstream_ngc_dsp_std(STREAMFILE* sf) { /* ignore ddsp, that set samples/nibbles counting both channels so can't be detected * (could check for .dsp but most files don't need this) */ - if (check_extensions(sf, "adp")) { + if (check_extensions(sf, "adp,")) { uint32_t interleave = (get_streamfile_size(sf) / 2); ko = !read_dsp_header_be(&header2, interleave, sf); @@ -371,7 +375,7 @@ VGMSTREAM* init_vgmstream_ngc_dsp_std(STREAMFILE* sf) { } } } - + if (header.loop_flag) { off_t loop_off; /* check loop predictor/scale */ @@ -846,15 +850,16 @@ fail: return NULL; } -/* .ddsp - full interleaved dsp [Shark Tale (GC), The Sims 2: Pets (Wii), Wacky Races: Crash & Dash (Wii)] */ +/* .ddsp - full interleaved dsp [Shark Tale (GC), The Sims series (GC/Wii), Wacky Races: Crash & Dash (Wii)] */ VGMSTREAM* init_vgmstream_dsp_ddsp(STREAMFILE* sf) { dsp_meta dspm = {0}; /* checks */ /* .adp: Tale of Despereaux (Wii) */ /* .ddsp: fake extension (games have bigfiles without names, but has references to .wav) - * .wav: Wacky Races: Crash & Dash (Wii) */ - if (!check_extensions(sf, "adp,ddsp,wav,lwav")) + * .wav: Wacky Races: Crash & Dash (Wii) + * (extensionless): The Sims series (GC/Wii) */ + if (!check_extensions(sf, "adp,ddsp,wav,lwav,")) goto fail; dspm.channels = 2; @@ -913,7 +918,7 @@ VGMSTREAM* init_vgmstream_dsp_str_ig(STREAMFILE* sf) { dspm.header_spacing = 0x80; dspm.start_offset = 0x800; dspm.interleave = 0x4000; - + dspm.meta_type = meta_DSP_STR_IG; return init_vgmstream_dsp_common(sf, &dspm); fail: @@ -1271,7 +1276,7 @@ fail: } /* .ds2 - LucasArts wrapper [Star Wars: Bounty Hunter (GC)] */ -VGMSTREAM* init_vgmstream_dsp_ds2(STREAMFILE* sf) { +VGMSTREAM* init_vgmstream_dsp_lucasarts_ds2(STREAMFILE* sf) { dsp_meta dspm = {0}; size_t file_size, channel_offset; @@ -1406,7 +1411,7 @@ VGMSTREAM* init_vgmstream_dsp_wiiadpcm(STREAMFILE* sf) { goto fail; dspm.interleave = read_u32be(0x08,sf); /* interleave offset */ - /* 0x0c: NFS = 0 when RAM (2 DSP headers), interleave size when stream (2 WIIADPCM headers) + /* 0x0c: NFS = 0 when RAM (2 DSP headers), interleave size when stream (2 WIIADPCM headers) * AB = 0 (2 WIIADPCM headers) */ dspm.channels = (dspm.interleave ? 2 : 1); @@ -1556,3 +1561,84 @@ VGMSTREAM* init_vgmstream_dsp_apex(STREAMFILE* sf) { fail: return NULL; } + + +/* DSP - Rebellion Developments (Asura engine) games */ +VGMSTREAM* init_vgmstream_dsp_asura(STREAMFILE* sf) { + dsp_meta dspm = {0}; + off_t start_offset; + size_t data_size; + uint8_t flag; + + /* checks */ + if (!is_id32be(0x00, sf, "DSP\x00") && /* GC */ + !is_id32be(0x00, sf, "DSP\x01") && /* GC/Wii/WiiU */ + !is_id32be(0x00, sf, "DSP\x02")) /* WiiU */ + return NULL; + + /* .dsp: Judge Dredd (GC) + * .wav: Judge Dredd (GC), The Simpsons Game (Wii), Sniper Elite V2 (WiiU) */ + if (!check_extensions(sf, "dsp,wav,lwav")) + return NULL; + + /* flag set to 0x00 so far only seen in Judge Dredd, which also uses 0x01 + * at first assumed being 0 means it has a stream name at 0x48 (unlikely) */ + /* flag set to 0x02 means it's ddsp-like stereo */ + flag = read_u8(0x03, sf); + /* GC/Wii games are all just standard DSP with an id string */ + /* Sniper Elite V2 (WiiU) added a filesize value in the header + * and has extra garbage 0xCD bytes at the end for alignment */ + start_offset = 0x04; + + data_size = read_u32be(start_offset, sf); + /* stereo flag should only occur on the WiiU, Wii uses .ds2 or .sfx (ngc_dsp_asura) */ + if (align_size_to_block(data_size + 0x08, 0x04) == get_streamfile_size(sf) || (flag == 0x02 && + align_size_to_block(data_size * 2 + 0x0C, 0x04) == get_streamfile_size(sf))) + start_offset = 0x08; + + dspm.channels = 1; + dspm.max_channels = 1; + + if (flag == 0x02) { /* channels are not aligned */ + if (read_u32be(data_size + 0x08, sf) != data_size) + goto fail; /* size should match */ + + dspm.channels = 2; + dspm.max_channels = 2; + dspm.header_spacing = data_size + 0x04; + dspm.interleave = dspm.header_spacing; + } + + dspm.header_offset = start_offset + 0x00; + dspm.start_offset = start_offset + 0x60; + + dspm.meta_type = meta_DSP_ASURA; + return init_vgmstream_dsp_common(sf, &dspm); +fail: + return NULL; +} + + +/* .ds2 - Rebellion (Asura engine) [PDC World Championship Darts 2009 & Pro Tour (Wii)] */ +VGMSTREAM* init_vgmstream_dsp_asura_ds2(STREAMFILE* sf) { + dsp_meta dspm = {0}; + + if (!check_extensions(sf, "ds2")) + goto fail; + + dspm.channels = 2; + dspm.max_channels = 2; + dspm.interleave = 0x8000; + + dspm.header_offset = 0x00; + dspm.start_offset = 0x60; + + dspm.header_spacing = dspm.interleave; + dspm.interleave_first_skip = dspm.start_offset; + dspm.interleave_first = dspm.interleave - dspm.interleave_first_skip; + + dspm.meta_type = meta_DSP_ASURA; + return init_vgmstream_dsp_common(sf, &dspm); +fail: + return NULL; +} diff --git a/src/meta/vag.c b/src/meta/vag.c index ae1af756..13c0a7af 100644 --- a/src/meta/vag.c +++ b/src/meta/vag.c @@ -27,8 +27,9 @@ VGMSTREAM* init_vgmstream_vag(STREAMFILE* sf) { * .xa2: Shikigami no Shiro (PS2) * .snd: Alien Breed (Vita) * .svg: ModernGroove: Ministry of Sound Edition (PS2) - * (extensionless): The Urbz (PS2), The Sims series (PS2) */ - if (!check_extensions(sf,"vag,swag,str,vig,l,r,vas,xa2,snd,svg,")) + * (extensionless): The Urbz (PS2), The Sims series (PS2) + * .wav: Sniper Elite (PS2), The Simpsons Game (PS2/PSP) */ + if (!check_extensions(sf,"vag,swag,str,vig,l,r,vas,xa2,snd,svg,,wav,lwav")) return NULL; file_size = get_streamfile_size(sf); diff --git a/src/vgmstream.c b/src/vgmstream.c index 64816b32..721db791 100644 --- a/src/vgmstream.c +++ b/src/vgmstream.c @@ -409,7 +409,7 @@ init_vgmstream_t init_vgmstream_functions[] = { init_vgmstream_gin, init_vgmstream_dsf, init_vgmstream_208, - init_vgmstream_dsp_ds2, + init_vgmstream_dsp_lucasarts_ds2, init_vgmstream_ffdl, init_vgmstream_mus_vc, init_vgmstream_strm_abylight, @@ -527,6 +527,9 @@ init_vgmstream_t init_vgmstream_functions[] = { init_vgmstream_cbx, init_vgmstream_vas_rockstar, init_vgmstream_ea_sbk, + init_vgmstream_dsp_asura, + init_vgmstream_dsp_asura_ds2, + init_vgmstream_dsp_asura_sfx, /* lower priority metas (no clean header identity, somewhat ambiguous, or need extension/companion file to identify) */ init_vgmstream_agsc, diff --git a/src/vgmstream_types.h b/src/vgmstream_types.h index e8140e5a..cd62f7ac 100644 --- a/src/vgmstream_types.h +++ b/src/vgmstream_types.h @@ -709,6 +709,7 @@ typedef enum { meta_CBX, meta_VAS_ROCKSTAR, meta_EA_SBK, + meta_DSP_ASURA, } meta_t;