Merge pull request #926 from bnnm/xsh-lyn-etc

- Fix .sgt DMSG with streams
- Fix Ubi Lyn .wav MPEG last interleave
- Fix EA .WVE with PCM8 [Madden NHL 97 (PC)]
- Add HCA key [m HOLD'EM (Android)]
- Add .xsh+xsd/xss [KS's Pro Surfer (Xbox)]
- Fix CLI smpl chunk with subsongs
- Fix some Ubi BAO [Just Dance (Wii)]
This commit is contained in:
bnnm 2021-08-22 13:20:44 +02:00 committed by GitHub
commit 49dfc218a8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 649 additions and 291 deletions

View File

@ -58,7 +58,7 @@ export RMF SHELL CC AR STRIP WINDRES DLLTOOL
############################################################################### ###############################################################################
### build defs ### build defs
DEF_CFLAGS += -ffast-math -O3 -Wall -Werror=format-security -Wlogical-op -Wdeclaration-after-statement -Wvla -Wimplicit-function-declaration -Wignored-qualifiers DEF_CFLAGS += -ffast-math -O3 -Wall -Werror=format-security -Wdeclaration-after-statement -Wvla -Wimplicit-function-declaration -Wignored-qualifiers
VGM_DEBUG_FLAGS = 0 VGM_DEBUG_FLAGS = 0
ifeq ($(VGM_DEBUG_FLAGS),1) ifeq ($(VGM_DEBUG_FLAGS),1)
@ -67,6 +67,7 @@ ifeq ($(VGM_DEBUG_FLAGS),1)
DEF_CFLAGS += -Wall DEF_CFLAGS += -Wall
DEF_CFLAGS += -Wextra DEF_CFLAGS += -Wextra
DEF_CFLAGS += -Wno-sign-compare DEF_CFLAGS += -Wno-sign-compare
DEF_CFLAGS += -Wlogical-op
#DEF_CFLAGS += -pedantic -Wconversion -std=gnu90 #DEF_CFLAGS += -pedantic -Wconversion -std=gnu90
#DEF_CFLAGS += -Wfloat-equal #DEF_CFLAGS += -Wfloat-equal
DEF_CFLAGS += -Wdisabled-optimization -Wunsafe-loop-optimizations -Wswitch-default DEF_CFLAGS += -Wdisabled-optimization -Wunsafe-loop-optimizations -Wswitch-default
@ -77,6 +78,7 @@ ifeq ($(VGM_DEBUG_FLAGS),1)
DEF_CFLAGS += -Wredundant-decls -Wmissing-include-dirs -Wmissing-declarations DEF_CFLAGS += -Wredundant-decls -Wmissing-include-dirs -Wmissing-declarations
#DEF_CFLAGS += -Wshadow #DEF_CFLAGS += -Wshadow
#DEF_CFLAGS += -Wstack-protector -fstack-protector #DEF_CFLAGS += -Wstack-protector -fstack-protector
STRIP = echo
endif endif
LIBS_CFLAGS= LIBS_CFLAGS=

View File

@ -150,7 +150,7 @@ Since XMPlay supports Winamp plugins you may also use `in_vgmstream.dll` instead
Because the XMPlay MP3 decoder incorrectly tries to play some vgmstream extensions, Because the XMPlay MP3 decoder incorrectly tries to play some vgmstream extensions,
you need to manually fix it by going to **options > plugins > input > vgmstream** you need to manually fix it by going to **options > plugins > input > vgmstream**
and in the "priority filetypes" put: `ahx,asf,awc,ckd,fsb,genh,msf,p3d,rak,scd,txth,xvag` and in the "priority filetypes" put: `ahx,asf,awc,ckd,fsb,genh,lwav,msf,p3d,rak,scd,txth,xvag`
XMPlay cannot support subsongs due to player limitations (with any plugin), try XMPlay cannot support subsongs due to player limitations (with any plugin), try
using *TXTP* instead (explained below). using *TXTP* instead (explained below).

View File

@ -377,7 +377,11 @@ static void apply_config(VGMSTREAM* vgmstream, cli_config* cfg) {
cfg->lwav_loop_end = vgmstream->loop_end_sample; cfg->lwav_loop_end = vgmstream->loop_end_sample;
cfg->lwav_loop_end--; /* from spec, +1 is added when reading "smpl" */ cfg->lwav_loop_end--; /* from spec, +1 is added when reading "smpl" */
} }
else {
/* reset for subsongs */
cfg->lwav_loop_start = 0;
cfg->lwav_loop_end = 0;
}
} }
/* only allowed if manually active */ /* only allowed if manually active */
if (cfg->play_forever) { if (cfg->play_forever) {

View File

@ -444,8 +444,10 @@ typedef struct {
int channels; /* max channels */ int channels; /* max channels */
int fsb_padding; /* fsb padding mode */ int fsb_padding; /* fsb padding mode */
int chunk_size; /* size of a data portion */ int chunk_size; /* size of a data portion */
int max_chunks;
int data_size; /* playable size */ int data_size; /* playable size */
int interleave; /* size of stream interleave */ int interleave; /* size of stream interleave */
int interleave_last;
int encryption; /* encryption mode */ int encryption; /* encryption mode */
int big_endian; int big_endian;
int skip_samples; int skip_samples;

View File

@ -51,6 +51,7 @@ int mpeg_custom_setup_init_default(STREAMFILE* sf, off_t start_offset, mpeg_code
} }
break; break;
//todo simplify/unify XVAG/P3D/SCD/LYN and just feed arbitrary chunks to the decoder
case MPEG_P3D: case MPEG_P3D:
case MPEG_SCD: case MPEG_SCD:
if (data->config.interleave <= 0) if (data->config.interleave <= 0)
@ -61,7 +62,8 @@ int mpeg_custom_setup_init_default(STREAMFILE* sf, off_t start_offset, mpeg_code
if (data->config.interleave <= 0) if (data->config.interleave <= 0)
goto fail; /* needs external fixed size */ goto fail; /* needs external fixed size */
data->default_buffer_size = data->config.interleave; data->default_buffer_size = data->config.interleave;
//todo simplify/unify XVAG/P3D/SCD/LYN and just feed arbitrary chunks to the decoder if (data->default_buffer_size < data->config.interleave_last)
data->default_buffer_size = data->config.interleave_last;
break; break;
case MPEG_STANDARD: case MPEG_STANDARD:
@ -179,7 +181,6 @@ int mpeg_custom_parse_frame_default(VGMSTREAMCHANNEL* stream, mpeg_codec_data* d
case MPEG_P3D: /* fixed interleave, not frame-aligned (ie. blocks may end/start in part of a frame) */ case MPEG_P3D: /* fixed interleave, not frame-aligned (ie. blocks may end/start in part of a frame) */
case MPEG_SCD: case MPEG_SCD:
case MPEG_LYN:
current_interleave = data->config.interleave; current_interleave = data->config.interleave;
/* check if current interleave block is short */ /* check if current interleave block is short */
@ -197,6 +198,23 @@ int mpeg_custom_parse_frame_default(VGMSTREAMCHANNEL* stream, mpeg_codec_data* d
current_data_size = current_interleave; current_data_size = current_interleave;
break; break;
case MPEG_LYN:
/* after N interleaves last block is bigger */
if (ms->current_size_count < data->config.max_chunks)
current_interleave = data->config.interleave;
else if (ms->current_size_count == data->config.max_chunks)
current_interleave = data->config.interleave_last;
else
goto fail;
current_interleave_pre = current_interleave*num_stream;
current_interleave_post = current_interleave*(data->streams_size-1) - current_interleave_pre;
//VGM_LOG("o=%lx, %i: %x, %x, %x, %x\n", stream->offset, num_stream, ms->current_size_count, current_interleave, current_interleave_pre, current_interleave_post );
current_data_size = current_interleave;
ms->current_size_count++;
break;
default: /* standard frames (CBR or VBR) */ default: /* standard frames (CBR or VBR) */
if ( !mpeg_get_frame_info(stream->streamfile, stream->offset, &info) ) if ( !mpeg_get_frame_info(stream->streamfile, stream->offset, &info) )
goto fail; goto fail;

View File

@ -144,7 +144,11 @@ mpeg_codec_data* init_mpeg_custom(STREAMFILE* sf, off_t start_offset, coding_t*
if (channels <= 0 || channels > 16) goto fail; /* arbitrary max */ if (channels <= 0 || channels > 16) goto fail; /* arbitrary max */
if (channels < data->channels_per_frame) goto fail; if (channels < data->channels_per_frame) goto fail;
//todo simplify/unify XVAG/P3D/SCD/LYN and just feed arbitrary chunks to the decoder //todo simplify/unify XVAG/P3D/SCD/LYN and just feed arbitrary chunks to the decoder
if (data->default_buffer_size > 0x10000) goto fail; /* max for some Ubi Lyn */ /* max for some Ubi Lyn */
if (data->default_buffer_size > 0x20000) {
VGM_LOG("MPEG: buffer size too big %x\n", data->default_buffer_size);
goto fail;
}
/* init streams */ /* init streams */

View File

@ -238,9 +238,9 @@ static void init_dequantization(float* scales) {
static int unpack_frame(uint8_t* buf, int buf_size, float* freq1, float* freq2, const float* scales, uint8_t* exponents, int freq_size) { static int unpack_frame(uint8_t* buf, int buf_size, float* freq1, float* freq2, const float* scales, uint8_t* exponents, int freq_size) {
uint8_t flags, cb_bits, ev_bits, ei_bits, qv_bits; uint8_t flags, cb_bits, ev_bits, ei_bits, qv_bits;
int qv; int qv, pos;
uint8_t ev; uint8_t ev;
uint8_t move, pos; uint8_t move;
uint32_t bit_offset, max_offset; uint32_t bit_offset, max_offset;
int i, j; int i, j;
int freq_half = freq_size >> 1; int freq_half = freq_size >> 1;

View File

@ -155,7 +155,7 @@ static const char* extension_list[] = {
"de2", "de2",
"dec", "dec",
"diva", "diva",
"dmsg", "dmsg", //fake extension/header id for .sgt (to be removed)
"ds2", //txth/reserved [Star Wars Bounty Hunter (GC)] "ds2", //txth/reserved [Star Wars Bounty Hunter (GC)]
"dsb", "dsb",
"dsf", "dsf",
@ -402,7 +402,7 @@ static const char* extension_list[] = {
"rad", "rad",
"rak", "rak",
"ras", "ras",
"raw", "raw", //txth/reserved [Madden NHL 97 (PC)-pcm8u]
"rda", //FFmpeg/reserved [Rhythm Destruction (PC)] "rda", //FFmpeg/reserved [Rhythm Destruction (PC)]
"rkv", "rkv",
"rnd", "rnd",
@ -464,6 +464,7 @@ static const char* extension_list[] = {
"sfx", "sfx",
"sgb", "sgb",
"sgd", "sgd",
"sgt",
"sgx", "sgx",
"sl3", "sl3",
"slb", //txth/reserved [THE Nekomura no Hitobito (PS2)] "slb", //txth/reserved [THE Nekomura no Hitobito (PS2)]
@ -611,6 +612,7 @@ static const char* extension_list[] = {
"xma2", "xma2",
"xmu", "xmu",
"xnb", "xnb",
"xsh",
"xsf", "xsf",
"xse", "xse",
"xsew", "xsew",
@ -1114,7 +1116,7 @@ static const meta_info meta_info_list[] = {
{meta_HIS, "Her Interactive HIS header"}, {meta_HIS, "Her Interactive HIS header"},
{meta_PS2_AST, "KOEI AST header"}, {meta_PS2_AST, "KOEI AST header"},
{meta_CAPDSP, "Capcom DSP header"}, {meta_CAPDSP, "Capcom DSP header"},
{meta_DMSG, "RIFF/DMSGsegh header"}, {meta_DMSG, "Microsoft RIFF DMSG header"},
{meta_PONA_3DO, "Policenauts BGM header"}, {meta_PONA_3DO, "Policenauts BGM header"},
{meta_PONA_PSX, "Policenauts BGM header"}, {meta_PONA_PSX, "Policenauts BGM header"},
{meta_NGC_DSP_AAAP, "Acclaim Austin AAAp DSP header"}, {meta_NGC_DSP_AAAP, "Acclaim Austin AAAp DSP header"},
@ -1357,6 +1359,7 @@ static const meta_info meta_info_list[] = {
{meta_PIFF_TPCM, "Tantalus PIFF TPCM header"}, {meta_PIFF_TPCM, "Tantalus PIFF TPCM header"},
{meta_WXD_WXH, "Relic WXD+WXH header"}, {meta_WXD_WXH, "Relic WXD+WXH header"},
{meta_BNK_RELIC, "Relic BNK header"}, {meta_BNK_RELIC, "Relic BNK header"},
{meta_XSH_XSD_XSS, "Treyarch XSH+XSD/XSS header"},
}; };
void get_vgmstream_coding_description(VGMSTREAM* vgmstream, char* out, size_t out_size) { void get_vgmstream_coding_description(VGMSTREAM* vgmstream, char* out, size_t out_size) {

View File

@ -1,6 +1,7 @@
#include "layout.h" #include "layout.h"
#include "../vgmstream.h" #include "../vgmstream.h"
#include "../decode.h" #include "../decode.h"
#include "../coding/coding.h"
/* Decodes samples for blocked streams. /* Decodes samples for blocked streams.
@ -90,7 +91,7 @@ void render_vgmstream_blocked(sample_t* buffer, int32_t sample_count, VGMSTREAM*
} }
/* helper functions to parse new block */ /* helper functions to parse new block */
void block_update(off_t block_offset, VGMSTREAM * vgmstream) { void block_update(off_t block_offset, VGMSTREAM* vgmstream) {
switch (vgmstream->layout_type) { switch (vgmstream->layout_type) {
case layout_blocked_ast: case layout_blocked_ast:
block_update_ast(block_offset,vgmstream); block_update_ast(block_offset,vgmstream);
@ -216,3 +217,38 @@ void block_update(off_t block_offset, VGMSTREAM * vgmstream) {
break; break;
} }
} }
void blocked_count_samples(VGMSTREAM* vgmstream, STREAMFILE* sf, off_t offset) {
int block_samples;
off_t max_offset = get_streamfile_size(sf);
vgmstream->next_block_offset = offset;
do {
block_update(vgmstream->next_block_offset, vgmstream);
if (vgmstream->current_block_samples < 0 || vgmstream->current_block_size == 0xFFFFFFFF)
break;
if (vgmstream->current_block_samples) {
block_samples = vgmstream->current_block_samples;
}
else {
switch(vgmstream->coding_type) {
case coding_PCM16_int: block_samples = pcm16_bytes_to_samples(vgmstream->current_block_size, 1); break;
case coding_PCM8_int:
case coding_PCM8_U_int: block_samples = pcm8_bytes_to_samples(vgmstream->current_block_size, 1); break;
case coding_XBOX_IMA: block_samples = xbox_ima_bytes_to_samples(vgmstream->current_block_size, 1); break;
case coding_NGC_DSP: block_samples = dsp_bytes_to_samples(vgmstream->current_block_size, 1); break;
case coding_PSX: block_samples = ps_bytes_to_samples(vgmstream->current_block_size,1); break;
default:
VGM_LOG("BLOCKED: missing codec\n");
return;
}
}
vgmstream->num_samples += block_samples;
}
while (vgmstream->next_block_offset < max_offset);
block_update(offset, vgmstream); /* reset */
}

View File

@ -6,17 +6,20 @@
void block_update_ea_wve_ad10(off_t block_offset, VGMSTREAM* vgmstream) { void block_update_ea_wve_ad10(off_t block_offset, VGMSTREAM* vgmstream) {
STREAMFILE* sf = vgmstream->ch[0].streamfile; STREAMFILE* sf = vgmstream->ch[0].streamfile;
int i; int i;
size_t block_size, channel_size = 0, interleave = 0; size_t channel_size = 0, interleave = 0;
uint32_t block_id; uint32_t block_id, block_size;
block_id = read_32bitBE(block_offset+0x00, sf); int flag_be = (vgmstream->codec_config & 0x01);
block_size = read_32bitBE(block_offset+0x04, sf); uint32_t (*read_u32)(off_t,STREAMFILE*) = flag_be ? read_u32be : read_u32le;
block_id = read_u32be(block_offset+0x00, sf);
block_size = read_u32 (block_offset+0x04, sf);
/* accept "Ad10/Ad11" audio block/footer */ /* accept "Ad10/Ad11" audio block/footer */
if (block_id == 0x41643130 || block_id == 0x41643131) { if (block_id == 0x41643130 || block_id == 0x41643131) {
channel_size = block_size - 0x08; /* one block per channel */ channel_size = block_size - 0x08; /* one block per channel */
interleave = block_size; interleave = block_size;
block_size = block_size*vgmstream->channels; block_size = block_size * vgmstream->channels;
} }
/* rest could be "MDEC" video blocks with 0 size/samples */ /* rest could be "MDEC" video blocks with 0 size/samples */

View File

@ -7,6 +7,7 @@
/* blocked layouts */ /* blocked layouts */
void render_vgmstream_blocked(sample_t* buffer, int32_t sample_count, VGMSTREAM* vgmstream); void render_vgmstream_blocked(sample_t* buffer, int32_t sample_count, VGMSTREAM* vgmstream);
void block_update(off_t block_offset, VGMSTREAM* vgmstream); void block_update(off_t block_offset, VGMSTREAM* vgmstream);
void blocked_count_samples(VGMSTREAM* vgmstream, STREAMFILE* sf, off_t offset);
void block_update_ast(off_t block_ofset, VGMSTREAM* vgmstream); void block_update_ast(off_t block_ofset, VGMSTREAM* vgmstream);
void block_update_mxch(off_t block_ofset, VGMSTREAM* vgmstream); void block_update_mxch(off_t block_ofset, VGMSTREAM* vgmstream);

View File

@ -589,6 +589,7 @@
<ClCompile Include="meta\xpcm.c" /> <ClCompile Include="meta\xpcm.c" />
<ClCompile Include="meta\xps.c" /> <ClCompile Include="meta\xps.c" />
<ClCompile Include="meta\xse.c" /> <ClCompile Include="meta\xse.c" />
<ClCompile Include="meta\xsh_xsd_xss.c" />
<ClCompile Include="meta\xss.c" /> <ClCompile Include="meta\xss.c" />
<ClCompile Include="meta\xssb.c" /> <ClCompile Include="meta\xssb.c" />
<ClCompile Include="meta\xvag.c" /> <ClCompile Include="meta\xvag.c" />

View File

@ -1828,6 +1828,9 @@
<ClCompile Include="meta\xse.c"> <ClCompile Include="meta\xse.c">
<Filter>meta\Source Files</Filter> <Filter>meta\Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="meta\xsh_xsd_xss.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\x360_cxs.c"> <ClCompile Include="meta\x360_cxs.c">
<Filter>meta\Source Files</Filter> <Filter>meta\Source Files</Filter>
</ClCompile> </ClCompile>

View File

@ -28,9 +28,9 @@ VGMSTREAM* init_vgmstream_cpk_memory(STREAMFILE* sf, STREAMFILE* sf_acb) {
/* checks */ /* checks */
if (!check_extensions(sf, "awb")) if (!check_extensions(sf, "awb"))
goto fail; goto fail;
if (read_u32be(0x00,sf) != 0x43504B20) /* "CPK " */ if (!is_id32be(0x00,sf, "CPK "))
goto fail; goto fail;
if (read_u32be(0x10,sf) != 0x40555446) /* "@UTF" */ if (!is_id32be(0x10,sf, "@UTF"))
goto fail; goto fail;
/* 04: 0xFF? */ /* 04: 0xFF? */
/* 08: 0x02A0? */ /* 08: 0x02A0? */
@ -176,23 +176,21 @@ VGMSTREAM* init_vgmstream_cpk_memory(STREAMFILE* sf, STREAMFILE* sf_acb) {
//;VGM_LOG("CPK: subfile offset=%lx + %x, id=%i\n", subfile_offset, subfile_size, subfile_id); //;VGM_LOG("CPK: subfile offset=%lx + %x, id=%i\n", subfile_offset, subfile_size, subfile_id);
if ((read_u32be(subfile_offset,sf) & 0x7f7f7f7f) == 0x48434100) { /* "HCA\0" */ if ((read_u32be(subfile_offset,sf) & 0x7f7f7f7f) == get_id32be("HCA\0")) {
type = HCA; type = HCA;
extension = "hca"; extension = "hca";
} }
else if (read_u32be(subfile_offset,sf) == 0x43574156) { /* "CWAV" */ else if (is_id32be(subfile_offset,sf, "CWAV")) {
type = CWAV; type = CWAV;
extension = "bcwav"; extension = "bcwav";
} }
else if (read_u16be(subfile_offset, sf) == 0x8000) { else if (read_u16be(subfile_offset, sf) == 0x8000) {
off_t test_offset = subfile_offset + read_u16be(subfile_offset + 0x02, sf) + 0x04;
if (read_u16be(test_offset - 0x06, sf) != 0x2863 || /* "(c" */
read_u32be(test_offset - 0x04, sf) != 0x29435249) /* ")CRI" */
goto fail;
type = ADX; type = ADX;
extension = "adx"; extension = "adx";
} }
else {
goto fail;
}
temp_sf = setup_subfile_streamfile(sf, subfile_offset, subfile_size, extension); temp_sf = setup_subfile_streamfile(sf, subfile_offset, subfile_size, extension);
if (!temp_sf) goto fail; if (!temp_sf) goto fail;

View File

@ -1,110 +1,159 @@
#include "meta.h" #include "meta.h"
#include "../util.h" #include "../coding/coding.h"
/* DMSG typedef struct {
found in: Nightcaster II - Equinox uint32_t type;
2010-01-05 (manakoAT): Seems it's a corrupted "SGT" file, but I'm not sure... uint32_t size;
*/ uint32_t offset;
VGMSTREAM * init_vgmstream_dmsg(STREAMFILE *streamFile) { off_t current;
VGMSTREAM * vgmstream = NULL; off_t max;
char filename[PATH_LIMIT]; int le_type;
int loop_flag = 0; int be_size;
int frequency; int full_size;
int channel_count; } chunk_t;
int dataBuffer = 0;
int Founddata = 0;
size_t file_size;
off_t current_chunk;
off_t start_offset;
/* check extension, case insensitive */ static int next_chunk(chunk_t* chunk, STREAMFILE* sf) {
streamFile->get_name(streamFile,filename,sizeof(filename)); uint32_t (*read_u32type)(off_t,STREAMFILE*) = !chunk->le_type ? read_u32be : read_u32le;
if (strcasecmp("dmsg",filename_extension(filename))) goto fail; uint32_t (*read_u32size)(off_t,STREAMFILE*) = chunk->be_size ? read_u32be : read_u32le;
/* check header */ if (chunk->max == 0)
if (read_32bitBE(0x00,streamFile) != 0x52494646) /* "RIFF" */ chunk->max = get_streamfile_size(sf);
goto fail;
if (read_32bitBE(0x08,streamFile) != 0x444D5347) /* "DMSG" */ if (chunk->current >= chunk->max)
goto fail; return 0;
if (read_32bitBE(0x0C,streamFile) != 0x73656768) /* "segh" */ /* can be used to signal "stop" */
goto fail; if (chunk->current < 0)
if (read_32bitBE(0x10,streamFile) != 0x38000000) /* "0x38" */ return 0;
chunk->type = read_u32type(chunk->current + 0x00,sf);
chunk->size = read_u32size(chunk->current + 0x04,sf);
chunk->offset = chunk->current + 0x04 + 0x04;
chunk->current += chunk->full_size ? chunk->size : 0x08 + chunk->size;
//;VGM_LOG("CHUNK: %x, %x, %x\n", dc.offset, chunk->type, chunk->size);
/* read past data */
if (chunk->type == 0xFFFFFFFF || chunk->size == 0xFFFFFFFF)
return 0;
/* empty chunk with 0 size, seen in some formats (XVAG uses it as end marker, Wwise doesn't) */
if (chunk->type == 0 || chunk->size == 0)
return 0;
/* more chunks remain */
return 1;
}
enum {
CHUNK_RIFF = 0x52494646, /* "RIFF" */
CHUNK_LIST = 0x4C495354, /* "LIST" */
CHUNK_segh = 0x73656768, /* "segh" */
};
/* DMSG - DirectMusic Segment with streams [Nightcaster II: Equinox (Xbox), Wildfire (PC)] */
VGMSTREAM* init_vgmstream_dmsg(STREAMFILE* sf) {
VGMSTREAM* vgmstream = NULL;
//int loop_flag, channels, sample_rate;
//int found_data = 0;
//int32_t num_samples, loop_start, loop_end;
//off_t start_offset;
off_t offset = 0, name_offset = 0, name_size = 0;
/* checks */
/* .sgt: common
* .dmsg: header id */
if (!check_extensions(sf, "sgt,dmsg"))
goto fail; goto fail;
/* scan file until we find a "data" string */ if (!is_id32be(0x00,sf, "RIFF"))
file_size = get_streamfile_size(streamFile); goto fail;
if (!is_id32be(0x08,sf, "DMSG"))
goto fail;
/* A DirectMusic segment usually has lots of chunks then data pointing to .dls soundbank.
* This accepts .sgt with a RIFF WAVE inside (less common). */
{ {
current_chunk = 0; chunk_t rc = {0};
/* Start at 0 and loop until we reached the chunk_t dc = {0};
file size, or until we found a "data string */
while (!Founddata && current_chunk < file_size) { rc.current = 0x0c;
dataBuffer = (read_32bitBE(current_chunk,streamFile)); while (next_chunk(&rc, sf)) {
if (dataBuffer == 0x64617461) { /* "data" */ switch(rc.type) {
/* if "data" string found, retrieve the needed infos */ /* "segh" has loopnum/samplesloop */
Founddata = 1; case CHUNK_segh:
/* We will cancel the search here if we have a match */ //todo: missing TMusicTime format
/* 0x00: dwRepeats (>0 or -1=inf)
* 0x04: mtLength
* 0x08: mtPlayStart
* 0x0c: mtLoopStart
* 0x10: mtLoopEnd
* 0x14: dwResolution
* 0x18: rtLength (optional)
* .. */
break;
case CHUNK_LIST:
if (is_id32be(rc.offset + 0x00, sf, "UNFO") && is_id32be(rc.offset + 0x04, sf, "UNAM")) {
name_offset = rc.offset + 0x0c;
name_size = read_u32le(rc.offset + 0x08, sf);
}
break;
case CHUNK_RIFF:
if (!is_id32be(rc.offset, sf, "DMCN"))
goto fail;
dc.current = rc.offset + 0x04;
while (next_chunk(&dc, sf)) {
switch(dc.type) {
case CHUNK_LIST:
/* abridged, there are some sublists */
if (is_id32be(dc.offset + 0x00, sf, "cosl") && is_id32be(dc.offset + 0x30, sf, "WAVE")) {
offset = dc.offset + 0x34;
dc.current = -1;
rc.current = -1;
}
break;
default:
break;
}
}
break;
default:
break; break;
} }
/* else we will increase the search offset by 1 */
current_chunk = current_chunk + 1;
} }
} }
if (Founddata == 0) { if (!offset)
goto fail; goto fail;
} else if (Founddata == 1) {
channel_count = (uint16_t)read_16bitLE(current_chunk-0x10,streamFile);
frequency = read_32bitLE(current_chunk-0xE,streamFile);
}
loop_flag = 1;
/* build the VGMSTREAM */ /* subfile has a few extra chunks (guid, wavh) but otherwise standard (seen PCM and MS-ADPCM, with fact chunks) */
vgmstream = allocate_vgmstream(channel_count,loop_flag); {
STREAMFILE* temp_sf = NULL;
off_t subfile_offset = offset;
size_t subfile_size = read_u32le(offset + 0x04, sf) + 0x08;
temp_sf = setup_subfile_streamfile(sf, subfile_offset, subfile_size, "wav");
if (!temp_sf) goto fail;
vgmstream = init_vgmstream_riff(temp_sf);
close_streamfile(temp_sf);
if (!vgmstream) goto fail; if (!vgmstream) goto fail;
/* fill in the vital statistics */ if (name_offset) {
if (Founddata == 0) { if (name_size >= STREAM_NAME_SIZE)
goto fail; name_size = STREAM_NAME_SIZE;
} else if (Founddata == 1) { read_string_utf16le(vgmstream->stream_name,name_size, name_offset, sf);
start_offset = current_chunk+0x8;
vgmstream->channels = channel_count;
vgmstream->sample_rate = frequency;
vgmstream->coding_type = coding_PCM16LE;
vgmstream->num_samples = (read_32bitLE(current_chunk+0x4,streamFile)/2/channel_count);
if (loop_flag) {
vgmstream->loop_start_sample = 0;
vgmstream->loop_end_sample = (read_32bitLE(current_chunk+0x4,streamFile)/2/channel_count);
}
}
if (channel_count == 1) {
vgmstream->layout_type = layout_none;
} else if (channel_count > 1) {
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = 0x2;
}
vgmstream->meta_type = meta_DMSG;
/* open the file for reading */
{
int i;
STREAMFILE * file;
file = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE);
if (!file) goto fail;
for (i=0;i<channel_count;i++) {
vgmstream->ch[i].streamfile = file;
vgmstream->ch[i].channel_start_offset=
vgmstream->ch[i].offset=start_offset+
vgmstream->interleave_block_size*i;
}
} }
return vgmstream; return vgmstream;
}
/* clean up anything we may have opened */
fail: fail:
if (vgmstream) close_vgmstream(vgmstream); close_vgmstream(vgmstream);
return NULL; return NULL;
} }

View File

@ -2,49 +2,63 @@
#include "../coding/coding.h" #include "../coding/coding.h"
#include "../layout/layout.h" #include "../layout/layout.h"
/* EA WVE (Ad10) - from Electronic Arts PS movies [Wing Commander 3/4 (PS)] */ /* EA WVE (Ad10) - from early Electronic Arts movies [Wing Commander 3/4 (PS1), Madden NHL 97 (PC)-w95] */
VGMSTREAM * init_vgmstream_ea_wve_ad10(STREAMFILE *streamFile) { VGMSTREAM* init_vgmstream_ea_wve_ad10(STREAMFILE* sf) {
VGMSTREAM * vgmstream = NULL; VGMSTREAM* vgmstream = NULL;
off_t start_offset; off_t start_offset;
int loop_flag, channel_count; int loop_flag, channels;
int big_endian, is_ps1;
/* checks */ /* checks */
if (!check_extensions(streamFile, "wve")) /* .wve: common
* .mov: Madden NHL 97 (also uses .wve) */
if (!check_extensions(sf, "wve,mov"))
goto fail; goto fail;
start_offset = 0x00; start_offset = 0x00;
if (read_32bitBE(start_offset, streamFile) != 0x41643130 && /* "Ad10" */ if (!is_id32be(0x00, sf, "AABB") && /* video block */
read_32bitBE(start_offset, streamFile) != 0x41643131) /* "Ad11" (last block, but could be first) */ !is_id32be(0x00, sf, "Ad10") && /* audio block */
!is_id32be(0x00, sf, "Ad11")) /* last audio block, but could be first */
goto fail; goto fail;
big_endian = guess_endianness32bit(0x04, sf);
if (is_id32be(0x00, sf, "AABB"))
start_offset += big_endian ? read_u32be(0x04, sf) : read_u32le(0x04, sf);
loop_flag = 0; loop_flag = 0;
if (ps_check_format(sf, start_offset + 0x08, 0x40)) {
/* no header = no channels, but seems if the first PS-ADPCM header is 00 then it's mono, somehow /* no header = no channels, but seems if the first PS-ADPCM header is 00 then it's mono, somehow
* (ex. Wing Commander 3 intro / Wing Commander 4 = stereo, rest of Wing Commander 3 = mono) */ * (ex. Wing Commander 3 intro / Wing Commander 4 = stereo, rest of Wing Commander 3 = mono) */
channel_count = read_8bit(start_offset+0x08,streamFile) != 0 ? 2 : 1; channels = read_u8(start_offset + 0x08,sf) != 0 ? 2 : 1;
is_ps1 = 1;
VGM_LOG("ps1");
}
else {
channels = 1;
is_ps1 = 0;
}
/* build the VGMSTREAM */ /* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count, loop_flag); vgmstream = allocate_vgmstream(channels, loop_flag);
if (!vgmstream) goto fail; if (!vgmstream) goto fail;
vgmstream->sample_rate = 22050; vgmstream->sample_rate = 22050;
vgmstream->meta_type = meta_EA_WVE_AD10; vgmstream->meta_type = meta_EA_WVE_AD10;
vgmstream->coding_type = coding_PSX;
vgmstream->layout_type = layout_blocked_ea_wve_ad10; vgmstream->layout_type = layout_blocked_ea_wve_ad10;
vgmstream->codec_config = big_endian;
if (is_ps1)
vgmstream->coding_type = coding_PSX;
else
vgmstream->coding_type = coding_PCM8_U_int;
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) if (!vgmstream_open_stream(vgmstream, sf, start_offset))
goto fail; goto fail;
/* calc num_samples manually */ blocked_count_samples(vgmstream, sf, start_offset);
{
vgmstream->next_block_offset = start_offset;
do {
block_update(vgmstream->next_block_offset,vgmstream);
vgmstream->num_samples += ps_bytes_to_samples(vgmstream->current_block_size, 1);
}
while (vgmstream->next_block_offset < get_streamfile_size(streamFile));
block_update(start_offset, vgmstream);
}
return vgmstream; return vgmstream;

View File

@ -719,6 +719,9 @@ static const hcakey_info hcakey_list[] = {
// Mini 4WD Hyper Dash Grand Prix (Android) // Mini 4WD Hyper Dash Grand Prix (Android)
{7957824642808300098}, // 6E6FDF59AB704242 {7957824642808300098}, // 6E6FDF59AB704242
// m HOLD'EM (Android)
{369211553984367}, // 00014FCBC385AF6F
}; };
#endif/*_HCA_KEYS_H_*/ #endif/*_HCA_KEYS_H_*/

View File

@ -4,6 +4,7 @@
#include "../vgmstream.h" #include "../vgmstream.h"
VGMSTREAM* init_vgmstream_silence(int channels, int sample_rate, int32_t num_samples); VGMSTREAM* init_vgmstream_silence(int channels, int sample_rate, int32_t num_samples);
VGMSTREAM* init_vgmstream_silence_container(int total_subsongs);
VGMSTREAM* init_vgmstream_adx(STREAMFILE* sf); VGMSTREAM* init_vgmstream_adx(STREAMFILE* sf);
@ -959,4 +960,6 @@ VGMSTREAM* init_vgmstream_wxd_wxh(STREAMFILE* sf);
VGMSTREAM* init_vgmstream_bnk_relic(STREAMFILE* sf); VGMSTREAM* init_vgmstream_bnk_relic(STREAMFILE* sf);
VGMSTREAM* init_vgmstream_xsh_xsd_xss(STREAMFILE* sf);
#endif /*_META_H*/ #endif /*_META_H*/

View File

@ -27,8 +27,8 @@ static void parse_adtl(off_t adtl_offset, off_t adtl_length, STREAMFILE* sf, lon
off_t current_chunk = adtl_offset+0x04; off_t current_chunk = adtl_offset+0x04;
while (current_chunk < adtl_offset + adtl_length) { while (current_chunk < adtl_offset + adtl_length) {
uint32_t chunk_type = read_32bitBE(current_chunk+0x00,sf); uint32_t chunk_type = read_u32be(current_chunk+0x00,sf);
off_t chunk_size = read_32bitLE(current_chunk+0x04,sf); uint32_t chunk_size = read_u32le(current_chunk+0x04,sf);
if (current_chunk+0x08+chunk_size > adtl_offset+adtl_length) if (current_chunk+0x08+chunk_size > adtl_offset+adtl_length)
return; return;
@ -81,7 +81,7 @@ typedef struct {
off_t size; off_t size;
uint32_t codec; uint32_t codec;
int sample_rate; int sample_rate;
int channel_count; int channels;
uint32_t block_size; uint32_t block_size;
int bps; int bps;
off_t extra_size; off_t extra_size;
@ -95,38 +95,38 @@ typedef struct {
int is_at9; int is_at9;
} riff_fmt_chunk; } riff_fmt_chunk;
static int read_fmt(int big_endian, STREAMFILE* sf, off_t current_chunk, riff_fmt_chunk* fmt, int mwv) { static int read_fmt(int big_endian, STREAMFILE* sf, off_t offset, riff_fmt_chunk* fmt, int mwv) {
int32_t (*read_32bit)(off_t,STREAMFILE*) = big_endian ? read_32bitBE : read_32bitLE; uint32_t (*read_u32)(off_t,STREAMFILE*) = big_endian ? read_u32be : read_u32le;
int16_t (*read_16bit)(off_t,STREAMFILE*) = big_endian ? read_16bitBE : read_16bitLE; uint16_t (*read_u16)(off_t,STREAMFILE*) = big_endian ? read_u16be : read_u16le;
fmt->offset = current_chunk; fmt->offset = offset;
fmt->size = read_32bit(current_chunk+0x04,sf); fmt->size = read_u32(offset+0x04,sf);
/* WAVEFORMAT */ /* WAVEFORMAT */
fmt->codec = (uint16_t)read_16bit(current_chunk+0x08,sf); fmt->codec = read_u16(offset+0x08,sf);
fmt->channel_count = read_16bit(current_chunk+0x0a,sf); fmt->channels = read_u16(offset+0x0a,sf);
fmt->sample_rate = read_32bit(current_chunk+0x0c,sf); fmt->sample_rate = read_u32(offset+0x0c,sf);
//fmt->avg_bps = read_32bit(current_chunk+0x10,sf); //fmt->avg_bps = read_u32(offset+0x10,sf);
fmt->block_size = read_16bit(current_chunk+0x14,sf); fmt->block_size = read_u16(offset+0x14,sf);
fmt->bps = read_16bit(current_chunk+0x16,sf); fmt->bps = read_u16(offset+0x16,sf);
/* WAVEFORMATEX */ /* WAVEFORMATEX */
if (fmt->size >= 0x10) { if (fmt->size >= 0x10) {
fmt->extra_size = read_16bit(current_chunk+0x18,sf); fmt->extra_size = read_u16(offset+0x18,sf);
/* 0x1a+ depends on codec (ex. coef table for MSADPCM, samples_per_frame in MS-IMA, etc) */ /* 0x1a+ depends on codec (ex. coef table for MSADPCM, samples_per_frame in MS-IMA, etc) */
} }
/* WAVEFORMATEXTENSIBLE */ /* WAVEFORMATEXTENSIBLE */
if (fmt->codec == 0xFFFE && fmt->extra_size >= 0x16) { if (fmt->codec == 0xFFFE && fmt->extra_size >= 0x16) {
//fmt->extra_samples = read_16bit(current_chunk+0x1a,sf); /* valid_bits_per_sample or samples_per_block */ //fmt->extra_samples = read_u16(offset+0x1a,sf); /* valid_bits_per_sample or samples_per_block */
fmt->channel_layout = read_32bit(current_chunk+0x1c,sf); fmt->channel_layout = read_u32(offset+0x1c,sf);
/* 0x10 guid at 0x20 */ /* 0x10 guid at 0x20 */
/* happens in various .at3/at9, may be a bug in their encoder b/c MS's defs set mono as FC */ /* happens in various .at3/at9, may be a bug in their encoder b/c MS's defs set mono as FC */
if (fmt->channel_count == 1 && fmt->channel_layout == speaker_FL) { /* other channels are fine */ if (fmt->channels == 1 && fmt->channel_layout == speaker_FL) { /* other channels are fine */
fmt->channel_layout = speaker_FC; fmt->channel_layout = speaker_FC;
} }
/* happens in few at3p, may be a bug in older tools as other games have ok flags [Ridge Racer 7 (PS3)] */ /* happens in few at3p, may be a bug in older tools as other games have ok flags [Ridge Racer 7 (PS3)] */
if (fmt->channel_count == 6 && fmt->channel_layout == 0x013f) { if (fmt->channels == 6 && fmt->channel_layout == 0x013f) {
fmt->channel_layout = 0x3f; fmt->channel_layout = 0x3f;
} }
} }
@ -134,8 +134,8 @@ static int read_fmt(int big_endian, STREAMFILE* sf, off_t current_chunk, riff_fm
switch (fmt->codec) { switch (fmt->codec) {
case 0x00: /* Yamaha AICA ADPCM [Headhunter (DC), Bomber hehhe (DC), Rayman 2 (DC)] (unofficial) */ case 0x00: /* Yamaha AICA ADPCM [Headhunter (DC), Bomber hehhe (DC), Rayman 2 (DC)] (unofficial) */
if (fmt->bps != 4) goto fail; if (fmt->bps != 4) goto fail;
if (fmt->block_size != 0x02*fmt->channel_count && if (fmt->block_size != 0x02*fmt->channels &&
fmt->block_size != 0x01*fmt->channel_count) goto fail; fmt->block_size != 0x01*fmt->channels) goto fail;
fmt->coding_type = coding_AICA_int; fmt->coding_type = coding_AICA_int;
fmt->interleave = 0x01; fmt->interleave = 0x01;
break; break;
@ -161,7 +161,7 @@ static int read_fmt(int big_endian, STREAMFILE* sf, off_t current_chunk, riff_fm
if (!msadpcm_check_coefs(sf, fmt->offset + 0x08 + 0x14)) if (!msadpcm_check_coefs(sf, fmt->offset + 0x08 + 0x14))
goto fail; goto fail;
} }
else if (fmt->bps == 16 && fmt->block_size == 0x02 * fmt->channel_count && fmt->size == 0x14) { else if (fmt->bps == 16 && fmt->block_size == 0x02 * fmt->channels && fmt->size == 0x14) {
fmt->coding_type = coding_IMA; /* MX vs ATV Unleashed (PC) codec hijack */ fmt->coding_type = coding_IMA; /* MX vs ATV Unleashed (PC) codec hijack */
} }
else { else {
@ -201,9 +201,9 @@ static int read_fmt(int big_endian, STREAMFILE* sf, off_t current_chunk, riff_fm
case 0x0300: /* IMA ADPCM [Chrono Ma:gia (Android)] (unofficial) */ case 0x0300: /* IMA ADPCM [Chrono Ma:gia (Android)] (unofficial) */
if (fmt->bps != 4) goto fail; if (fmt->bps != 4) goto fail;
if (fmt->block_size != 0x0400*fmt->channel_count) goto fail; if (fmt->block_size != 0x0400*fmt->channels) goto fail;
if (fmt->size != 0x14) goto fail; if (fmt->size != 0x14) goto fail;
if (fmt->channel_count != 1) goto fail; /* not seen */ if (fmt->channels != 1) goto fail; /* not seen */
fmt->coding_type = coding_DVI_IMA; fmt->coding_type = coding_DVI_IMA;
/* real 0x300 is "Fujitsu FM Towns SND" with block align 0x01 */ /* real 0x300 is "Fujitsu FM Towns SND" with block align 0x01 */
break; break;
@ -218,25 +218,21 @@ static int read_fmt(int big_endian, STREAMFILE* sf, off_t current_chunk, riff_fm
case 0x6771: /* Ogg Vorbis (mode 3+) */ case 0x6771: /* Ogg Vorbis (mode 3+) */
fmt->coding_type = coding_OGG_VORBIS; fmt->coding_type = coding_OGG_VORBIS;
break; break;
#else
goto fail;
#endif #endif
case 0x0270: /* ATRAC3 */
#ifdef VGM_USE_FFMPEG #ifdef VGM_USE_FFMPEG
case 0x0270: /* ATRAC3 */
fmt->coding_type = coding_FFmpeg; fmt->coding_type = coding_FFmpeg;
fmt->is_at3 = 1; fmt->is_at3 = 1;
break; break;
#else
goto fail;
#endif #endif
case 0xFFFE: { /* WAVEFORMATEXTENSIBLE (see ksmedia.h for known GUIDs) */ case 0xFFFE: { /* WAVEFORMATEXTENSIBLE (see ksmedia.h for known GUIDs) */
uint32_t guid1 = (uint32_t)read_32bit (current_chunk+0x20,sf); uint32_t guid1 = read_u32 (offset+0x20,sf);
uint32_t guid2 = ((uint16_t)read_16bit (current_chunk+0x24,sf) << 16u) | uint32_t guid2 = (read_u16 (offset+0x24,sf) << 16u) |
((uint16_t)read_16bit (current_chunk+0x26,sf)); (read_u16 (offset+0x26,sf));
uint32_t guid3 = (uint32_t)read_32bitBE(current_chunk+0x28,sf); uint32_t guid3 = read_u32be(offset+0x28,sf);
uint32_t guid4 = (uint32_t)read_32bitBE(current_chunk+0x2c,sf); uint32_t guid4 = read_u32be(offset+0x2c,sf);
//;VGM_LOG("RIFF: guid %08x %08x %08x %08x\n", guid1, guid2, guid3, guid4); //;VGM_LOG("RIFF: guid %08x %08x %08x %08x\n", guid1, guid2, guid3, guid4);
/* PCM GUID (0x00000001,0000,0010,80,00,00,AA,00,38,9B,71) */ /* PCM GUID (0x00000001,0000,0010,80,00,00,AA,00,38,9B,71) */
@ -254,32 +250,30 @@ static int read_fmt(int big_endian, STREAMFILE* sf, off_t current_chunk, riff_fm
/* ATRAC3plus GUID (0xE923AABF,CB58,4471,A1,19,FF,FA,01,E4,CE,62) */ /* ATRAC3plus GUID (0xE923AABF,CB58,4471,A1,19,FF,FA,01,E4,CE,62) */
if (guid1 == 0xE923AABF && guid2 == 0xCB584471 && guid3 == 0xA119FFFA && guid4 == 0x01E4CE62) { if (guid1 == 0xE923AABF && guid2 == 0xCB584471 && guid3 == 0xA119FFFA && guid4 == 0x01E4CE62) {
#ifdef VGM_USE_MAIATRAC3PLUS #ifdef VGM_USE_FFMPEG
uint16_t bztmp = read_16bit(current_chunk+0x32,sf); fmt->coding_type = coding_FFmpeg;
fmt->is_at3p = 1;
break;
#elif defined(VGM_USE_MAIATRAC3PLUS)
uint16_t bztmp = read_u16(offset+0x32,sf);
bztmp = (bztmp >> 8) | (bztmp << 8); bztmp = (bztmp >> 8) | (bztmp << 8);
fmt->coding_type = coding_AT3plus; fmt->coding_type = coding_AT3plus;
fmt->block_size = (bztmp & 0x3FF) * 8 + 8; /* should match fmt->block_size */ fmt->block_size = (bztmp & 0x3FF) * 8 + 8; /* should match fmt->block_size */
fmt->is_at3p = 1; fmt->is_at3p = 1;
break; break;
#elif defined(VGM_USE_FFMPEG)
fmt->coding_type = coding_FFmpeg;
fmt->is_at3p = 1;
break;
#else #else
goto fail; goto fail;
#endif #endif
} }
#ifdef VGM_USE_ATRAC9
/* ATRAC9 GUID (0x47E142D2,36BA,4D8D,88,FC,61,65,4F,8C,83,6C) */ /* ATRAC9 GUID (0x47E142D2,36BA,4D8D,88,FC,61,65,4F,8C,83,6C) */
if (guid1 == 0x47E142D2 && guid2 == 0x36BA4D8D && guid3 == 0x88FC6165 && guid4 == 0x4F8C836C) { if (guid1 == 0x47E142D2 && guid2 == 0x36BA4D8D && guid3 == 0x88FC6165 && guid4 == 0x4F8C836C) {
#ifdef VGM_USE_ATRAC9
fmt->coding_type = coding_ATRAC9; fmt->coding_type = coding_ATRAC9;
fmt->is_at9 = 1; fmt->is_at9 = 1;
break; break;
#else
goto fail;
#endif
} }
#endif
goto fail; /* unknown GUID */ goto fail; /* unknown GUID */
} }
@ -370,7 +364,7 @@ VGMSTREAM* init_vgmstream_riff(STREAMFILE* sf) {
/* some games have wonky sizes, selectively fix to catch bad rips and new mutations */ /* some games have wonky sizes, selectively fix to catch bad rips and new mutations */
if (file_size != riff_size + 0x08) { if (file_size != riff_size + 0x08) {
uint16_t codec = read_16bitLE(0x14,sf); uint16_t codec = read_u16le(0x14,sf);
if (codec == 0x6771 && riff_size + 0x08 + 0x01 == file_size) if (codec == 0x6771 && riff_size + 0x08 + 0x01 == file_size)
riff_size += 0x01; /* [Shikkoku no Sharnoth (PC)] (Sony Sound Forge?) */ riff_size += 0x01; /* [Shikkoku no Sharnoth (PC)] (Sony Sound Forge?) */
@ -397,11 +391,10 @@ VGMSTREAM* init_vgmstream_riff(STREAMFILE* sf) {
riff_size += 0x18; /* [F1 2011 (Vita)] (adds a "pada" chunk but RIFF size wasn't updated) */ riff_size += 0x18; /* [F1 2011 (Vita)] (adds a "pada" chunk but RIFF size wasn't updated) */
else if (mwv) { else if (mwv) {
int channels = read_16bitLE(0x16, sf); /* [Dragon Quest VIII (PS2), Rogue Galaxy (PS2)] */ int channels = read_u16le(0x16, sf); /* [Dragon Quest VIII (PS2), Rogue Galaxy (PS2)] */
size_t file_size_fixed = riff_size + 0x08 + 0x04 * (channels - 1); size_t file_size_fixed = riff_size + 0x08 + 0x04 * (channels - 1);
if (file_size_fixed <= file_size && file_size - file_size_fixed < 0x10) if (file_size_fixed <= file_size && file_size - file_size_fixed < 0x10) {
{
/* files inside HD6/DAT are also padded to 0x10 so need to fix file_size */ /* files inside HD6/DAT are also padded to 0x10 so need to fix file_size */
file_size = file_size_fixed; file_size = file_size_fixed;
riff_size = file_size - 0x08; riff_size = file_size - 0x08;
@ -617,7 +610,7 @@ VGMSTREAM* init_vgmstream_riff(STREAMFILE* sf) {
/* build the VGMSTREAM */ /* build the VGMSTREAM */
vgmstream = allocate_vgmstream(fmt.channel_count,loop_flag); vgmstream = allocate_vgmstream(fmt.channels,loop_flag);
if (!vgmstream) goto fail; if (!vgmstream) goto fail;
vgmstream->sample_rate = fmt.sample_rate; vgmstream->sample_rate = fmt.sample_rate;
@ -661,7 +654,7 @@ VGMSTREAM* init_vgmstream_riff(STREAMFILE* sf) {
/* samples, codec init (after setting coding to ensure proper close on failure) */ /* samples, codec init (after setting coding to ensure proper close on failure) */
switch (fmt.coding_type) { switch (fmt.coding_type) {
case coding_PCM16LE: case coding_PCM16LE:
vgmstream->num_samples = pcm_bytes_to_samples(data_size, fmt.channel_count, 16); vgmstream->num_samples = pcm_bytes_to_samples(data_size, fmt.channels, 16);
break; break;
case coding_PCM8_U: case coding_PCM8_U:
@ -670,7 +663,7 @@ VGMSTREAM* init_vgmstream_riff(STREAMFILE* sf) {
case coding_L5_555: case coding_L5_555:
if (!mwv) goto fail; if (!mwv) goto fail;
vgmstream->num_samples = data_size / 0x12 / fmt.channel_count * 32; vgmstream->num_samples = data_size / 0x12 / fmt.channels * 32;
/* coefs */ /* coefs */
{ {
@ -684,7 +677,7 @@ VGMSTREAM* init_vgmstream_riff(STREAMFILE* sf) {
read_32bitLE(mwv_pflt_offset+0x04, sf) < 8 + filter_count * 4 * filter_order) read_32bitLE(mwv_pflt_offset+0x04, sf) < 8 + filter_count * 4 * filter_order)
goto fail; goto fail;
for (ch = 0; ch < fmt.channel_count; ch++) { for (ch = 0; ch < fmt.channels; ch++) {
for (i = 0; i < filter_count * filter_order; i++) { for (i = 0; i < filter_count * filter_order; i++) {
int coef = read_32bitLE(mwv_pflt_offset+0x10+i*0x04, sf); int coef = read_32bitLE(mwv_pflt_offset+0x10+i*0x04, sf);
vgmstream->ch[ch].adpcm_coef_3by32[i] = coef; vgmstream->ch[ch].adpcm_coef_3by32[i] = coef;
@ -695,31 +688,31 @@ VGMSTREAM* init_vgmstream_riff(STREAMFILE* sf) {
break; break;
case coding_MSADPCM: case coding_MSADPCM:
vgmstream->num_samples = msadpcm_bytes_to_samples(data_size, fmt.block_size, fmt.channel_count); vgmstream->num_samples = msadpcm_bytes_to_samples(data_size, fmt.block_size, fmt.channels);
if (fact_sample_count && fact_sample_count < vgmstream->num_samples) if (fact_sample_count && fact_sample_count < vgmstream->num_samples)
vgmstream->num_samples = fact_sample_count; vgmstream->num_samples = fact_sample_count;
break; break;
case coding_MS_IMA: case coding_MS_IMA:
vgmstream->num_samples = ms_ima_bytes_to_samples(data_size, fmt.block_size, fmt.channel_count); vgmstream->num_samples = ms_ima_bytes_to_samples(data_size, fmt.block_size, fmt.channels);
if (fact_sample_count && fact_sample_count < vgmstream->num_samples) if (fact_sample_count && fact_sample_count < vgmstream->num_samples)
vgmstream->num_samples = fact_sample_count; vgmstream->num_samples = fact_sample_count;
break; break;
case coding_AICA: case coding_AICA:
case coding_AICA_int: case coding_AICA_int:
vgmstream->num_samples = yamaha_bytes_to_samples(data_size, fmt.channel_count); vgmstream->num_samples = yamaha_bytes_to_samples(data_size, fmt.channels);
break; break;
case coding_XBOX_IMA: case coding_XBOX_IMA:
vgmstream->num_samples = xbox_ima_bytes_to_samples(data_size, fmt.channel_count); vgmstream->num_samples = xbox_ima_bytes_to_samples(data_size, fmt.channels);
if (fact_sample_count && fact_sample_count < vgmstream->num_samples) if (fact_sample_count && fact_sample_count < vgmstream->num_samples)
vgmstream->num_samples = fact_sample_count; /* some (converted?) Xbox games have bigger fact_samples */ vgmstream->num_samples = fact_sample_count; /* some (converted?) Xbox games have bigger fact_samples */
break; break;
case coding_IMA: case coding_IMA:
case coding_DVI_IMA: case coding_DVI_IMA:
vgmstream->num_samples = ima_bytes_to_samples(data_size, fmt.channel_count); vgmstream->num_samples = ima_bytes_to_samples(data_size, fmt.channels);
break; break;
#ifdef VGM_USE_FFMPEG #ifdef VGM_USE_FFMPEG
@ -873,7 +866,7 @@ fail:
static int is_ue4_msadpcm(STREAMFILE* sf, riff_fmt_chunk* fmt, int fact_sample_count, off_t start) { static int is_ue4_msadpcm(STREAMFILE* sf, riff_fmt_chunk* fmt, int fact_sample_count, off_t start) {
/* multichannel ok */ /* multichannel ok */
if (fmt->channel_count < 2) if (fmt->channels < 2)
goto fail; goto fail;
/* UE4 class is "ADPCM", assume it's the extension too */ /* UE4 class is "ADPCM", assume it's the extension too */
@ -902,7 +895,7 @@ static int is_ue4_msadpcm(STREAMFILE* sf, riff_fmt_chunk* fmt, int fact_sample_c
/* their encoder doesn't calculate optimal coefs and uses fixed values every frame /* their encoder doesn't calculate optimal coefs and uses fixed values every frame
* (could do it for fmt size 0x36 too but maybe they'll fix it in the future) */ * (could do it for fmt size 0x36 too but maybe they'll fix it in the future) */
while (offset <= max_offset) { while (offset <= max_offset) {
if (read_8bit(offset+0x00, sf) != 0 || read_16bitLE(offset+0x01, sf) != 0x00E6) if (read_u8(offset+0x00, sf) != 0 || read_u16le(offset+0x01, sf) != 0x00E6)
goto fail; goto fail;
offset += fmt->block_size; offset += fmt->block_size;
} }
@ -916,7 +909,7 @@ fail:
/* for maximum annoyance later UE4 versions (~v4.2x?) interleave single frames instead of /* for maximum annoyance later UE4 versions (~v4.2x?) interleave single frames instead of
* half interleave, but don't have flags to detect so we need some heuristics */ * half interleave, but don't have flags to detect so we need some heuristics */
static size_t get_ue4_msadpcm_interleave(STREAMFILE* sf, riff_fmt_chunk* fmt, off_t start, size_t size) { static size_t get_ue4_msadpcm_interleave(STREAMFILE* sf, riff_fmt_chunk* fmt, off_t start, size_t size) {
size_t v1_interleave = size / fmt->channel_count; size_t v1_interleave = size / fmt->channels;
size_t v2_interleave = fmt->block_size; size_t v2_interleave = fmt->block_size;
uint8_t nibbles1[0x08] = {0}; uint8_t nibbles1[0x08] = {0};
uint8_t nibbles2[0x08] = {0}; uint8_t nibbles2[0x08] = {0};
@ -927,7 +920,7 @@ static size_t get_ue4_msadpcm_interleave(STREAMFILE* sf, riff_fmt_chunk* fmt, of
return v1_interleave; return v1_interleave;
/* 6ch only observed in later versions [Fortnite (PC)], not padded */ /* 6ch only observed in later versions [Fortnite (PC)], not padded */
if (fmt->channel_count > 2) if (fmt->channels > 2)
return v2_interleave; return v2_interleave;
read_streamfile(nibbles1, start + size - 0x08, sizeof(nibbles2), sf); read_streamfile(nibbles1, start + size - 0x08, sizeof(nibbles2), sf);
@ -1053,7 +1046,7 @@ VGMSTREAM* init_vgmstream_rifx(STREAMFILE* sf) {
/* build the VGMSTREAM */ /* build the VGMSTREAM */
vgmstream = allocate_vgmstream(fmt.channel_count,loop_flag); vgmstream = allocate_vgmstream(fmt.channels,loop_flag);
if (!vgmstream) goto fail; if (!vgmstream) goto fail;
vgmstream->sample_rate = fmt.sample_rate; vgmstream->sample_rate = fmt.sample_rate;

View File

@ -27,3 +27,19 @@ fail:
close_vgmstream(vgmstream); close_vgmstream(vgmstream);
return NULL; return NULL;
} }
/* silent stream - for containers that have dummy streams but it's a hassle to detect/filter out */
VGMSTREAM* init_vgmstream_silence_container(int total_subsongs) {
VGMSTREAM* vgmstream = NULL;
vgmstream = init_vgmstream_silence(0, 0, 0);
if (!vgmstream) goto fail;
vgmstream->num_streams = total_subsongs;
snprintf(vgmstream->stream_name, STREAM_NAME_SIZE, "%s", "dummy");
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;
}

View File

@ -35,6 +35,7 @@ typedef struct {
int audio_fix_psx_samples; int audio_fix_psx_samples;
int audio_external_and; int audio_external_and;
int audio_loop_and; int audio_loop_and;
int audio_ignore_resource_size;
off_t sequence_sequence_loop; off_t sequence_sequence_loop;
off_t sequence_sequence_single; off_t sequence_sequence_single;
@ -1116,8 +1117,11 @@ static int parse_offsets(ubi_bao_header* bao, STREAMFILE* sf) {
read_string(bao->resource_name,255, resources_offset + 0x04+0x04 + name_offset, sf); read_string(bao->resource_name,255, resources_offset + 0x04+0x04 + name_offset, sf);
if (bao->stream_size != resource_size - bao->stream_skip + bao->prefetch_size) { if (bao->stream_size != resource_size - bao->stream_skip + bao->prefetch_size) {
VGM_ASSERT(bao->stream_size != resource_size - bao->stream_skip + bao->prefetch_size, VGM_LOG("UBI BAO: stream vs resource size mismatch at %lx (%x vs %x, %x, %x)\n", offset+0x10*i, bao->stream_size, resource_size, bao->stream_skip, bao->prefetch_size);
"UBI BAO: stream vs resource size mismatch at %lx\n", offset+0x10*i);
/* rarely resource has more data than stream (sometimes a few bytes, others +0x100000)
* sometimes short song versions, but not accessed? no samples/sizes/cues/etc in header seem to refer to that [Just Dance (Wii)] */
if (!bao->cfg.audio_ignore_resource_size || bao->prefetch_size)
goto fail; goto fail;
} }
break; break;
@ -1755,6 +1759,9 @@ static int config_bao_version(ubi_bao_header* bao, STREAMFILE* sf) {
bao->cfg.codec_map[0x09] = RAW_DSP; bao->cfg.codec_map[0x09] = RAW_DSP;
bao->cfg.file_type = UBI_FORGE_b; bao->cfg.file_type = UBI_FORGE_b;
if (version == 0x0022000D) /* Just Dance (Wii) oddity */
bao->cfg.audio_ignore_resource_size = 1;
return 1; return 1;
case 0x00220015: /* James Cameron's Avatar: The Game (PSP)-package */ case 0x00220015: /* James Cameron's Avatar: The Game (PSP)-package */

View File

@ -5,38 +5,40 @@
/* LyN RIFF - from Ubisoft LyN engine games [Red Steel 2 (Wii), Adventures of Tintin (Multi), From Dust (Multi), Just Dance 3/4 (multi)] */ /* LyN RIFF - from Ubisoft LyN engine games [Red Steel 2 (Wii), Adventures of Tintin (Multi), From Dust (Multi), Just Dance 3/4 (multi)] */
VGMSTREAM * init_vgmstream_ubi_lyn(STREAMFILE *streamFile) { VGMSTREAM* init_vgmstream_ubi_lyn(STREAMFILE* sf) {
VGMSTREAM * vgmstream = NULL; VGMSTREAM * vgmstream = NULL;
off_t start_offset, first_offset = 0xc; off_t start_offset, first_offset = 0xc;
off_t fmt_offset, data_offset, fact_offset; off_t fmt_offset, data_offset, fact_offset;
size_t fmt_size, data_size, fact_size; size_t fmt_size, data_size, fact_size;
int loop_flag, channel_count, sample_rate, codec; int loop_flag, channels, sample_rate, codec;
int num_samples; int num_samples;
/* checks */ /* checks */
/* .sns: Red Steel 2, .wav: Tintin, .son: From Dust */ /* .sns: Red Steel 2
if (!check_extensions(streamFile,"sns,wav,lwav,son")) * .wav: Tintin, Just Dance
* .son: From Dust */
if (!check_extensions(sf,"sns,wav,lwav,son"))
goto fail; goto fail;
/* a slightly eccentric RIFF with custom codecs */ /* a slightly eccentric RIFF with custom codecs */
if (read_32bitBE(0x00,streamFile) != 0x52494646 || /* "RIFF" */ if (!is_id32be(0x00,sf, "RIFF") ||
read_32bitBE(0x08,streamFile) != 0x57415645) /* "WAVE" */ !is_id32be(0x08,sf, "WAVE"))
goto fail; goto fail;
if (read_32bitLE(0x04,streamFile)+0x04+0x04 != get_streamfile_size(streamFile)) if (read_32bitLE(0x04,sf) + 0x04 + 0x04 != get_streamfile_size(sf))
goto fail; goto fail;
if (!find_chunk(streamFile, 0x666d7420,first_offset,0, &fmt_offset,&fmt_size, 0, 0)) /* "fmt " */ if (!find_chunk(sf, 0x666d7420,first_offset,0, &fmt_offset,&fmt_size, 0, 0)) /* "fmt " */
goto fail; goto fail;
if (!find_chunk(streamFile, 0x64617461,first_offset,0, &data_offset,&data_size, 0, 0)) /* "data" */ if (!find_chunk(sf, 0x64617461,first_offset,0, &data_offset,&data_size, 0, 0)) /* "data" */
goto fail; goto fail;
/* always found, even with PCM (LyN subchunk seems to contain the engine version, ex. 0x0d/10) */ /* always found, even with PCM (LyN subchunk seems to contain the engine version, ex. 0x0d/10) */
if (!find_chunk(streamFile, 0x66616374,first_offset,0, &fact_offset,&fact_size, 0, 0)) /* "fact" */ if (!find_chunk(sf, 0x66616374,first_offset,0, &fact_offset,&fact_size, 0, 0)) /* "fact" */
goto fail; goto fail;
if (fact_size != 0x10 || read_32bitBE(fact_offset+0x04, streamFile) != 0x4C794E20) /* "LyN " */ if (fact_size != 0x10 || read_32bitBE(fact_offset+0x04, sf) != 0x4C794E20) /* "LyN " */
goto fail; goto fail;
num_samples = read_32bitLE(fact_offset+0x00, streamFile); num_samples = read_32bitLE(fact_offset+0x00, sf);
/* sometimes there is a LySE chunk */ /* sometimes there is a LySE chunk */
@ -44,9 +46,9 @@ VGMSTREAM * init_vgmstream_ubi_lyn(STREAMFILE *streamFile) {
{ {
if (fmt_size < 0x12) if (fmt_size < 0x12)
goto fail; goto fail;
codec = (uint16_t)read_16bitLE(fmt_offset+0x00,streamFile); codec = read_u16le(fmt_offset+0x00,sf);
channel_count = read_16bitLE(fmt_offset+0x02,streamFile); channels = read_u16le(fmt_offset+0x02,sf);
sample_rate = read_32bitLE(fmt_offset+0x04,streamFile); sample_rate = read_s32le(fmt_offset+0x04,sf);
/* 0x08: average bytes, 0x0c: block align, 0x0e: bps, etc */ /* 0x08: average bytes, 0x0c: block align, 0x0e: bps, etc */
/* fake WAVEFORMATEX, used with > 2ch */ /* fake WAVEFORMATEX, used with > 2ch */
@ -54,10 +56,10 @@ VGMSTREAM * init_vgmstream_ubi_lyn(STREAMFILE *streamFile) {
if (fmt_size < 0x28) if (fmt_size < 0x28)
goto fail; goto fail;
/* fake GUID with first value doubling as codec */ /* fake GUID with first value doubling as codec */
codec = read_32bitLE(fmt_offset+0x18,streamFile); codec = read_32bitLE(fmt_offset+0x18,sf);
if (read_32bitBE(fmt_offset+0x1c,streamFile) != 0x00001000 && if (read_32bitBE(fmt_offset+0x1c,sf) != 0x00001000 &&
read_32bitBE(fmt_offset+0x20,streamFile) != 0x800000AA && read_32bitBE(fmt_offset+0x20,sf) != 0x800000AA &&
read_32bitBE(fmt_offset+0x24,streamFile) != 0x00389B71) { read_32bitBE(fmt_offset+0x24,sf) != 0x00389B71) {
goto fail; goto fail;
} }
} }
@ -70,7 +72,7 @@ VGMSTREAM * init_vgmstream_ubi_lyn(STREAMFILE *streamFile) {
/* build the VGMSTREAM */ /* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag); vgmstream = allocate_vgmstream(channels, loop_flag);
if (!vgmstream) goto fail; if (!vgmstream) goto fail;
vgmstream->sample_rate = sample_rate; vgmstream->sample_rate = sample_rate;
@ -99,7 +101,7 @@ VGMSTREAM * init_vgmstream_ubi_lyn(STREAMFILE *streamFile) {
}; };
int i, ch; int i, ch;
for (ch = 0; ch < channel_count; ch++) { for (ch = 0; ch < channels; ch++) {
for (i = 0; i < 16; i++) { for (i = 0; i < 16; i++) {
vgmstream->ch[ch].adpcm_coef[i] = coef[i]; vgmstream->ch[ch].adpcm_coef[i] = coef[i];
} }
@ -114,27 +116,27 @@ VGMSTREAM * init_vgmstream_ubi_lyn(STREAMFILE *streamFile) {
layered_layout_data* data = NULL; layered_layout_data* data = NULL;
int i; int i;
if (read_32bitLE(start_offset+0x00,streamFile) != 1) /* id? */ if (read_32bitLE(start_offset+0x00,sf) != 1) /* id? */
goto fail; goto fail;
interleave_size = read_32bitLE(start_offset+0x04,streamFile); interleave_size = read_32bitLE(start_offset+0x04,sf);
/* interleave is adjusted so there is no smaller last block, it seems */ /* interleave is adjusted so there is no smaller last block, it seems */
vgmstream->coding_type = coding_OGG_VORBIS; vgmstream->coding_type = coding_OGG_VORBIS;
vgmstream->layout_type = layout_layered; vgmstream->layout_type = layout_layered;
/* init layout */ /* init layout */
data = init_layout_layered(channel_count); data = init_layout_layered(channels);
if (!data) goto fail; if (!data) goto fail;
vgmstream->layout_data = data; vgmstream->layout_data = data;
/* open each layer subfile */ /* open each layer subfile */
for (i = 0; i < channel_count; i++) { for (i = 0; i < channels; i++) {
STREAMFILE* temp_sf = NULL; STREAMFILE* temp_sf = NULL;
size_t logical_size = read_32bitLE(start_offset+0x08 + 0x04*i,streamFile); size_t logical_size = read_32bitLE(start_offset+0x08 + 0x04*i,sf);
off_t layer_offset = start_offset + 0x08 + 0x04*channel_count; //+ interleave_size*i; off_t layer_offset = start_offset + 0x08 + 0x04*channels; //+ interleave_size*i;
temp_sf = setup_ubi_lyn_streamfile(streamFile, layer_offset, interleave_size, i, channel_count, logical_size); temp_sf = setup_ubi_lyn_streamfile(sf, layer_offset, interleave_size, i, channels, logical_size);
if (!temp_sf) goto fail; if (!temp_sf) goto fail;
data->layers[i] = init_vgmstream_ogg_vorbis(temp_sf); data->layers[i] = init_vgmstream_ogg_vorbis(temp_sf);
@ -155,17 +157,23 @@ VGMSTREAM * init_vgmstream_ubi_lyn(STREAMFILE *streamFile) {
#ifdef VGM_USE_MPEG #ifdef VGM_USE_MPEG
case 0x5051: { /* MPEG (PS3/PC), interleaved 1ch */ case 0x5051: { /* MPEG (PS3/PC), interleaved 1ch */
mpeg_custom_config cfg = {0}; mpeg_custom_config cfg = {0};
int i; int i, frame_samples;
cfg.interleave = read_32bitLE(start_offset+0x04,streamFile); /* 0x00: config? (related to chunk size? 2=Tintin, some JD4?, 3=Michael Jackson, JD4) */
cfg.chunk_size = read_32bitLE(start_offset+0x08,streamFile); /* frame size (not counting MPEG padding?) */ cfg.interleave = read_u32le(start_offset+0x04,sf);
/* 0x00: id? (2=Tintin, 3=Michael Jackson), 0x0c: frame per interleave, 0x10: samples per frame */ /* 0x08: CBR frame size (not counting MPEG padding) */
/* 0x0c: bps (32) */
frame_samples = read_s32le(start_offset+0x10,sf);
/* skip seek tables and find actual start */ /* skip seek tables and find actual start */
/* table: entries per channel (1 per chunk, but not chunk-aligned?) */
/* - 0x00: offset but same for both channels? */
/* - 0x04: flag? (-1~-N) */
/* - 0x06: flag? (-1~-N) */
start_offset += 0x14; start_offset += 0x14;
data_size -= 0x14; data_size -= 0x14;
for (i = 0; i < channel_count; i++) { for (i = 0; i < channels; i++) {
int entries = read_32bitLE(start_offset,streamFile); int entries = read_s32le(start_offset,sf);
start_offset += 0x04 + entries*0x08; start_offset += 0x04 + entries*0x08;
data_size -= 0x04 + entries*0x08; data_size -= 0x04 + entries*0x08;
@ -173,8 +181,25 @@ VGMSTREAM * init_vgmstream_ubi_lyn(STREAMFILE *streamFile) {
cfg.data_size = data_size; cfg.data_size = data_size;
//todo data parsing looks correct but some files decode a bit wrong at the end (ex. Tintin: Music~Boss~Allan~Victory~02) /* last interleave is half remaining data */
vgmstream->codec_data = init_mpeg_custom(streamFile, start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_LYN, &cfg); cfg.max_chunks = (cfg.data_size / (cfg.interleave * channels));
/* somehow certain interleaves don't work:
* - 0xB400: 0x2D00 ko, 0xE100 ok (max-1)
* - 0x8000: 0x3ec7 ko, 0xbec7 ok (max-1)
* - 0x8000: 0x5306 ok, 0xd306 ko (max-1) !!!
* (doesn't seem related to anything like data size/frame samples/sample rate/etc) */
cfg.max_chunks--;
cfg.interleave_last = (cfg.data_size - cfg.max_chunks * cfg.interleave * channels) / channels;
if (cfg.interleave <= 0x8000 && cfg.interleave_last > 0xD000) {
cfg.max_chunks++;
cfg.interleave_last = (cfg.data_size - cfg.max_chunks * cfg.interleave * channels) / channels;
}
/* for some reason num_samples may be +1 or +2 of max possible samples (no apparent meaning) */
vgmstream->num_samples = vgmstream->num_samples / frame_samples * frame_samples;
vgmstream->loop_end_sample = vgmstream->num_samples;
vgmstream->codec_data = init_mpeg_custom(sf, start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_LYN, &cfg);
if (!vgmstream->codec_data) goto fail; if (!vgmstream->codec_data) goto fail;
vgmstream->layout_type = layout_none; vgmstream->layout_type = layout_none;
@ -192,13 +217,13 @@ VGMSTREAM * init_vgmstream_ubi_lyn(STREAMFILE *streamFile) {
/* skip standard XMA header + seek table */ /* skip standard XMA header + seek table */
/* 0x00: version? no apparent differences (0x1=Just Dance 4, 0x3=others) */ /* 0x00: version? no apparent differences (0x1=Just Dance 4, 0x3=others) */
chunk_offset = start_offset + 0x04 + 0x04; chunk_offset = start_offset + 0x04 + 0x04;
chunk_size = read_32bitLE(start_offset + 0x04, streamFile); chunk_size = read_32bitLE(start_offset + 0x04, sf);
seek_size = read_32bitLE(chunk_offset+chunk_size, streamFile); seek_size = read_32bitLE(chunk_offset+chunk_size, sf);
start_offset += (0x04 + 0x04 + chunk_size + 0x04 + seek_size); start_offset += (0x04 + 0x04 + chunk_size + 0x04 + seek_size);
data_size -= (0x04 + 0x04 + chunk_size + 0x04 + seek_size); data_size -= (0x04 + 0x04 + chunk_size + 0x04 + seek_size);
bytes = ffmpeg_make_riff_xma_from_fmt_chunk(buf,0x100, chunk_offset,chunk_size, data_size, streamFile, 1); bytes = ffmpeg_make_riff_xma_from_fmt_chunk(buf,0x100, chunk_offset,chunk_size, data_size, sf, 1);
vgmstream->codec_data = init_ffmpeg_header_offset(streamFile, buf,bytes, start_offset,data_size); vgmstream->codec_data = init_ffmpeg_header_offset(sf, buf,bytes, start_offset,data_size);
if ( !vgmstream->codec_data ) goto fail; if ( !vgmstream->codec_data ) goto fail;
vgmstream->coding_type = coding_FFmpeg; vgmstream->coding_type = coding_FFmpeg;
vgmstream->layout_type = layout_none; vgmstream->layout_type = layout_none;
@ -212,7 +237,7 @@ VGMSTREAM * init_vgmstream_ubi_lyn(STREAMFILE *streamFile) {
} }
if ( !vgmstream_open_stream(vgmstream,streamFile,start_offset) ) if ( !vgmstream_open_stream(vgmstream,sf,start_offset) )
goto fail; goto fail;
return vgmstream; return vgmstream;
@ -223,7 +248,7 @@ fail:
/* LyN RIFF in containers */ /* LyN RIFF in containers */
VGMSTREAM * init_vgmstream_ubi_lyn_container(STREAMFILE *streamFile) { VGMSTREAM * init_vgmstream_ubi_lyn_container(STREAMFILE *sf) {
VGMSTREAM *vgmstream = NULL; VGMSTREAM *vgmstream = NULL;
STREAMFILE *temp_sf = NULL; STREAMFILE *temp_sf = NULL;
off_t subfile_offset; off_t subfile_offset;
@ -233,29 +258,29 @@ VGMSTREAM * init_vgmstream_ubi_lyn_container(STREAMFILE *streamFile) {
* data before the RIFF. Might as well support them in case the RIFF wasn't extracted. */ * data before the RIFF. Might as well support them in case the RIFF wasn't extracted. */
/* checks */ /* checks */
if (!check_extensions(streamFile,"sns,wav,lwav,son")) if (!check_extensions(sf,"sns,wav,lwav,son"))
goto fail; goto fail;
/* find "RIFF" position */ /* find "RIFF" position */
if (read_32bitBE(0x00,streamFile) == 0x4C795345 && /* "LySE" */ if (read_32bitBE(0x00,sf) == 0x4C795345 && /* "LySE" */
read_32bitBE(0x14,streamFile) == 0x52494646) { /* "RIFF" */ read_32bitBE(0x14,sf) == 0x52494646) { /* "RIFF" */
subfile_offset = 0x14; /* Adventures of Tintin */ subfile_offset = 0x14; /* Adventures of Tintin */
} }
else if (read_32bitBE(0x20,streamFile) == 0x4C795345 && /* "LySE" */ else if (read_32bitBE(0x20,sf) == 0x4C795345 && /* "LySE" */
read_32bitBE(0x34,streamFile) == 0x52494646) { /* "RIFF" */ read_32bitBE(0x34,sf) == 0x52494646) { /* "RIFF" */
subfile_offset = 0x34; /* Michael Jackson The Experience (Wii) */ subfile_offset = 0x34; /* Michael Jackson The Experience (Wii) */
} }
else if (read_32bitLE(0x00,streamFile)+0x20 == get_streamfile_size(streamFile) && else if (read_32bitLE(0x00,sf)+0x20 == get_streamfile_size(sf) &&
read_32bitBE(0x20,streamFile) == 0x52494646) { /* "RIFF" */ read_32bitBE(0x20,sf) == 0x52494646) { /* "RIFF" */
subfile_offset = 0x20; /* Red Steel 2, From Dust */ subfile_offset = 0x20; /* Red Steel 2, From Dust */
} }
else { else {
goto fail; goto fail;
} }
subfile_size = read_32bitLE(subfile_offset+0x04,streamFile) + 0x04+0x04; subfile_size = read_32bitLE(subfile_offset+0x04,sf) + 0x04+0x04;
temp_sf = setup_subfile_streamfile(streamFile, subfile_offset, subfile_size, NULL); temp_sf = setup_subfile_streamfile(sf, subfile_offset, subfile_size, NULL);
if (!temp_sf) goto fail; if (!temp_sf) goto fail;
vgmstream = init_vgmstream_ubi_lyn(temp_sf); vgmstream = init_vgmstream_ubi_lyn(temp_sf);

170
src/meta/xsh_xsd_xss.c Normal file
View File

@ -0,0 +1,170 @@
#include "meta.h"
#include "../coding/coding.h"
/* XSH+XSD/XSS - from Treyarch games [Spider-Man 2002 (Xbox), Kelly Slater's Pro Surfer (Xbox)] */
VGMSTREAM* init_vgmstream_xsh_xsd_xss(STREAMFILE* sf) {
VGMSTREAM* vgmstream = NULL;
STREAMFILE* sf_body = NULL;
uint32_t offset;
uint32_t stream_type, stream_offset, stream_size;
uint32_t name_offset, name_size;
uint32_t flags;
int32_t num_samples;
int version = 0;
int loop_flag, channels, codec, sample_rate;
int total_subsongs, target_subsong = sf->stream_index;
/* checks */
if (!check_extensions(sf, "xsh"))
goto fail;
version = read_u32le(0x00, sf);
if (read_u32le(0x04, sf) != 0)
goto fail;
total_subsongs = read_u32le(0x08, sf);
if (target_subsong == 0) target_subsong = 1;
if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail;
switch(version) {
case 0x009D:
offset = 0x0c + (target_subsong-1) * 0x60;
name_offset = offset + 0x00;
name_size = 0x20;
stream_type = read_u32le(offset + 0x20,sf);
stream_offset = read_u32le(offset + 0x24,sf);
stream_size = read_u32le(offset + 0x28,sf);
flags = read_u32le(offset + 0x34,sf);
/* 0x38: flags? */
num_samples = 0;
offset = offset + 0x3c;
break;
case 0x0100:
offset = 0x0c + (target_subsong-1) * 0x64;
name_offset = offset + 0x00;
name_size = 0x20;
stream_type = read_u32le(offset + 0x20,sf);
stream_offset = read_u32le(offset + 0x24,sf);
stream_size = read_u32le(offset + 0x28,sf);
flags = read_u32le(offset + 0x34,sf);
num_samples = read_u32le(offset + 0x38,sf);
/* 0x3c: flags? */
offset = offset + 0x40;
break;
default:
goto fail;
}
loop_flag = 0;
if (stream_type < 0 || stream_type > 2)
goto fail;
/* 0x00: floats x4 (volume/pan/etc? usually 1.0, 1.0, 10.0, 10.0) */
codec = read_u16le(offset + 0x10,sf);
channels = read_u16le(offset + 0x12,sf);
sample_rate = read_u32le(offset + 0x14,sf);
/* 0x18: avg bitrate */
/* 0x1c: block size */
/* 0x1e: bps */
/* 0x20: 2? */
if (stream_type == 0) {
vgmstream = init_vgmstream_silence_container(total_subsongs);
if (!vgmstream) goto fail;
close_streamfile(sf_body);
return vgmstream;
}
if (flags & 0x04) {
char filename[255];
switch (version) {
case 0x009D:
/* stream is a named .xss, with stream_offset/size = 0 (Spider-Man) */
read_string(filename, name_size, name_offset,sf);
strcat(filename, ".xss");
sf_body = open_streamfile_by_filename(sf,filename);
if (!sf_body) {
VGM_LOG("XSH: can't find %s\n", filename);
goto fail;
}
/* xss is playable externally, so this is mostly for show */
vgmstream = init_vgmstream_riff(sf_body);
if (!vgmstream) goto fail;
vgmstream->num_streams = total_subsongs;
read_string(vgmstream->stream_name, name_size, name_offset,sf);
close_streamfile(sf_body);
return vgmstream;
//break;
case 0x0100:
/* bigfile with all streams (Kelly Slater) */
snprintf(filename, sizeof(filename), "%s", "STREAMS.XSS");
sf_body = open_streamfile_by_filename(sf,filename);
if (!sf_body) {
VGM_LOG("XSH: can't find %s\n", filename);
goto fail;
}
break;
default:
goto fail;
}
}
else {
sf_body = open_streamfile_by_ext(sf,"xsd");
if (!sf_body) {
VGM_LOG("XSH: can't find XSD");
goto fail;
}
}
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channels, loop_flag);
if (!vgmstream) goto fail;
vgmstream->meta_type = meta_XSH_XSD_XSS;
vgmstream->sample_rate = sample_rate;
vgmstream->num_streams = total_subsongs;
vgmstream->stream_size = stream_size;
switch(codec) {
case 0x0069:
vgmstream->coding_type = coding_XBOX_IMA;
vgmstream->layout_type = layout_none;
if (!num_samples)
num_samples = xbox_ima_bytes_to_samples(stream_size, channels);
vgmstream->num_samples = num_samples;
break;
}
read_string(vgmstream->stream_name, name_size, name_offset,sf);
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;
}

View File

@ -528,6 +528,7 @@ VGMSTREAM* (*init_vgmstream_functions[])(STREAMFILE* sf) = {
init_vgmstream_piff_tpcm, init_vgmstream_piff_tpcm,
init_vgmstream_wxd_wxh, init_vgmstream_wxd_wxh,
init_vgmstream_bnk_relic, init_vgmstream_bnk_relic,
init_vgmstream_xsh_xsd_xss,
/* lowest priority metas (should go after all metas, and TXTH should go before raw formats) */ /* 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 */ init_vgmstream_txth, /* proper parsers should supersede TXTH, once added */

View File

@ -743,6 +743,8 @@ typedef enum {
meta_PIFF_TPCM, meta_PIFF_TPCM,
meta_WXD_WXH, meta_WXD_WXH,
meta_BNK_RELIC, meta_BNK_RELIC,
meta_XSH_XSD_XSS,
} meta_t; } meta_t;
/* standard WAVEFORMATEXTENSIBLE speaker positions */ /* standard WAVEFORMATEXTENSIBLE speaker positions */