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;