From b23e1fbac747407bff07b01ed2fc4d81b2f75d47 Mon Sep 17 00:00:00 2001 From: bnnm Date: Sat, 19 May 2018 11:21:40 +0200 Subject: [PATCH 1/5] Extra validations for Wwise/Jade --- src/meta/ubi_jade.c | 24 ++++++++++++++---------- src/meta/wwise.c | 41 ++++++++++++++++++++++++++--------------- 2 files changed, 40 insertions(+), 25 deletions(-) diff --git a/src/meta/ubi_jade.c b/src/meta/ubi_jade.c index aab40b98..47906398 100644 --- a/src/meta/ubi_jade.c +++ b/src/meta/ubi_jade.c @@ -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" */ diff --git a/src/meta/wwise.c b/src/meta/wwise.c index 53d1e3e3..4871a53f 100644 --- a/src/meta/wwise.c +++ b/src/meta/wwise.c @@ -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; From 1888990b75b790739595c8e9844ec4551a49600e Mon Sep 17 00:00:00 2001 From: bnnm Date: Sat, 19 May 2018 11:22:08 +0200 Subject: [PATCH 2/5] Add HCA key --- src/meta/hca_keys.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/meta/hca_keys.h b/src/meta/hca_keys.h index fce65608..82c43fa2 100644 --- a/src/meta/hca_keys.h +++ b/src/meta/hca_keys.h @@ -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_*/ From 883e018aaba64069663902216a2b56cb8f150309 Mon Sep 17 00:00:00 2001 From: bnnm Date: Sat, 19 May 2018 11:22:29 +0200 Subject: [PATCH 3/5] Fix nus3bank with varying header [Super Smash Bros (Wii U)] --- src/meta/ngc_dsp_std.c | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/src/meta/ngc_dsp_std.c b/src/meta/ngc_dsp_std.c index 990a1c21..3754d1ec 100644 --- a/src/meta/ngc_dsp_std.c +++ b/src/meta/ngc_dsp_std.c @@ -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; - offset += 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 */ From f1f165a8157642d24790604b080f8a09f6b7dc07 Mon Sep 17 00:00:00 2001 From: bnnm Date: Sat, 19 May 2018 11:24:20 +0200 Subject: [PATCH 4/5] Minor Ogg Vorbis cleanup --- src/meta/ogg_vorbis.c | 65 +++++++++++++++---------------------------- 1 file changed, 23 insertions(+), 42 deletions(-) diff --git a/src/meta/ogg_vorbis.c b/src/meta/ogg_vorbis.c index 67d21149..8041e79b 100644 --- a/src/meta/ogg_vorbis.c +++ b/src/meta/ogg_vorbis.c @@ -223,17 +223,14 @@ 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; /* 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) */ @@ -255,22 +252,26 @@ VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) { 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 +280,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 +291,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 +302,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 +319,28 @@ 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; - } - - 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); From a57134dc2bd259f3dcb959dfd45c96493d577f70 Mon Sep 17 00:00:00 2001 From: bnnm Date: Sat, 19 May 2018 11:37:21 +0200 Subject: [PATCH 5/5] Add .gwd Ogg [Adagio Cloudburst (PC)] --- src/formats.c | 2 ++ src/meta/ogg_vorbis.c | 22 ++++++++++++++++++++++ src/vgmstream.h | 1 + 3 files changed, 25 insertions(+) diff --git a/src/formats.c b/src/formats.c index aa893007..9ee28ba9 100644 --- a/src/formats.c +++ b/src/formats.c @@ -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"}, diff --git a/src/meta/ogg_vorbis.c b/src/meta/ogg_vorbis.c index 8041e79b..93b3b955 100644 --- a/src/meta/ogg_vorbis.c +++ b/src/meta/ogg_vorbis.c @@ -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}; @@ -227,6 +238,7 @@ VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) { int is_isd = 0; int is_rpgmvo = 0; int is_eno = 0; + int is_gwm = 0; /* check extension */ @@ -248,6 +260,8 @@ 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; } @@ -342,6 +356,14 @@ VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) { } + /* 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; + } + + return init_vgmstream_ogg_vorbis_callbacks(streamFile, NULL, start_offset, &ovmi); fail: diff --git a/src/vgmstream.h b/src/vgmstream.h index 01ed8682..fe019961 100644 --- a/src/vgmstream.h +++ b/src/vgmstream.h @@ -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,