Merge pull request #228 from bnnm/gwd-fixes

GWM, fixes
This commit is contained in:
Christopher Snowhill 2018-05-20 16:18:36 -07:00 committed by GitHub
commit 2179d29639
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 104 additions and 73 deletions

View File

@ -135,6 +135,7 @@ static const char* extension_list[] = {
"gms",
"gsb",
"gtd",
"gwm",
"hca",
"hgc1",
@ -1014,6 +1015,7 @@ static const meta_info meta_info_list[] = {
{meta_UBI_BAO, "Ubisoft BAO header"},
{meta_DSP_SWITCH_AUDIO, "UE4 Switch Audio header"},
{meta_TA_AAC_VITA, "tri-Ace AAC (Vita) header"},
{meta_OGG_GWM, "Ogg Vorbis (GWM header)"},
#ifdef VGM_USE_FFMPEG
{meta_FFmpeg, "FFmpeg supported file format"},

View File

@ -231,6 +231,9 @@ static const hcakey_info hcakey_list[] = {
// Oira (Cygames) [iOS/Android]
{46460622}, // 0000000002C4EECE
// Dragon Ball Legends (Bandai Namco) [iOS/Android]
{7335633962698440504}, // 65CD683924EE7F38
};
#endif/*_HCA_KEYS_H_*/

View File

@ -629,19 +629,26 @@ VGMSTREAM * init_vgmstream_3ds_idsp(STREAMFILE *streamFile) {
/* try NUS3BANK container */
if (read_32bitBE(0x00,streamFile) == 0x4E555333) { /* "NUS3" */
offset = 0x14 + read_32bitLE(0x10, streamFile); /* header size */
offset += read_32bitLE(0x1C, streamFile) + 0x08;
offset += read_32bitLE(0x24, streamFile) + 0x08;
offset += read_32bitLE(0x2C, streamFile) + 0x08;
offset += read_32bitLE(0x34, streamFile) + 0x08;
offset += read_32bitLE(0x3C, streamFile) + 0x08;
offset += read_32bitLE(0x44, streamFile) + 0x08;
int i, chunk_count;
offset = 0x14 + read_32bitLE(0x10, streamFile); /* TOC size */
chunk_count = read_32bitLE(0x14, streamFile); /* rarely not 7 (ex. SMB U's snd_bgm_CRS12_Simple_Result_Final) */
for (i = 0; i < chunk_count; i++) {
if (read_32bitBE(0x18 + i*0x08 + 0x00, streamFile) == 0x5041434B) { /* "PACK" */
offset += 0x08;
break; /* contains "IDSP", should appear last anyway */
}
else {
offset += 0x08 + read_32bitLE(0x18 + i*0x08 + 0x04, streamFile);
}
}
}
else {
offset = 0x00;
}
if (read_32bitBE(offset,streamFile) != 0x49445350) /* "IDSP" */
goto fail;
/* 0x0c: sample rate, 0x10: num_samples, 0x14: loop_start_sample, 0x18: loop_start_sample */

View File

@ -215,6 +215,17 @@ static void ys8_ogg_decryption_callback(void *ptr, size_t size, size_t nmemb, vo
}
}
static void gwm_ogg_decryption_callback(void *ptr, size_t size, size_t nmemb, void *datasource) {
size_t bytes_read = size*nmemb;
ogg_vorbis_streamfile * const ov_streamfile = datasource;
int i;
/* bytes are xor'd with key */
for (i = 0; i < bytes_read; i++) {
((uint8_t*)ptr)[i] ^= (uint8_t)ov_streamfile->xor_value;
}
}
/* Ogg Vorbis, by way of libvorbisfile; may contain loop comments */
VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) {
ogg_vorbis_meta_info_t ovmi = {0};
@ -223,17 +234,15 @@ VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) {
int is_ogg = 0;
int is_um3 = 0;
int is_kovs = 0;
int is_psychic = 0;
int is_sngw = 0;
int is_isd = 0;
int is_l2sd = 0;
int is_rpgmvo = 0;
int is_eno = 0;
int is_ys8 = 0;
int is_gwm = 0;
/* check extension */
/* .ogg: standard/psychic, .logg: renamed for plugins,
/* .ogg: standard/various, .logg: renamed for plugins,
* .adx: KID [Remember11 (PC)],
* .rof: The Rhythm of Fighters (Mobile)
* .acm: Planescape Torment Enhanced Edition (PC) */
@ -251,26 +260,32 @@ VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) {
is_rpgmvo = 1;
} else if (check_extensions(streamFile,"eno")) { /* .eno: Metronomicon (PC) */
is_eno = 1;
} else if (check_extensions(streamFile,"gwm")) { /* .gwm: Adagio: Cloudburst (PC) */
is_gwm = 1;
} else {
goto fail;
}
/* check standard Ogg Vorbis */
/* check standard Ogg Vorbis and variations */
if (is_ogg) {
if (read_32bitBE(0x00,streamFile) == 0x2c444430) { /* Psychic Software obfuscation [Darkwind: War on Wheels (PC)] */
is_psychic = 1;
if (read_32bitBE(0x00,streamFile) == 0x2c444430) { /* Psychic Software [Darkwind: War on Wheels (PC)] */
ovmi.decryption_callback = psychic_ogg_decryption_callback;
ovmi.meta_type = meta_OGG_PSYCHIC;
}
else if (read_32bitBE(0x00,streamFile) == 0x4C325344) { /* "L2SD" [Lineage II Chronicle 4 (PC)] */
is_l2sd = 1;
ovmi.decryption_callback = l2sd_ogg_decryption_callback;
ovmi.meta_type = meta_OGG_L2SD;
}
else if (read_32bitBE(0x00,streamFile) == 0x048686C5) { /* XOR'ed + bitswapped "OggS" [Ys VIII (PC)] */
is_ys8 = 1;
ovmi.xor_value = 0xF0;
ovmi.decryption_callback = ys8_ogg_decryption_callback;
ovmi.meta_type = meta_OGG_YS8;
}
else if (read_32bitBE(0x00,streamFile) != 0x4f676753) { /* "OggS" */
goto fail; /* unknown/not ogg (ex. Wwise) */
else if (read_32bitBE(0x00,streamFile) == 0x4f676753) { /* "OggS" */
ovmi.meta_type = meta_OGG_VORBIS;
}
else {
goto fail; /* unknown/not Ogg Vorbis (ex. Wwise) */
}
}
@ -279,9 +294,10 @@ VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) {
if (read_32bitBE(0x00,streamFile) != 0x4f676753) { /* "OggS" */
ovmi.decryption_callback = um3_ogg_decryption_callback;
}
ovmi.meta_type = meta_OGG_UM3;
}
/* check KOVS (Koei Tecmo games), encrypted and has an actual header */
/* check KOVS (Koei Tecmo games), header + encrypted */
if (is_kovs) {
if (read_32bitBE(0x00,streamFile) != 0x4b4f5653) { /* "KOVS" */
goto fail;
@ -289,6 +305,7 @@ VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) {
ovmi.loop_start = read_32bitLE(0x08,streamFile);
ovmi.loop_flag = (ovmi.loop_start != 0);
ovmi.decryption_callback = kovs_ogg_decryption_callback;
ovmi.meta_type = meta_OGG_KOVS;
start_offset = 0x20;
}
@ -299,11 +316,13 @@ VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) {
ovmi.xor_value = read_32bitBE(0x00,streamFile);
ovmi.decryption_callback = sngw_ogg_decryption_callback;
}
ovmi.meta_type = meta_OGG_SNGW;
}
/* check ISD (Gunvolt PC) */
/* check ISD [Gunvolt (PC)], encrypted */
if (is_isd) {
ovmi.decryption_callback = isd_ogg_decryption_callback;
ovmi.meta_type = meta_OGG_ISD;
//todo looping unknown, not in Ogg comments
// game has sound/GV_steam.* files with info about sound/stream/*.isd
@ -314,52 +333,36 @@ VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) {
// 0x0c(2): PCM block size, 0x0e(2): PCM bps, 0x10: null, 0x18: samples (in PCM bytes)
}
/* check RPGMKVO (RPG Maker MV), header + minor encryption */
/* check RPGMKVO [RPG Maker MV (PC)], header + partially encrypted */
if (is_rpgmvo) {
if (read_32bitBE(0x00,streamFile) != 0x5250474D && /* "RPGM" */
read_32bitBE(0x00,streamFile) != 0x56000000) { /* "V\0\0\0" */
goto fail;
}
ovmi.decryption_callback = rpgmvo_ogg_decryption_callback;
ovmi.meta_type = meta_OGG_RPGMV;
start_offset = 0x10;
}
/* check ENO [Metronomicon (PC)] */
/* check ENO [Metronomicon (PC)], key + encrypted */
if (is_eno) {
/* first byte probably derives into xor key, but this works too */
ovmi.xor_value = read_8bit(0x05,streamFile); /* always zero = easy key */
ovmi.decryption_callback = eno_ogg_decryption_callback;
ovmi.meta_type = meta_OGG_ENO;
start_offset = 0x01;
}
/* check Ys VIII (PC) */
if (is_ys8) {
ovmi.xor_value = 0xF0;
ovmi.decryption_callback = ys8_ogg_decryption_callback;
/* check GWM [Adagio: Cloudburst (PC)], encrypted */
if (is_gwm) {
ovmi.xor_value = 0x5D;
ovmi.decryption_callback = gwm_ogg_decryption_callback;
ovmi.meta_type = meta_OGG_GWM;
}
if (is_um3) {
ovmi.meta_type = meta_OGG_UM3;
} else if (is_kovs) {
ovmi.meta_type = meta_OGG_KOVS;
} else if (is_psychic) {
ovmi.meta_type = meta_OGG_PSYCHIC;
} else if (is_sngw) {
ovmi.meta_type = meta_OGG_SNGW;
} else if (is_isd) {
ovmi.meta_type = meta_OGG_ISD;
} else if (is_l2sd) {
ovmi.meta_type = meta_OGG_L2SD;
} else if (is_rpgmvo) {
ovmi.meta_type = meta_OGG_RPGMV;
} else if (is_eno) {
ovmi.meta_type = meta_OGG_ENO;
} else if (is_ys8) {
ovmi.meta_type = meta_OGG_YS8;
} else {
ovmi.meta_type = meta_OGG_VORBIS;
}
return init_vgmstream_ogg_vorbis_callbacks(streamFile, NULL, start_offset, &ovmi);

View File

@ -122,8 +122,9 @@ VGMSTREAM * init_vgmstream_ubi_jade(STREAMFILE *streamFile) {
switch(codec) {
case 0x0069: /* Xbox */
if (block_size != 0x24*channel_count)
goto fail;
if (fmt_size != 0x12) goto fail;
if (block_size != 0x24*channel_count) goto fail;
vgmstream->coding_type = coding_XBOX_IMA;
vgmstream->layout_type = layout_none;
@ -136,8 +137,9 @@ VGMSTREAM * init_vgmstream_ubi_jade(STREAMFILE *streamFile) {
break;
case 0xFFFF: /* PS2 */
if (block_size != 0x10)
goto fail;
if (fmt_size != 0x12) goto fail;
if (block_size != 0x10) goto fail;
vgmstream->coding_type = coding_PSX;
vgmstream->layout_type = layout_interleave;
@ -159,8 +161,9 @@ VGMSTREAM * init_vgmstream_ubi_jade(STREAMFILE *streamFile) {
break;
case 0xFFFE: /* GC/Wii */
if (block_size != 0x08)
goto fail;
if (fmt_size != 0x12) goto fail;
if (block_size != 0x08) goto fail;
vgmstream->coding_type = coding_NGC_DSP;
vgmstream->layout_type = layout_interleave;
@ -201,8 +204,9 @@ VGMSTREAM * init_vgmstream_ubi_jade(STREAMFILE *streamFile) {
break;
case 0x0002: /* PC */
if (block_size != 0x24*channel_count)
goto fail;
if (fmt_size != 0x12) goto fail;
if (block_size != 0x24*channel_count) goto fail;
vgmstream->coding_type = coding_MSADPCM;
vgmstream->layout_type = layout_none;
vgmstream->interleave_block_size = 0x24*channel_count;
@ -219,8 +223,8 @@ VGMSTREAM * init_vgmstream_ubi_jade(STREAMFILE *streamFile) {
VGMSTREAM *temp_vgmstream = NULL;
STREAMFILE *temp_streamFile = NULL;
if (block_size != 0x02*channel_count)
goto fail;
if (fmt_size != 0x10) goto fail;
if (block_size != 0x02*channel_count) goto fail;
/* a MSF (usually ATRAC3) masquerading as PCM */
if (read_32bitBE(start_offset, streamFile) != 0x4D534643) /* "MSF\43" */

View File

@ -40,7 +40,7 @@ typedef struct {
/* Wwise - Audiokinetic Wwise (Wave Works Interactive Sound Engine) middleware */
VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
wwise_header ww;
wwise_header ww = {0};
off_t start_offset, first_offset = 0xc;
int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL;
int16_t (*read_16bit)(off_t,STREAMFILE*) = NULL;
@ -57,7 +57,6 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) {
(read_32bitBE(0x08,streamFile) != 0x58574D41)) /* "XWMA" */
goto fail;
memset(&ww,0,sizeof(wwise_header));
ww.big_endian = read_32bitBE(0x00,streamFile) == 0x52494658;/* RIFX */
if (ww.big_endian) { /* Wwise honors machine's endianness (PC=RIFF, X360=RIFX --unlike XMA) */
@ -165,10 +164,19 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) {
VGM_LOG("WWISE: unknown codec 0x%x \n", ww.format);
goto fail;
}
/* fix for newer Wwise DSP with coefs: Epic Mickey 2 (Wii), Batman Arkham Origins Blackgate (3DS) */
if (ww.format == 0x0002 && ww.extra_size == 0x0c + ww.channels * 0x2e) {
ww.codec = DSP;
}
else if (ww.format == 0x0002 && ww.block_align == 0x104 * ww.channels) {
//ww.codec = SWITCH_ADPCM;
/* unknown codec, found in Bayonetta 2 (Switch)
* frames of 0x104 per ch, possibly frame header is hist1(2)/hist2(2)/predictor(1)
* (may write 2 header samples + FF*2 nibbles = 0x200 samples per block?) */
goto fail;
}
/* Some Wwise files (ex. Oddworld PSV, Bayonetta 2 WiiU, often in BGM.bnk) are truncated mirrors of another file.
* They come in RAM banks, probably to play the beginning while the rest of the real stream loads.
@ -196,6 +204,7 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) {
switch(ww.codec) {
case PCM: /* common */
/* normally riff.c has priority but it's needed when .wem is used */
if (ww.fmt_size != 0x10 && ww.fmt_size != 0x18) goto fail; /* old, new */
if (ww.bits_per_sample != 16) goto fail;
vgmstream->coding_type = (ww.big_endian ? coding_PCM16BE : coding_PCM16LE);
@ -209,8 +218,10 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) {
/* slightly modified XBOX-IMA */
/* Wwise reuses common codec ids (ex. 0x0002 MSADPCM) for IMA so this parser should go AFTER riff.c avoid misdetection */
if (ww.fmt_size != 0x28 && ww.fmt_size != 0x18) goto fail; /* old, new */
if (ww.bits_per_sample != 4) goto fail;
if (ww.block_align != 0x24 * ww.channels) goto fail;
vgmstream->coding_type = coding_WWISE_IMA;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = ww.block_align / ww.channels;
@ -227,9 +238,8 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) {
/* Wwise uses custom Vorbis, which changed over time (config must be detected to pass to the decoder). */
off_t vorb_offset, data_offsets, block_offsets;
size_t vorb_size, setup_offset, audio_offset;
vorbis_custom_config cfg;
vorbis_custom_config cfg = {0};
memset(&cfg, 0, sizeof(vorbis_custom_config));
cfg.channels = ww.channels;
cfg.sample_rate = ww.sample_rate;
cfg.big_endian = ww.big_endian;
@ -369,11 +379,12 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) {
size_t wiih_size;
int i;
//if (ww.fmt_size != 0x28 && ww.fmt_size != ?) goto fail; /* old, new */
if (ww.bits_per_sample != 4) goto fail;
vgmstream->coding_type = coding_NGC_DSP;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = 0x8; /* ww.block_align = 0x8 in older Wwise, samples per block in newer Wwise */
vgmstream->interleave_block_size = 0x08; /* ww.block_align = 0x8 in older Wwise, samples per block in newer Wwise */
/* find coef position */
if (find_chunk(streamFile, 0x57696948,first_offset,0, &wiih_offset,&wiih_size, ww.big_endian, 0)) { /*"WiiH"*/ /* older Wwise */
@ -406,6 +417,7 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) {
off_t xma2_offset;
size_t xma2_size;
if (ww.fmt_size != 0x20 && ww.fmt_size != 0x34 && ww.fmt_size != 0x40) goto fail; /* XMA1, XMA2old, XMA2new */
if (!ww.big_endian) goto fail; /* must be Wwise (real XMA are LE and parsed elsewhere) */
if (find_chunk(streamFile, 0x584D4132,first_offset,0, &xma2_offset,&xma2_size, ww.big_endian, 0)) { /*"XMA2"*/ /* older Wwise */
@ -413,7 +425,6 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) {
} else { /* newer Wwise */
bytes = ffmpeg_make_riff_xma_from_fmt_chunk(buf,0x100, ww.fmt_offset, ww.fmt_size, ww.data_size, streamFile, ww.big_endian);
}
if (bytes <= 0) goto fail;
vgmstream->codec_data = init_ffmpeg_header_offset(streamFile, buf,bytes, ww.data_offset,ww.data_size);
if ( !vgmstream->codec_data ) goto fail;
@ -435,11 +446,10 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) {
uint8_t buf[0x100];
int bytes;
if (ww.fmt_size != 0x18) goto fail;
if (!ww.big_endian) goto fail; /* must be from Wwise X360 (PC LE XWMA is parsed elsewhere) */
bytes = ffmpeg_make_riff_xwma(buf,0x100, ww.format, ww.data_size, vgmstream->channels, vgmstream->sample_rate, ww.average_bps, ww.block_align);
if (bytes <= 0) goto fail;
ffmpeg_data = init_ffmpeg_header_offset(streamFile, buf,bytes, ww.data_offset,ww.data_size);
if ( !ffmpeg_data ) goto fail;
vgmstream->codec_data = ffmpeg_data;
@ -449,8 +459,7 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) {
/* manually find total samples, why don't they put this in the header is beyond me */
{
ms_sample_data msd;
memset(&msd,0,sizeof(ms_sample_data));
ms_sample_data msd = {0};
msd.channels = ww.channels;
msd.data_offset = ww.data_offset;
@ -472,6 +481,8 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) {
case AAC: { /* iOS/Mac */
ffmpeg_codec_data * ffmpeg_data = NULL;
if (ww.fmt_size != 0x24) goto fail;
if (ww.block_align != 0 || ww.bits_per_sample != 0) goto fail;
/* extra: size 0x12, unknown values */
@ -489,7 +500,7 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) {
case OPUS: { /* Switch */
uint8_t buf[0x100];
size_t bytes, skip;
ffmpeg_custom_config cfg;
ffmpeg_custom_config cfg = {0};
/* values up to 0x14 seem fixed and similar to HEVAG's (block_align 0x02/04, bits_per_sample 0x10) */
if (ww.fmt_size == 0x28) {
@ -508,13 +519,10 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) {
skip = 0; /* Wwise doesn't seem to use it? (0x138 /0x3E8 ~default) */
bytes = ffmpeg_make_opus_header(buf,0x100, ww.channels, skip, ww.sample_rate);
if (bytes <= 0) goto fail;
memset(&cfg, 0, sizeof(ffmpeg_custom_config));
cfg.type = FFMPEG_SWITCH_OPUS;
//cfg.big_endian = ww.big_endian; /* internally BE */
bytes = ffmpeg_make_opus_header(buf,0x100, ww.channels, skip, ww.sample_rate);
vgmstream->codec_data = init_ffmpeg_config(streamFile, buf,bytes, start_offset,ww.data_size, &cfg);
if (!vgmstream->codec_data) goto fail;
@ -529,6 +537,7 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) {
//ww.bits_per_sample; /* unknown (0x10) */
//if (ww.bits_per_sample != 4) goto fail;
if (ww.fmt_size != 0x18) goto fail;
if (ww.big_endian) goto fail;
/* extra_data: size 0x06, @0x00: samples per block (0x1c), @0x04: channel config */
@ -543,6 +552,8 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) {
#ifdef VGM_USE_ATRAC9
case ATRAC9: { /* PSV/PS4 */
atrac9_config cfg = {0};
if (ww.fmt_size != 0x24) goto fail;
if (ww.extra_size != 0x12) goto fail;
cfg.channels = vgmstream->channels;

View File

@ -676,6 +676,7 @@ typedef enum {
meta_UBI_BAO, /* Ubisoft BAO */
meta_DSP_SWITCH_AUDIO, /* Gal Gun 2 (Switch) */
meta_TA_AAC_VITA, /* tri-Ace AAC (Judas Code) */
meta_OGG_GWM, /* Ogg Vorbis with encryption [Metronomicon (PC)] */
#ifdef VGM_USE_FFMPEG
meta_FFmpeg,