Merge pull request #232 from bnnm/xwb-lyn-etc

XWB, LYN, etc
This commit is contained in:
Christopher Snowhill 2018-05-27 14:52:16 -07:00 committed by GitHub
commit 311eeebc1b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 83 additions and 26 deletions

View File

@ -147,7 +147,8 @@ mpeg_codec_data *init_mpeg_custom(STREAMFILE *streamFile, off_t start_offset, co
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;
if (data->default_buffer_size > 0x8000) goto fail; //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 */
/* init streams */ /* init streams */

View File

@ -259,6 +259,7 @@ static const char* extension_list[] = {
"rak", "rak",
"ras", "ras",
"raw", "raw",
"rda", //FFmpeg/reserved [Rhythm Destruction (PC)]
"rkv", "rkv",
"rnd", "rnd",
"rof", "rof",
@ -294,7 +295,7 @@ static const char* extension_list[] = {
"scd", "scd",
"sck", "sck",
"sd9", "sd9",
"sdf", "sdf", //txth/reserved [Gummy Bears Mini Golf (3DS), Agent Hugo - Lemoon Twist (PS2)]
"sdt", "sdt",
"seg", "seg",
"sf0", "sf0",

View File

@ -257,8 +257,9 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) {
/* .lwav: to avoid hijacking .wav, .xwav: fake for Xbox games (unneded anymore) */ /* .lwav: to avoid hijacking .wav, .xwav: fake for Xbox games (unneded anymore) */
/* .da: The Great Battle VI (PS), .cd: Exector (PS), .med: Psi Ops (PC), .snd: Layton Brothers (iOS/Android), /* .da: The Great Battle VI (PS), .cd: Exector (PS), .med: Psi Ops (PC), .snd: Layton Brothers (iOS/Android),
* .adx: Remember11 (PC) sfx * .adx: Remember11 (PC) sfx
* .adp: Headhunter (DC) */ * .adp: Headhunter (DC)
if ( check_extensions(streamFile, "wav,lwav,xwav,da,cd,med,snd,adx,adp") ) { * .xss: Spider-Man The Movie (Xbox) */
if ( check_extensions(streamFile, "wav,lwav,xwav,da,cd,med,snd,adx,adp,xss") ) {
; ;
} }
else if ( check_extensions(streamFile, "mwv") ) { else if ( check_extensions(streamFile, "mwv") ) {

View File

@ -687,13 +687,15 @@ static int get_bytes_to_samples(txth_header * txth, uint32_t bytes) {
if (!txth->interleave) return 0; if (!txth->interleave) return 0;
return bytes / txth->interleave * 256 * txth->channels; return bytes / txth->interleave * 256 * txth->channels;
/* untested */
case IMA: case IMA:
case DVI_IMA: case DVI_IMA:
return ima_bytes_to_samples(bytes, txth->channels);
case AICA:
return aica_bytes_to_samples(bytes, txth->channels);
/* untested */
case SDX2: case SDX2:
return bytes; return bytes;
case AICA:
return bytes * 2 / txth->channels;
case NGC_DTK: case NGC_DTK:
return bytes / 32 * 28; /* always stereo? */ return bytes / 32 * 28; /* always stereo? */
case APPLE_IMA4: case APPLE_IMA4:

View File

@ -159,12 +159,9 @@ VGMSTREAM * init_vgmstream_ubi_lyn(STREAMFILE *streamFile) {
mpeg_custom_config cfg = {0}; mpeg_custom_config cfg = {0};
int i; int i;
if (read_32bitLE(start_offset+0x00,streamFile) != 2) /* id? */
goto fail;
cfg.interleave = read_32bitLE(start_offset+0x04,streamFile); cfg.interleave = read_32bitLE(start_offset+0x04,streamFile);
cfg.chunk_size = read_32bitLE(start_offset+0x08,streamFile); cfg.chunk_size = read_32bitLE(start_offset+0x08,streamFile); /* frame size (not counting MPEG padding?) */
/* 0x08: frame size, 0x0c: frame per interleave, 0x10: samples per frame */ /* 0x00: id? (2=Tintin, 3=Michael Jackson), 0x0c: frame per interleave, 0x10: samples per frame */
/* skip seek tables and find actual start */ /* skip seek tables and find actual start */
start_offset += 0x14; start_offset += 0x14;

View File

@ -26,7 +26,7 @@ static const int wma_block_align_index[17] = {
}; };
typedef enum { PCM, XBOX_ADPCM, MS_ADPCM, XMA1, XMA2, WMA, XWMA, ATRAC3, OGG, DSP } xact_codec; typedef enum { PCM, XBOX_ADPCM, MS_ADPCM, XMA1, XMA2, WMA, XWMA, ATRAC3, OGG, DSP, ATRAC9_RIFF } xact_codec;
typedef struct { typedef struct {
int little_endian; int little_endian;
int version; int version;
@ -70,6 +70,7 @@ typedef struct {
} xwb_header; } xwb_header;
static void get_name(char * buf, size_t maxsize, int target_subsong, xwb_header * xwb, STREAMFILE *streamFile); static void get_name(char * buf, size_t maxsize, int target_subsong, xwb_header * xwb, STREAMFILE *streamFile);
static STREAMFILE* setup_subfile_streamfile(STREAMFILE *streamFile, off_t subfile_offset, size_t subfile_size, const char* fake_ext);
/* XWB - XACT Wave Bank (Microsoft SDK format for XBOX/XBOX360/Windows) */ /* XWB - XACT Wave Bank (Microsoft SDK format for XBOX/XBOX360/Windows) */
@ -293,37 +294,44 @@ VGMSTREAM * init_vgmstream_xwb(STREAMFILE *streamFile) {
} }
} }
/* Techland's bizarre format hijack (Nail'd, Sniper: Ghost Warrior PS3).
* Somehow they used XWB + ATRAC3 in their PS3 games, very creative */ /* format hijacks from creative devs, using non-official codecs */
if (xwb.version == XACT_TECHLAND && xwb.codec == XMA2 /* XACT_TECHLAND used in their X360 games too */ if (xwb.version == XACT_TECHLAND && xwb.codec == XMA2 /* XACT_TECHLAND used in their X360 games too */
&& (xwb.block_align == 0x60 || xwb.block_align == 0x98 || xwb.block_align == 0xc0) ) { && (xwb.block_align == 0x60 || xwb.block_align == 0x98 || xwb.block_align == 0xc0) ) { /* standard ATRAC3 blocks sizes */
xwb.codec = ATRAC3; /* standard ATRAC3 blocks sizes; no other way to identify (other than reading data) */ /* Techland ATRAC3 [Nail'd (PS3), Sniper: Ghost Warrior (PS3)] */
xwb.codec = ATRAC3;
/* num samples uses a modified entry_info format (maybe skip samples + samples? sfx use the standard format) /* num samples uses a modified entry_info format (maybe skip samples + samples? sfx use the standard format)
* ignore for now and just calc max samples */ * ignore for now and just calc max samples */
xwb.num_samples = atrac3_bytes_to_samples(xwb.stream_size, xwb.block_align * xwb.channels); xwb.num_samples = atrac3_bytes_to_samples(xwb.stream_size, xwb.block_align * xwb.channels);
} }
else if (xwb.codec == OGG) {
/* Oddworld: Stranger's Wrath iOS/Android format hijack, with changed meanings */ /* Oddworld: Stranger's Wrath (iOS/Android) */
if (xwb.codec == OGG) {
xwb.num_samples = xwb.stream_size / (2 * xwb.channels); /* uncompressed bytes */ xwb.num_samples = xwb.stream_size / (2 * xwb.channels); /* uncompressed bytes */
xwb.stream_size = xwb.loop_end; xwb.stream_size = xwb.loop_end;
xwb.loop_start = 0; xwb.loop_start = 0;
xwb.loop_end = 0; xwb.loop_end = 0;
} }
else if (xwb.version == XACT3_0_MAX && xwb.codec == XMA2
/* Stardew Valley (Switch) format hijack, with full interleaved DSPs (including headers) */ && xwb.bits_per_sample == 0x01 && xwb.block_align == 0x04
if (xwb.version == XACT3_0_MAX && xwb.codec == XMA2 && xwb.data_size == 0x55951c1c) { /* some kind of id? */
&& xwb.bits_per_sample == 0x01 && xwb.block_align == 0x04 && xwb.data_size == 0x55951c1c) { /* Stardew Valley (Switch), full interleaved DSPs (including headers) */
xwb.codec = DSP; xwb.codec = DSP;
} }
else if (xwb.version == XACT3_0_MAX && xwb.codec == XMA2
&& xwb.bits_per_sample == 0x01 && xwb.block_align == 0x04
&& xwb.data_size == 0x4e0a1000) { /* some kind of id? */
/* Stardew Valley (Vita), standard RIFF with ATRAC9 */
xwb.codec = ATRAC9_RIFF;
}
/* test loop after the above fixes */ /* test loop after the above fixes */
xwb.loop_flag = (xwb.loop_end > 0 || xwb.loop_end_sample > xwb.loop_start) xwb.loop_flag = (xwb.loop_end > 0 || xwb.loop_end_sample > xwb.loop_start)
&& !(xwb.entry_flags & WAVEBANKENTRY_FLAGS_IGNORELOOP); && !(xwb.entry_flags & WAVEBANKENTRY_FLAGS_IGNORELOOP);
/* Oddworld OGG the data_size value is size of uncompressed bytes instead; DSP uses some id/config as value */ /* Oddworld OGG the data_size value is size of uncompressed bytes instead; DSP uses some id/config as value */
if (xwb.codec != OGG && xwb.codec != DSP) { if (xwb.codec != OGG && xwb.codec != DSP && xwb.codec != ATRAC9_RIFF) {
/* some low-q rips don't remove padding, relax validation a bit */ /* some low-q rips don't remove padding, relax validation a bit */
if (xwb.data_offset + xwb.data_size > get_streamfile_size(streamFile)) if (xwb.data_offset + xwb.data_size > get_streamfile_size(streamFile))
goto fail; goto fail;
@ -516,7 +524,6 @@ VGMSTREAM * init_vgmstream_xwb(STREAMFILE *streamFile) {
vgmstream->layout_type = layout_none; vgmstream->layout_type = layout_none;
break; break;
} }
#endif #endif
case DSP: { /* Stardew Valley (Switch) extension */ case DSP: { /* Stardew Valley (Switch) extension */
@ -530,6 +537,30 @@ VGMSTREAM * init_vgmstream_xwb(STREAMFILE *streamFile) {
break; break;
} }
#ifdef VGM_USE_ATRAC9
case ATRAC9_RIFF: { /* Stardew Valley (Vita) extension */
VGMSTREAM *temp_vgmstream = NULL;
STREAMFILE *temp_streamFile = NULL;
/* standard RIFF, use subfile (seems doesn't use xwb loops) */
VGM_ASSERT(xwb.loop_flag, "XWB: RIFF ATRAC9 loop flag found\n");
temp_streamFile = setup_subfile_streamfile(streamFile, xwb.stream_offset,xwb.stream_size, "at9");
if (!temp_streamFile) goto fail;
temp_vgmstream = init_vgmstream_riff(temp_streamFile);
close_streamfile(temp_streamFile);
if (!temp_vgmstream) goto fail;
temp_vgmstream->num_streams = vgmstream->num_streams;
temp_vgmstream->stream_size = vgmstream->stream_size;
temp_vgmstream->meta_type = vgmstream->meta_type;
close_vgmstream(vgmstream);
return temp_vgmstream;
}
#endif
default: default:
goto fail; goto fail;
} }
@ -546,6 +577,30 @@ fail:
return NULL; return NULL;
} }
/* ****************************************************************************** */
static STREAMFILE* setup_subfile_streamfile(STREAMFILE *streamFile, off_t subfile_offset, size_t subfile_size, const char* fake_ext) {
STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL;
/* setup subfile */
new_streamFile = open_wrap_streamfile(streamFile);
if (!new_streamFile) goto fail;
temp_streamFile = new_streamFile;
new_streamFile = open_clamp_streamfile(temp_streamFile, subfile_offset,subfile_size);
if (!new_streamFile) goto fail;
temp_streamFile = new_streamFile;
new_streamFile = open_fakename_streamfile(temp_streamFile, NULL,fake_ext);
if (!new_streamFile) goto fail;
temp_streamFile = new_streamFile;
return temp_streamFile;
fail:
close_streamfile(temp_streamFile);
return NULL;
}
/* ****************************************************************************** */ /* ****************************************************************************** */