Merge pull request #796 from bnnm/dsp-sfx

dsp sfx
This commit is contained in:
bnnm 2021-01-08 00:20:56 +01:00 committed by GitHub
commit 3ffed063c6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 221 additions and 107 deletions

View File

@ -2,7 +2,7 @@
#include "../coding/coding.h" #include "../coding/coding.h"
typedef enum { MFX, MFX_BANK, SFX_BANK, SBNK, FORM } musx_form; typedef enum { MFX, MFX_BANK, SFX_BANK, SBNK, FORM } musx_form;
typedef enum { PSX, DSP, XBOX, IMA, DAT } musx_codec; typedef enum { PSX, DSP, XBOX, IMA, DAT, NGCA, PCM } musx_codec;
typedef struct { typedef struct {
int big_endian; int big_endian;
int version; int version;
@ -31,10 +31,10 @@ typedef struct {
int32_t loop_end_sample; int32_t loop_end_sample;
} musx_header; } musx_header;
static int parse_musx(STREAMFILE *streamFile, musx_header *musx); static int parse_musx(STREAMFILE* sf, musx_header* musx);
/* MUSX - from Eurocom's games */ /* MUSX - from Eurocom's games */
VGMSTREAM * init_vgmstream_musx(STREAMFILE *streamFile) { VGMSTREAM* init_vgmstream_musx(STREAMFILE* sf) {
VGMSTREAM* vgmstream = NULL; VGMSTREAM* vgmstream = NULL;
off_t start_offset; off_t start_offset;
musx_header musx = {0}; musx_header musx = {0};
@ -43,12 +43,12 @@ VGMSTREAM * init_vgmstream_musx(STREAMFILE *streamFile) {
/* checks */ /* checks */
/* .sfx: actual extension (extracted from bigfiles with sometimes encrypted names) /* .sfx: actual extension (extracted from bigfiles with sometimes encrypted names)
* .musx: header id */ * .musx: header id */
if (!check_extensions(streamFile, "sfx,musx")) if (!check_extensions(sf, "sfx,musx"))
goto fail; goto fail;
if (read_32bitBE(0x00,streamFile) != 0x4D555358) /* "MUSX" */ if (!is_id32be(0x00,sf, "MUSX"))
goto fail; goto fail;
if (!parse_musx(streamFile, &musx)) if (!parse_musx(sf, &musx))
goto fail; goto fail;
@ -112,8 +112,41 @@ VGMSTREAM * init_vgmstream_musx(STREAMFILE *streamFile) {
vgmstream->layout_type = layout_interleave; vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = 0x08; vgmstream->interleave_block_size = 0x08;
dsp_read_coefs(vgmstream,streamFile,musx.coefs_offset+0x1c, 0x60, musx.big_endian); dsp_read_coefs(vgmstream,sf,musx.coefs_offset+0x1c, 0x60, musx.big_endian);
dsp_read_hist(vgmstream,streamFile,musx.coefs_offset+0x40, 0x60, musx.big_endian); dsp_read_hist(vgmstream,sf,musx.coefs_offset+0x40, 0x60, musx.big_endian);
break;
case PCM:
//vgmstream->num_samples = pcm_bytes_to_samples(musx.stream_size, musx.channels, 16);
//vgmstream->loop_start_sample = pcm_bytes_to_samples(musx.loop_start, musx.channels, 16);
//vgmstream->loop_end_sample = pcm_bytes_to_samples(musx.loop_end, musx.channels, 16);
vgmstream->coding_type = musx.big_endian ? coding_PCM16BE : coding_PCM16LE; /* only seen on PC but just in case */
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = 0x02;
break;
case NGCA:
if (!is_id32be(start_offset,sf, "NGCA"))
goto fail;
/* 0x04: size (stereo full-interleaves 2 NGCA headers but sizes count all) */
/* 0x08: channels */
/* 0x0c: coefs */
musx.coefs_offset = start_offset;
start_offset += 0x40;
//musx.stream_size -= 0x40 * musx.channels; /* needed for interleave */
//vgmstream->num_samples = dsp_bytes_to_samples(musx.stream_size - 0x40 * musx.channels, musx.channels);
//vgmstream->loop_start_sample = dsp_bytes_to_samples(musx.loop_start + 0x40, musx.channels);
//vgmstream->loop_end_sample = dsp_bytes_to_samples(musx.loop_end - 0x40 * musx.channels, musx.channels);
vgmstream->coding_type = coding_NGC_DSP;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = musx.stream_size / musx.channels;
dsp_read_coefs(vgmstream, sf, musx.coefs_offset+0x0c, vgmstream->interleave_block_size, musx.big_endian);
//dsp_read_hist(vgmstream, sf, musx.coefs_offset+0x30, 0x00, musx.big_endian); /* used? */
break; break;
default: default:
@ -127,7 +160,7 @@ VGMSTREAM * init_vgmstream_musx(STREAMFILE *streamFile) {
if (musx.loop_flag && musx.loop_end_sample) if (musx.loop_flag && musx.loop_end_sample)
vgmstream->loop_end_sample = musx.loop_end_sample; vgmstream->loop_end_sample = musx.loop_end_sample;
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) if (!vgmstream_open_stream(vgmstream,sf,start_offset))
goto fail; goto fail;
return vgmstream; return vgmstream;
@ -137,21 +170,21 @@ fail:
} }
static int parse_musx_stream(STREAMFILE *streamFile, musx_header *musx) { static int parse_musx_stream(STREAMFILE* sf, musx_header* musx) {
int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL; uint32_t (*read_u32)(off_t,STREAMFILE*) = NULL;
int default_channels, default_sample_rate; int default_channels, default_sample_rate;
if (musx->big_endian) { if (musx->big_endian) {
read_32bit = read_32bitBE; read_u32 = read_u32be;
} else { } else {
read_32bit = read_32bitLE; read_u32 = read_u32le;
} }
/* autodetect for older versions that have no info */ /* autodetect for older versions that have no info */
if (musx->platform == 0) { if (musx->platform == 0) {
if (musx->big_endian) { if (musx->big_endian) {
musx->platform = 0x47433032; /* "GC02" (fake) */ musx->platform = get_id32be("GC02"); /* (fake) */
} }
else { else {
off_t offset = musx->stream_offset; off_t offset = musx->stream_offset;
@ -159,10 +192,10 @@ static int parse_musx_stream(STREAMFILE *streamFile, musx_header *musx) {
if (max > musx->stream_size) if (max > musx->stream_size)
max = musx->stream_size; max = musx->stream_size;
if (ps_check_format(streamFile, offset, max)) { if (ps_check_format(sf, offset, max)) {
musx->platform = 0x5053325F; /* "PS2_" */ musx->platform = get_id32be("PS2_");
} else { } else {
musx->platform = 0x58423032; /* "XB02" (fake) */ musx->platform = get_id32be("XB02"); /* (fake) */
} }
} }
} }
@ -258,19 +291,19 @@ static int parse_musx_stream(STREAMFILE *streamFile, musx_header *musx) {
* 0x10: volume? (usually <= 100) */ * 0x10: volume? (usually <= 100) */
/* find loops (cues1 also seems to have this info but this looks ok) */ /* find loops (cues1 also seems to have this info but this looks ok) */
cues2_count = read_32bit(musx->loops_offset+0x04, streamFile); cues2_count = read_u32(musx->loops_offset+0x04, sf);
cues2_offset = musx->loops_offset + read_32bit(musx->loops_offset+0x0c, streamFile); cues2_offset = musx->loops_offset + read_u32(musx->loops_offset+0x0c, sf);
for (i = 0; i < cues2_count; i++) { for (i = 0; i < cues2_count; i++) {
uint32_t type, offset1, offset2; uint32_t type, offset1, offset2;
if (musx->is_old) { if (musx->is_old) {
offset1 = read_32bit(cues2_offset + i*0x20 + 0x04, streamFile); offset1 = read_u32(cues2_offset + i*0x20 + 0x04, sf);
type = read_32bit(cues2_offset + i*0x20 + 0x08, streamFile); type = read_u32(cues2_offset + i*0x20 + 0x08, sf);
offset2 = read_32bit(cues2_offset + i*0x20 + 0x14, streamFile); offset2 = read_u32(cues2_offset + i*0x20 + 0x14, sf);
} else { } else {
offset1 = read_32bit(cues2_offset + i*0x14 + 0x04, streamFile); offset1 = read_u32(cues2_offset + i*0x14 + 0x04, sf);
type = read_32bit(cues2_offset + i*0x14 + 0x08, streamFile); type = read_u32(cues2_offset + i*0x14 + 0x08, sf);
offset2 = read_32bit(cues2_offset + i*0x14 + 0x0c, streamFile); offset2 = read_u32(cues2_offset + i*0x14 + 0x0c, sf);
} }
/* other types (0x0a, 0x09) look like section/end markers, 0x06/07 only seems to exist once */ /* other types (0x0a, 0x09) look like section/end markers, 0x06/07 only seems to exist once */
@ -282,7 +315,7 @@ static int parse_musx_stream(STREAMFILE *streamFile, musx_header *musx) {
} }
} }
} }
else if (musx->loops_offset && read_32bitBE(musx->loops_offset, streamFile) != 0xABABABAB) { else if (musx->loops_offset && read_u32be(musx->loops_offset, sf) != 0xABABABAB) {
/* parse loop table (loop starts are -1 if non-looping) /* parse loop table (loop starts are -1 if non-looping)
* 0x00: version? * 0x00: version?
* 0x04: flags? (&1=loops) * 0x04: flags? (&1=loops)
@ -292,10 +325,10 @@ static int parse_musx_stream(STREAMFILE *streamFile, musx_header *musx) {
* 0x14: loop start sample * 0x14: loop start sample
* 0x18: loop end offset * 0x18: loop end offset
* 0x1c: loop start offset */ * 0x1c: loop start offset */
musx->loop_end_sample = read_32bitLE(musx->loops_offset+0x10, streamFile); musx->loop_end_sample = read_s32le(musx->loops_offset+0x10, sf);
musx->loop_start_sample = read_32bitLE(musx->loops_offset+0x14, streamFile); musx->loop_start_sample = read_s32le(musx->loops_offset+0x14, sf);
musx->loop_end = read_32bitLE(musx->loops_offset+0x18, streamFile); musx->loop_end = read_s32le(musx->loops_offset+0x18, sf);
musx->loop_start = read_32bitLE(musx->loops_offset+0x1c, streamFile); musx->loop_start = read_s32le(musx->loops_offset+0x1c, sf);
musx->num_samples = musx->loop_end_sample; /* preferable even if not looping as some files have padding */ musx->num_samples = musx->loop_end_sample; /* preferable even if not looping as some files have padding */
musx->loop_flag = (musx->loop_start_sample >= 0); musx->loop_flag = (musx->loop_start_sample >= 0);
} }
@ -310,7 +343,7 @@ static int parse_musx_stream(STREAMFILE *streamFile, musx_header *musx) {
int pos; int pos;
off_t offset = musx->stream_offset + musx->stream_size - 0x800; off_t offset = musx->stream_offset + musx->stream_size - 0x800;
if (read_streamfile(buf, offset, sizeof(buf), streamFile) != 0x800) if (read_streamfile(buf, offset, sizeof(buf), sf) != 0x800)
goto fail; goto fail;
pos = 0x800 - 0x04; pos = 0x800 - 0x04;
@ -328,58 +361,66 @@ fail:
return 0; return 0;
} }
static int parse_musx(STREAMFILE *streamFile, musx_header *musx) { static int parse_musx(STREAMFILE* sf, musx_header* musx) {
int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL; int32_t (*read_s32)(off_t,STREAMFILE*) = NULL;
int target_subsong = streamFile->stream_index; uint32_t (*read_u32)(off_t,STREAMFILE*) = NULL;
uint16_t (*read_u16)(off_t,STREAMFILE*) = NULL;
int target_subsong = sf->stream_index;
/* base header is LE */ /* base header is LE */
/* 0x04: file ID (referenced in external .h, sometimes files are named HC(id).sfx too) */ /* 0x04: file ID (referenced in external .h, sometimes files are named [prefix](id)[suffix].sfx too) */
musx->version = read_32bitLE(0x08,streamFile); musx->version = read_u32le(0x08,sf);
musx->file_size = read_32bitLE(0x0c,streamFile); musx->file_size = read_u32le(0x0c,sf);
switch(musx->version) { switch(musx->version) {
case 201: /* Sphinx and the Cursed Mummy (PS2), Buffy the Vampire Slayer: Chaos Bleeds (PS2/GC/Xbox) */ case 201: /* Sphinx and the Cursed Mummy (PS2), Buffy the Vampire Slayer: Chaos Bleeds (PS2/GC/Xbox) */
case 1: /* Athens 2004 (PS2) */ case 1: /* Athens 2004 (PS2) */
musx->platform = 0; /* guess later */ musx->platform = 0; /* guess later */
musx->tables_offset = 0x10; musx->tables_offset = 0x10;
musx->big_endian = guess_endianness32bit(0x10, streamFile); musx->big_endian = guess_endianness32bit(0x10, sf);
musx->is_old = 1; musx->is_old = 1;
break; break;
case 4: /* Spyro: A Hero's Tail (PS2/Xbox/GC), Athens 2004 (PC) */ case 4: /* Spyro: A Hero's Tail (PS2/Xbox/GC), Athens 2004 (PC) */
case 5: /* Predator: Concrete Jungle (PS2/Xbox), Robots (GC) */ case 5: /* Predator: Concrete Jungle (PS2/Xbox), Robots (GC) */
case 6: /* Batman Begins (GC), Ice Age 2 (PS2/PC) */ case 6: /* Batman Begins (GC), Ice Age 2 (PS2/PC) */
musx->platform = read_32bitBE(0x10,streamFile); musx->platform = read_u32be(0x10,sf);
/* 0x14: some id/hash? */ /* 0x14: some id/hash? */
/* 0x18: platform number? */ /* 0x18: platform number? */
/* 0x1c: null */ /* 0x1c: null */
musx->tables_offset = 0x20; musx->tables_offset = 0x20;
musx->big_endian = (musx->platform == 0x47435F5F); /* "GC__" */ musx->big_endian = (musx->platform == get_id32be("GC__"));
break; break;
case 10: /* 007: Quantum of Solace (PS2), Pirates of the Caribbean: At World's End (PSP), GoldenEye 007 (Wii), Rio (PS3) */ case 10: /* 007: Quantum of Solace (PS2), Pirates of the Caribbean: At World's End (PSP), GoldenEye 007 (Wii), Rio (PS3) */
musx->platform = read_32bitBE(0x10,streamFile); musx->platform = read_u32be(0x10,sf);
/* 0x14: null */ /* 0x14: null */
/* 0x18: platform number? */ /* 0x18: platform number? */
/* 0x1c: null */ /* 0x1c: null */
/* 0x20: hash */ /* 0x20: hash */
musx->tables_offset = 0; /* no tables */ musx->tables_offset = 0; /* no tables */
musx->big_endian = (musx->platform == 0x47435F5F || /* "GC__" */ musx->big_endian = (
musx->platform == 0x58455F5F || /* "XE__" */ musx->platform == get_id32be("GC__") ||
musx->platform == 0x5053335F || /* "PS3_" */ musx->platform == get_id32be("XE__") ||
musx->platform == 0x5749495F); /* "WII_" */ musx->platform == get_id32be("PS3_") ||
musx->platform == get_id32be("WII_")
);
break; break;
default: default:
VGM_LOG("MUSX: unknown version\n");
goto fail; goto fail;
} }
if (musx->big_endian) { if (musx->big_endian) {
read_32bit = read_32bitBE; read_s32 = read_s32be;
read_u32 = read_u32be;
read_u16 = read_u16be;
} else { } else {
read_32bit = read_32bitLE; read_s32 = read_s32le;
read_u32 = read_u32le;
read_u16 = read_u16le;
} }
@ -388,53 +429,56 @@ static int parse_musx(STREAMFILE *streamFile, musx_header *musx) {
off_t table1_offset, table2_offset /*, table3_offset, table4_offset*/; off_t table1_offset, table2_offset /*, table3_offset, table4_offset*/;
size_t /*table1_size, table2_size, table3_size,*/ table4_size; size_t /*table1_size, table2_size, table3_size,*/ table4_size;
table1_offset = read_32bit(musx->tables_offset+0x00, streamFile); table1_offset = read_u32(musx->tables_offset+0x00, sf);
//table1_size = read_32bit(musx->tables_offset+0x04, streamFile); //table1_size = read_u32(musx->tables_offset+0x04, sf);
table2_offset = read_32bit(musx->tables_offset+0x08, streamFile); table2_offset = read_u32(musx->tables_offset+0x08, sf);
//table2_size = read_32bit(musx->tables_offset+0x0c, streamFile); //table2_size = read_u32(musx->tables_offset+0x0c, sf);
//table3_offset = read_32bit(musx->tables_offset+0x10, streamFile); //table3_offset = read_u32(musx->tables_offset+0x10, sf);
//table3_size = read_32bit(musx->tables_offset+0x14, streamFile); //table3_size = read_u32(musx->tables_offset+0x14, sf);
//table4_offset = read_32bit(musx->tables_offset+0x18, streamFile); //table4_offset = read_u32(musx->tables_offset+0x18, sf);
table4_size = read_32bit(musx->tables_offset+0x1c, streamFile); table4_size = read_u32(musx->tables_offset+0x1c, sf);
if (table2_offset == 0 || table2_offset == 0xABABABAB) { if (table2_offset == 0 || table2_offset == 0xABABABAB) {
/* possibly a collection of IDs */ /* possibly a collection of IDs */
VGM_LOG("MUSX: unsupported ID type\n");
goto fail; goto fail;
} }
else if (table4_size != 0 && table4_size != 0xABABABAB) { else if (table4_size != 0 && table4_size != 0xABABABAB) {
/* sfx banks have table1=id table? table2=header table, table=DSP coefs table (optional), table4=data */ /* sfx banks have table1=id table? table2=header table, table=DSP coefs table (optional), table4=data */
musx->form = SFX_BANK; musx->form = SFX_BANK;
} }
else if (read_32bit(table1_offset+0x00, streamFile) < 0x9 && else if (read_u32(table1_offset+0x00, sf) < 0x9 &&
read_32bit(table1_offset+0x08, streamFile) == 0x14 && read_u32(table1_offset+0x08, sf) == 0x14 &&
read_32bit(table1_offset+0x10, streamFile) <= 100) { read_u32(table1_offset+0x10, sf) <= 100) {
/* streams have a info table with a certain format */ /* streams have a info table with a certain format */
musx->form = MFX; musx->form = MFX;
} }
else if (read_32bit(table1_offset+0x00, streamFile) == 0 && else if (read_u32(table1_offset+0x00, sf) == 0 &&
read_32bit(table1_offset+0x04, streamFile) > read_32bit(table1_offset+0x00, streamFile) && read_u32(table1_offset+0x04, sf) > read_u32(table1_offset+0x00, sf) &&
read_32bit(table1_offset+0x08, streamFile) > read_32bit(table1_offset+0x04, streamFile)) { read_u32(table1_offset+0x08, sf) > read_u32(table1_offset+0x04, sf)) {
/* a list of offsets starting from 0, each stream then has info table at offset */ /* a list of offsets starting from 0, each stream then has info table at offset */
musx->form = MFX_BANK; musx->form = MFX_BANK;
} }
else { else {
VGM_LOG("MUSX: unsupported unknown type\n");
goto fail; goto fail;
} }
} }
else { else {
if (read_32bitBE(0x800,streamFile) == 0x53424E4B) { /* "SBNK" */ if (is_id32be(0x800,sf, "SBNK")) {
/* SFX_BANK-like sound bank found on v10 Wii games */ /* SFX_BANK-like sound bank found on v10 Wii/PC games [Dead Space Extraction (Wii), G-Force (PC)] */
musx->form = SBNK; musx->form = SBNK;
} }
else if (read_32bitBE(0x800,streamFile) == 0x464F524D) { /* "FORM" */ else if (is_id32be(0x800,sf, "FORM")) {
/* RIFF-like sound bank found on v10 PSP games */ /* RIFF-like sound bank found on v10 PSP games */
musx->form = FORM; musx->form = FORM;
} }
else if (read_32bitBE(0x800,streamFile) == 0x45535044) { /* "ESPD" */ else if (is_id32be(0x800,sf, "ESPD")) {
/* projectdetails.sfx */ /* projectdetails.sfx */
goto fail; goto fail;
} }
else { else {
/* simplest music type*/
musx->form = MFX; musx->form = MFX;
} }
} }
@ -445,27 +489,27 @@ static int parse_musx(STREAMFILE *streamFile, musx_header *musx) {
case MFX: case MFX:
if (musx->tables_offset) { if (musx->tables_offset) {
musx->loops_offset = read_32bit(musx->tables_offset+0x00, streamFile); musx->loops_offset = read_u32(musx->tables_offset+0x00, sf);
musx->stream_offset = read_32bit(musx->tables_offset+0x08, streamFile); musx->stream_offset = read_u32(musx->tables_offset+0x08, sf);
musx->stream_size = read_32bit(musx->tables_offset+0x0c, streamFile); musx->stream_size = read_u32(musx->tables_offset+0x0c, sf);
} }
else { else {
if (read_32bitBE(0x30, streamFile) != 0xABABABAB) { if (read_u32be(0x30, sf) != 0xABABABAB) {
uint32_t miniheader = read_32bitBE(0x40, streamFile); uint32_t miniheader = read_u32be(0x40, sf);
switch(miniheader) { switch(miniheader) {
case 0x44415434: /* "DAT4" */ case 0x44415434: /* "DAT4" */
case 0x44415435: /* "DAT5" */ case 0x44415435: /* "DAT5" */
case 0x44415438: /* "DAT8" */ case 0x44415438: /* "DAT8" */
/* found on PS3/Wii (but not always?) */ /* found on PS3/Wii (but not always?) */
musx->stream_size = read_32bitLE(0x44, streamFile); musx->stream_size = read_u32le(0x44, sf);
musx->channels = read_32bitLE(0x48, streamFile); musx->channels = read_u32le(0x48, sf);
musx->sample_rate = read_32bitLE(0x4c, streamFile); musx->sample_rate = read_u32le(0x4c, sf);
musx->loops_offset = 0x50; musx->loops_offset = 0x50;
break; break;
default: default:
if (read_32bitBE(0x30, streamFile) == 0x00 && if (read_u32be(0x30, sf) == 0x00 &&
read_32bitBE(0x34, streamFile) == 0x00) { read_u32be(0x34, sf) == 0x00) {
/* no subheader [Spider-Man 4 (Wii)] */ /* no subheader [Spider-Man 4 (Wii)] */
musx->loops_offset = 0x00; musx->loops_offset = 0x00;
} else { } else {
@ -480,32 +524,32 @@ static int parse_musx(STREAMFILE *streamFile, musx_header *musx) {
musx->stream_size = 0; /* read later */ musx->stream_size = 0; /* read later */
} }
if (!parse_musx_stream(streamFile, musx)) if (!parse_musx_stream(sf, musx))
goto fail; goto fail;
break; break;
case MFX_BANK: { case MFX_BANK: {
off_t target_offset, base_offset, data_offset; off_t target_offset, base_offset, data_offset;
musx->total_subsongs = read_32bit(musx->tables_offset+0x04, streamFile) / 0x04; /* size */ musx->total_subsongs = read_u32(musx->tables_offset+0x04, sf) / 0x04; /* size */
if (target_subsong == 0) target_subsong = 1; if (target_subsong == 0) target_subsong = 1;
if (target_subsong > musx->total_subsongs || musx->total_subsongs <= 0) goto fail; if (target_subsong > musx->total_subsongs || musx->total_subsongs <= 0) goto fail;
base_offset = read_32bit(musx->tables_offset+0x00, streamFile); base_offset = read_u32(musx->tables_offset+0x00, sf);
data_offset = read_32bit(musx->tables_offset+0x08, streamFile); data_offset = read_u32(musx->tables_offset+0x08, sf);
target_offset = read_32bit(base_offset + (target_subsong - 1)*0x04, streamFile) + data_offset; target_offset = read_u32(base_offset + (target_subsong - 1)*0x04, sf) + data_offset;
/* 0x00: id? */ /* 0x00: id? */
musx->stream_offset = read_32bit(target_offset + 0x04, streamFile) + data_offset; musx->stream_offset = read_u32(target_offset + 0x04, sf) + data_offset;
musx->stream_size = read_32bit(target_offset + 0x08, streamFile); musx->stream_size = read_u32(target_offset + 0x08, sf);
musx->loops_offset = target_offset + 0x0c; musx->loops_offset = target_offset + 0x0c;
/* this looks correct for PS2 and Xbox, not sure about GC */ /* this looks correct for PS2 and Xbox, not sure about GC */
musx->channels = 1; musx->channels = 1;
musx->sample_rate = 22050; musx->sample_rate = 22050;
if (!parse_musx_stream(streamFile, musx)) if (!parse_musx_stream(sf, musx))
goto fail; goto fail;
break; break;
} }
@ -514,13 +558,13 @@ static int parse_musx(STREAMFILE *streamFile, musx_header *musx) {
off_t target_offset, head_offset, coef_offset, data_offset; off_t target_offset, head_offset, coef_offset, data_offset;
size_t coef_size; size_t coef_size;
//unk_offset = read_32bit(musx->tables_offset+0x00, streamFile); /* ids and cue-like config? */ //unk_offset = read_u32(musx->tables_offset+0x00, sf); /* ids and cue-like config? */
head_offset = read_32bit(musx->tables_offset+0x08, streamFile); head_offset = read_u32(musx->tables_offset+0x08, sf);
coef_offset = read_32bit(musx->tables_offset+0x10, streamFile); coef_offset = read_u32(musx->tables_offset+0x10, sf);
coef_size = read_32bit(musx->tables_offset+0x14, streamFile); coef_size = read_u32(musx->tables_offset+0x14, sf);
data_offset = read_32bit(musx->tables_offset+0x18, streamFile); data_offset = read_u32(musx->tables_offset+0x18, sf);
musx->total_subsongs = read_32bit(head_offset+0x00, streamFile); musx->total_subsongs = read_u32(head_offset+0x00, sf);
if (target_subsong == 0) target_subsong = 1; if (target_subsong == 0) target_subsong = 1;
if (target_subsong > musx->total_subsongs || musx->total_subsongs <= 0) goto fail; if (target_subsong > musx->total_subsongs || musx->total_subsongs <= 0) goto fail;
@ -529,13 +573,13 @@ static int parse_musx(STREAMFILE *streamFile, musx_header *musx) {
target_offset = head_offset + 0x04 + (target_subsong - 1)*0x28; target_offset = head_offset + 0x04 + (target_subsong - 1)*0x28;
/* 0x00: flag? */ /* 0x00: flag? */
musx->stream_offset = read_32bit(target_offset + 0x04, streamFile) + data_offset; musx->stream_offset = read_u32(target_offset + 0x04, sf) + data_offset;
musx->stream_size = read_32bit(target_offset + 0x08, streamFile); musx->stream_size = read_u32(target_offset + 0x08, sf);
musx->sample_rate = read_32bit(target_offset + 0x0c, streamFile); musx->sample_rate = read_u32(target_offset + 0x0c, sf);
/* 0x10: size? */ /* 0x10: size? */
/* 0x14: channels? */ /* 0x14: channels? */
/* 0x18: always 4? */ /* 0x18: always 4? */
musx->coefs_offset = read_32bit(target_offset + 0x1c, streamFile) + coef_offset; /* may be set even for non-GC */ musx->coefs_offset = read_u32(target_offset + 0x1c, sf) + coef_offset; /* may be set even for non-GC */
/* 0x20: null? */ /* 0x20: null? */
/* 0x24: sub-id? */ /* 0x24: sub-id? */
musx->channels = 1; musx->channels = 1;
@ -544,11 +588,11 @@ static int parse_musx(STREAMFILE *streamFile, musx_header *musx) {
target_offset = head_offset + 0x04 + (target_subsong - 1)*0x20; target_offset = head_offset + 0x04 + (target_subsong - 1)*0x20;
/* 0x00: flag? */ /* 0x00: flag? */
musx->stream_offset = read_32bit(target_offset + 0x04, streamFile) + data_offset; musx->stream_offset = read_u32(target_offset + 0x04, sf) + data_offset;
musx->stream_size = read_32bit(target_offset + 0x08, streamFile); musx->stream_size = read_u32(target_offset + 0x08, sf);
musx->sample_rate = read_32bit(target_offset + 0x0c, streamFile); musx->sample_rate = read_u32(target_offset + 0x0c, sf);
/* 0x10: size? */ /* 0x10: size? */
musx->coefs_offset = read_32bit(target_offset + 0x14, streamFile) + coef_offset; /* may be set even for non-GC */ musx->coefs_offset = read_u32(target_offset + 0x14, sf) + coef_offset; /* may be set even for non-GC */
/* 0x18: null? */ /* 0x18: null? */
/* 0x1c: sub-id? */ /* 0x1c: sub-id? */
musx->channels = 1; musx->channels = 1;
@ -557,13 +601,81 @@ static int parse_musx(STREAMFILE *streamFile, musx_header *musx) {
if (coef_size == 0) if (coef_size == 0)
musx->coefs_offset = 0; /* disable for later detection */ musx->coefs_offset = 0; /* disable for later detection */
if (!parse_musx_stream(streamFile, musx)) if (!parse_musx_stream(sf, musx))
goto fail; goto fail;
break; break;
} }
case SBNK: {
off_t target_offset, head_offset, data_offset;
uint8_t codec;
/* 0x00: id */
/* 0x04: always 0x12? */
/* 0x08: file ID (same as base file) */
/* 0x0c: some ID? */
/* 0x10: table1 count */
/* 0x14: table1 offset (from here) */
/* 0x18: table2 count */
/* 0x1c: table2 offset (from here) */
/* 0x20: table3 count */
/* 0x24: table3 offset (from here) */
/* 0x28: table4 count */
/* 0x2c: table4 offset (from here) */
/* 0x30: table5 count (waves) */
/* 0x34: table5 offset (from here) */
/* 0x38: table6 count */
/* 0x3c: table6 offset (from here) */
/* 0x40: table7 count */
/* 0x44: table7 offset (from here) */
/* 0x48: data size */
/* 0x4c: data offset (absolute) */
musx->tables_offset = 0x800;
if (!is_id32be(musx->tables_offset+0x00, sf, "SBNK"))
goto fail;
musx->total_subsongs = read_u32(musx->tables_offset+0x30, sf);
if (target_subsong == 0) target_subsong = 1;
if (target_subsong > musx->total_subsongs || musx->total_subsongs <= 0) goto fail;
head_offset = read_u32(musx->tables_offset+0x34, sf) + musx->tables_offset + 0x34;
data_offset = read_u32(musx->tables_offset+0x4c, sf);
target_offset = head_offset + (target_subsong - 1)*0x1c;
;VGM_LOG("MUSX: ho=%lx, do=%lx, to=%lx\n", head_offset, data_offset, target_offset);
/* 0x00: subfile ID */
musx->num_samples = read_s32(target_offset + 0x04, sf);
musx->loop_start_sample = read_s32(target_offset + 0x08, sf); /* -1 if no loop */
musx->sample_rate = read_u16(target_offset + 0x0c, sf);
codec = read_u8 (target_offset + 0x0e, sf);
musx->channels = read_u8 (target_offset + 0x0f, sf);
musx->stream_offset = read_u32(target_offset + 0x10, sf) + data_offset;
musx->stream_size = read_u32(target_offset + 0x14, sf);
musx->loop_start = read_s32(target_offset + 0x18, sf);
musx->loop_end_sample = musx->num_samples;
musx->loop_flag = (musx->loop_start_sample >= 0);
switch(codec) {
case 0x11: musx->codec = DAT; break;
case 0x13: musx->codec = NGCA; break;
case 0x14: musx->codec = PCM; break;
default: default:
VGM_LOG("MUSX: unknown form\n"); VGM_LOG("MUSX: unknown SBNK codec %x\n", codec);
goto fail;
}
break;
}
default:
VGM_LOG("MUSX: unsupported form\n");
goto fail; goto fail;
} }

View File

@ -178,13 +178,18 @@ static VGMSTREAM* init_vgmstream_dsp_common(STREAMFILE* sf, dsp_meta *dspm) {
for (i = 0; i < channels; i++) { for (i = 0; i < channels; i++) {
off_t loop_offset = ch_header[i].loop_start_offset; 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 */ /* Loop offset points to a nibble, but we need closest frame header.
if (dspm->interleave) { * Stereo doesn't always point to a proper offset unless de-adjusted with interleave first. */
if (!dspm->interleave) {
loop_offset = loop_offset / 0x08 * 0x8;
}
else {
loop_offset = loop_offset / 0x10 * 0x8;
loop_offset = (loop_offset / dspm->interleave * dspm->interleave * channels) + (loop_offset % dspm->interleave); loop_offset = (loop_offset / dspm->interleave * dspm->interleave * channels) + (loop_offset % dspm->interleave);
} }
if (ch_header[i].loop_ps != read_u8(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); //;VGM_LOG("DSP: ch%i bad loop ps: %x vs at %lx\n", i, ch_header[i].loop_ps, dspm->start_offset + i*dspm->interleave + loop_offset);
goto fail; goto fail;
} }
} }
@ -660,8 +665,6 @@ VGMSTREAM* init_vgmstream_sadb(STREAMFILE* sf) {
dspm.start_offset = read_32bitBE(0x48,sf); dspm.start_offset = read_32bitBE(0x48,sf);
dspm.interleave = 0x10; dspm.interleave = 0x10;
dspm.ignore_loop_ps = 1; /* does something strange with offsets/etc, ignore */
dspm.meta_type = meta_DSP_SADB; dspm.meta_type = meta_DSP_SADB;
return init_vgmstream_dsp_common(sf, &dspm); return init_vgmstream_dsp_common(sf, &dspm);
fail: fail:
@ -1370,7 +1373,6 @@ VGMSTREAM* init_vgmstream_dsp_cwac(STREAMFILE* sf) {
dspm.max_channels = 2; dspm.max_channels = 2;
dspm.header_spacing = dspm.interleave; dspm.header_spacing = dspm.interleave;
dspm.start_offset = dspm.header_offset + 0x60; 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; dspm.meta_type = meta_DSP_CWAC;
return init_vgmstream_dsp_common(sf, &dspm); return init_vgmstream_dsp_common(sf, &dspm);