Updated VGMStream to r1050-2345-g1d28e0f5

CQTexperiment
Christopher Snowhill 2019-07-02 15:54:31 -07:00
parent 73b7f3651f
commit 7657eb4971
15 changed files with 559 additions and 219 deletions

View File

@ -213,6 +213,8 @@
8351F32D2212B57000A606E4 /* 208.c in Sources */ = {isa = PBXBuildFile; fileRef = 8351F32A2212B57000A606E4 /* 208.c */; }; 8351F32D2212B57000A606E4 /* 208.c in Sources */ = {isa = PBXBuildFile; fileRef = 8351F32A2212B57000A606E4 /* 208.c */; };
8351F32E2212B57000A606E4 /* ubi_bao_streamfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 8351F32B2212B57000A606E4 /* ubi_bao_streamfile.h */; }; 8351F32E2212B57000A606E4 /* ubi_bao_streamfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 8351F32B2212B57000A606E4 /* ubi_bao_streamfile.h */; };
8351F32F2212B57000A606E4 /* dsf.c in Sources */ = {isa = PBXBuildFile; fileRef = 8351F32C2212B57000A606E4 /* dsf.c */; }; 8351F32F2212B57000A606E4 /* dsf.c in Sources */ = {isa = PBXBuildFile; fileRef = 8351F32C2212B57000A606E4 /* dsf.c */; };
835C883622CC17BE001B4B3F /* bwav.c in Sources */ = {isa = PBXBuildFile; fileRef = 835C883122CC17BD001B4B3F /* bwav.c */; };
835C883722CC17BE001B4B3F /* ogg_vorbis_streamfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 835C883522CC17BE001B4B3F /* ogg_vorbis_streamfile.h */; };
836F6B4718BDB8880095E648 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 836F6B4518BDB8880095E648 /* InfoPlist.strings */; }; 836F6B4718BDB8880095E648 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 836F6B4518BDB8880095E648 /* InfoPlist.strings */; };
836F6F1E18BDC2190095E648 /* acm_decoder.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6DE018BDC2180095E648 /* acm_decoder.c */; }; 836F6F1E18BDC2190095E648 /* acm_decoder.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6DE018BDC2180095E648 /* acm_decoder.c */; };
836F6F2018BDC2190095E648 /* adx_decoder.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6DE218BDC2180095E648 /* adx_decoder.c */; }; 836F6F2018BDC2190095E648 /* adx_decoder.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6DE218BDC2180095E648 /* adx_decoder.c */; };
@ -873,6 +875,8 @@
8351F32A2212B57000A606E4 /* 208.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = 208.c; sourceTree = "<group>"; }; 8351F32A2212B57000A606E4 /* 208.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = 208.c; sourceTree = "<group>"; };
8351F32B2212B57000A606E4 /* ubi_bao_streamfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ubi_bao_streamfile.h; sourceTree = "<group>"; }; 8351F32B2212B57000A606E4 /* ubi_bao_streamfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ubi_bao_streamfile.h; sourceTree = "<group>"; };
8351F32C2212B57000A606E4 /* dsf.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = dsf.c; sourceTree = "<group>"; }; 8351F32C2212B57000A606E4 /* dsf.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = dsf.c; sourceTree = "<group>"; };
835C883122CC17BD001B4B3F /* bwav.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = bwav.c; sourceTree = "<group>"; };
835C883522CC17BE001B4B3F /* ogg_vorbis_streamfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ogg_vorbis_streamfile.h; sourceTree = "<group>"; };
836F6B3918BDB8880095E648 /* libvgmstream.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = libvgmstream.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 836F6B3918BDB8880095E648 /* libvgmstream.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = libvgmstream.framework; sourceTree = BUILT_PRODUCTS_DIR; };
836F6B4418BDB8880095E648 /* libvgmstream-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "libvgmstream-Info.plist"; sourceTree = "<group>"; }; 836F6B4418BDB8880095E648 /* libvgmstream-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "libvgmstream-Info.plist"; sourceTree = "<group>"; };
836F6B4618BDB8880095E648 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; }; 836F6B4618BDB8880095E648 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
@ -1526,6 +1530,7 @@
836F6E3918BDC2180095E648 /* bnsf.c */, 836F6E3918BDC2180095E648 /* bnsf.c */,
836F6E3A18BDC2180095E648 /* brstm.c */, 836F6E3A18BDC2180095E648 /* brstm.c */,
83EDE5D71A70951A005F5D84 /* btsnd.c */, 83EDE5D71A70951A005F5D84 /* btsnd.c */,
835C883122CC17BD001B4B3F /* bwav.c */,
8306B0CF2098458F000302D4 /* caf.c */, 8306B0CF2098458F000302D4 /* caf.c */,
836F6E3B18BDC2180095E648 /* capdsp.c */, 836F6E3B18BDC2180095E648 /* capdsp.c */,
834FE0E8215C79EC000A5D3D /* ck.c */, 834FE0E8215C79EC000A5D3D /* ck.c */,
@ -1657,6 +1662,7 @@
834FE0DB215C79EA000A5D3D /* nxa.c */, 834FE0DB215C79EA000A5D3D /* nxa.c */,
8306B0C02098458C000302D4 /* nxap.c */, 8306B0C02098458C000302D4 /* nxap.c */,
832BF81321E05149006F50F1 /* ogg_opus.c */, 832BF81321E05149006F50F1 /* ogg_opus.c */,
835C883522CC17BE001B4B3F /* ogg_vorbis_streamfile.h */,
83A21F7F201D8980000F04B9 /* ogg_vorbis.c */, 83A21F7F201D8980000F04B9 /* ogg_vorbis.c */,
831BA60F1EAC61A500CF89B0 /* ogl.c */, 831BA60F1EAC61A500CF89B0 /* ogl.c */,
8349A8FB1FE6257F00E26435 /* omu.c */, 8349A8FB1FE6257F00E26435 /* omu.c */,
@ -1920,6 +1926,7 @@
832BF82C21E0514B006F50F1 /* hca_keys_awb.h in Headers */, 832BF82C21E0514B006F50F1 /* hca_keys_awb.h in Headers */,
8306B0E020984590000302D4 /* ppst_streamfile.h in Headers */, 8306B0E020984590000302D4 /* ppst_streamfile.h in Headers */,
834FE0BA215C798C000A5D3D /* ea_mt_decoder_utk.h in Headers */, 834FE0BA215C798C000A5D3D /* ea_mt_decoder_utk.h in Headers */,
835C883722CC17BE001B4B3F /* ogg_vorbis_streamfile.h in Headers */,
832BF82021E0514B006F50F1 /* zsnd_streamfile.h in Headers */, 832BF82021E0514B006F50F1 /* zsnd_streamfile.h in Headers */,
8375737721F950ED00F01AF5 /* ubi_sb_streamfile.h in Headers */, 8375737721F950ED00F01AF5 /* ubi_sb_streamfile.h in Headers */,
839E21E01F2EDAF100EE54D7 /* vorbis_custom_data_fsb.h in Headers */, 839E21E01F2EDAF100EE54D7 /* vorbis_custom_data_fsb.h in Headers */,
@ -2200,6 +2207,7 @@
8306B0ED20984590000302D4 /* txtp.c in Sources */, 8306B0ED20984590000302D4 /* txtp.c in Sources */,
836F6FA018BDC2190095E648 /* musx.c in Sources */, 836F6FA018BDC2190095E648 /* musx.c in Sources */,
836F705818BDC2190095E648 /* vgmstream.c in Sources */, 836F705818BDC2190095E648 /* vgmstream.c in Sources */,
835C883622CC17BE001B4B3F /* bwav.c in Sources */,
8349A90A1FE6258200E26435 /* sab.c in Sources */, 8349A90A1FE6258200E26435 /* sab.c in Sources */,
836F6F6818BDC2190095E648 /* ads.c in Sources */, 836F6F6818BDC2190095E648 /* ads.c in Sources */,
8306B08620984518000302D4 /* fadpcm_decoder.c in Sources */, 8306B08620984518000302D4 /* fadpcm_decoder.c in Sources */,

View File

@ -78,18 +78,23 @@ int mpeg_custom_setup_init_default(STREAMFILE *streamFile, off_t start_offset, m
//todo: test more: this improves the output, but seems formats aren't usually prepared //todo: test more: this improves the output, but seems formats aren't usually prepared
// (and/or the num_samples includes all possible samples in file, so by discarding some it'll reach EOF) // (and/or the num_samples includes all possible samples in file, so by discarding some it'll reach EOF)
#if 0
/* set encoder delay (samples to skip at the beginning of a stream) if needed, which varies with encoder used */ /* set encoder delay (samples to skip at the beginning of a stream) if needed, which varies with encoder used */
switch(data->type) { switch(data->type) {
case MPEG_AHX: data->skip_samples = 480; break; /* observed default */ //case MPEG_AHX: data->skip_samples = 480; break; /* observed default */
case MPEG_P3D: data->skip_samples = info.frame_samples; break; /* matches Radical ADPCM (PC) output */ //case MPEG_P3D: data->skip_samples = info.frame_samples; break; /* matches Radical ADPCM (PC) output */
/* FSBs (with FMOD DLLs) don't seem to need it. Particularly a few games (all from Wayforward?) /* FSBs (with FMOD DLLs) don't seem to need it. Particularly a few games (all from Wayforward?)
* contain audible garbage at the beginning, but it's actually there in-game too */ * contain audible garbage at the beginning, but it's actually there in-game too */
case MPEG_FSB: data->skip_samples = 0; //case MPEG_FSB: data->skip_samples = 0; break;
default: break;
case MPEG_XVAG: /* set in header and needed for gapless looping */
data->skip_samples = data->config.skip_samples; break;
default:
break;
} }
data->samples_to_discard = data->skip_samples; data->samples_to_discard = data->skip_samples;
#endif
return 1; return 1;
fail: fail:

View File

@ -101,6 +101,7 @@ static const char* extension_list[] = {
"brstmspm", "brstmspm",
"btsnd", "btsnd",
"bvg", "bvg",
"bwav",
"caf", "caf",
"capdsp", "capdsp",
@ -1189,6 +1190,7 @@ static const meta_info meta_info_list[] = {
{meta_MSF_KONAMI, "Konami MSF header"}, {meta_MSF_KONAMI, "Konami MSF header"},
{meta_XWMA_KONAMI, "Konami XWMA header"}, {meta_XWMA_KONAMI, "Konami XWMA header"},
{meta_9TAV, "Konami 9TAV header"}, {meta_9TAV, "Konami 9TAV header"},
{meta_BWAV, "Nintendo BWAV header"},
}; };

View File

@ -0,0 +1,49 @@
#include "meta.h"
#include "../coding/coding.h"
/* BWAV - NintendoWare(?) [Super Mario Maker 2 (Switch)] */
VGMSTREAM * init_vgmstream_bwav(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
off_t start_offset;
int channel_count, loop_flag;
size_t interleave = 0;
int32_t coef_start_offset, coef_spacing;
/* checks */
if (!check_extensions(streamFile, "bwav"))
goto fail;
/* BWAV header */
if (read_32bitBE(0x00, streamFile) != 0x42574156) /* "BWAV" */
goto fail;
channel_count = read_16bitLE(0x0E, streamFile);
start_offset = read_32bitLE(0x40, streamFile);
loop_flag = read_32bitLE(0x4C, streamFile);
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count, loop_flag);
if (!vgmstream) goto fail;
vgmstream->sample_rate = read_32bitLE(0x14, streamFile);
vgmstream->num_samples = read_32bitLE(0x18, streamFile);
vgmstream->loop_start_sample = read_32bitLE(0x50, streamFile);
vgmstream->loop_end_sample = read_32bitLE(0x4C, streamFile);
vgmstream->meta_type = meta_BWAV;
vgmstream->layout_type = (channel_count == 1) ? layout_none : layout_interleave;
vgmstream->interleave_block_size = read_32bitLE(0x8C, streamFile) - start_offset;
vgmstream->coding_type = coding_NGC_DSP;
coef_start_offset = 0x20;
coef_spacing = 0x4C;
dsp_read_coefs_le(vgmstream, streamFile, coef_start_offset, coef_spacing);
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
goto fail;
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;
}

View File

@ -35,9 +35,14 @@ VGMSTREAM * init_vgmstream_hd3_bd3(STREAMFILE *streamFile) {
if (read_32bitBE(section_offset+0x00,streamHeader) != 0x50335641) /* "P3VA" */ if (read_32bitBE(section_offset+0x00,streamHeader) != 0x50335641) /* "P3VA" */
goto fail; goto fail;
section_size = read_32bitBE(section_offset+0x04,streamHeader); /* (not including first 0x08) */ section_size = read_32bitBE(section_offset+0x04,streamHeader); /* (not including first 0x08) */
/* 0x08 always 0x10? */ /* 0x08 size of all subsong headers + 0x10 */
entries = read_32bitBE(section_offset+0x14,streamHeader) + 1;
if (entries != (section_size-0x18) / 0x10) entries = read_32bitBE(section_offset+0x14,streamHeader);
/* often there is an extra subsong than written, but may be padding instead */
if (read_32bitBE(section_offset + 0x20 + entries*0x10 + 0x04,streamHeader)) /* has sample rate */
entries += 1;
if (entries * 0x10 > section_size) /* just in case, padding after entries is possible */
goto fail; goto fail;
/* autodetect use of N bank entries as channels [Elevator Action Deluxe (PS3)] */ /* autodetect use of N bank entries as channels [Elevator Action Deluxe (PS3)] */

View File

@ -854,4 +854,6 @@ VGMSTREAM * init_vgmstream_9tav(STREAMFILE* streamFile);
VGMSTREAM * init_vgmstream_fsb5_fev_bank(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_fsb5_fev_bank(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_bwav(STREAMFILE * streamFile);
#endif /*_META_H*/ #endif /*_META_H*/

View File

@ -3,8 +3,9 @@
#ifdef VGM_USE_VORBIS #ifdef VGM_USE_VORBIS
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include "meta.h"
#include <vorbis/vorbisfile.h> #include <vorbis/vorbisfile.h>
#include "meta.h"
#include "ogg_vorbis_streamfile.h"
#define OGG_DEFAULT_BITSTREAM 0 #define OGG_DEFAULT_BITSTREAM 0
@ -110,64 +111,12 @@ static void psychic_ogg_decryption_callback(void *ptr, size_t size, size_t nmemb
size_t bytes_read = size*nmemb; size_t bytes_read = size*nmemb;
int i; int i;
/* bytes add 0x23 ('#') */ /* bytes add 0x23 ('#') */ //todo incorrect, add changes every 0x64 bytes
for (i = 0; i < bytes_read; i++) { for (i = 0; i < bytes_read; i++) {
((uint8_t*)ptr)[i] += 0x23; ((uint8_t*)ptr)[i] += 0x23;
} }
} }
static void sngw_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;
char *header_id = "OggS";
uint8_t key[4];
put_32bitBE(key, ov_streamfile->xor_value);
/* first "OggS" is changed and bytes are xor'd and nibble-swapped */
for (i = 0; i < bytes_read; i++) {
if (ov_streamfile->offset+i < 0x04) {
((uint8_t*)ptr)[i] = (uint8_t)header_id[(ov_streamfile->offset + i) % 4];
}
else {
uint8_t val = ((uint8_t*)ptr)[i] ^ key[(ov_streamfile->offset + i) % 4];
((uint8_t*)ptr)[i] = ((val << 4) & 0xf0) | ((val >> 4) & 0x0f);
}
}
}
static void isd_ogg_decryption_callback(void *ptr, size_t size, size_t nmemb, void *datasource) {
static const uint8_t key[16] = {
0xe0,0x00,0xe0,0x00,0xa0,0x00,0x00,0x00,0xe0,0x00,0xe0,0x80,0x40,0x40,0x40,0x00
};
size_t bytes_read = size*nmemb;
ogg_vorbis_streamfile * const ov_streamfile = datasource;
int i;
/* bytes are xor'd */
for (i = 0; i < bytes_read; i++) {
((uint8_t*)ptr)[i] ^= key[(ov_streamfile->offset + i) % 16];
}
}
static void l2sd_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;
char *header_id = "OggS";
/* first "OggS" is changed */
for (i = 0; i < bytes_read; i++) {
if (ov_streamfile->offset+i < 0x04) {
((uint8_t*)ptr)[i] = (uint8_t)header_id[(ov_streamfile->offset + i) % 4];
}
else {
break;
}
}
}
static void rpgmvo_ogg_decryption_callback(void *ptr, size_t size, size_t nmemb, void *datasource) { static void rpgmvo_ogg_decryption_callback(void *ptr, size_t size, size_t nmemb, void *datasource) {
static const uint8_t header[16] = { /* OggS, packet type, granule, stream id(empty) */ static const uint8_t header[16] = { /* OggS, packet type, granule, stream id(empty) */
0x4F,0x67,0x67,0x53,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 0x4F,0x67,0x67,0x53,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
@ -191,89 +140,6 @@ static void rpgmvo_ogg_decryption_callback(void *ptr, size_t size, size_t nmemb,
} }
} }
static void eno_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 */
for (i = 0; i < bytes_read; i++) {
((uint8_t*)ptr)[i] ^= (uint8_t)ov_streamfile->xor_value;
}
}
static void ys8_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 and nibble-swapped */
for (i = 0; i < bytes_read; i++) {
uint8_t val = ((uint8_t*)ptr)[i] ^ ov_streamfile->xor_value;
((uint8_t*)ptr)[i] = ((val << 4) & 0xf0) | ((val >> 4) & 0x0f);
}
}
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 */
for (i = 0; i < bytes_read; i++) {
((uint8_t*)ptr)[i] ^= (uint8_t)ov_streamfile->xor_value;
}
}
static void mus_ogg_decryption_callback(void *ptr, size_t size, size_t nmemb, void *datasource) {
static const uint8_t key[16] = {
0x21,0x4D,0x6F,0x01,0x20,0x4C,0x6E,0x02,0x1F,0x4B,0x6D,0x03,0x20,0x4C,0x6E,0x02
};
size_t bytes_read = size*nmemb;
ogg_vorbis_streamfile * const ov_streamfile = datasource;
int i;
char *header_id = "OggS";
/* first "OggS" is changed and bytes are xor'd */
for (i = 0; i < bytes_read; i++) {
if (ov_streamfile->offset+i < 0x04) { /* if decrypted gives "Mus " */
((uint8_t*)ptr)[i] = (uint8_t)header_id[(ov_streamfile->offset + i) % 4];
}
else {
((uint8_t*)ptr)[i] ^= key[(ov_streamfile->offset + i) % sizeof(key)];
}
}
}
static void lse_add_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 */
for (i = 0; i < bytes_read; i++) {
int key = (uint8_t)ov_streamfile->xor_value + ((ov_streamfile->offset + i) % 256);
((uint8_t*)ptr)[i] ^= key;
}
}
static void lse_ff_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;
char *header_id = "OggS";
/* first "OggS" is changed and bytes are xor'd */
for (i = 0; i < bytes_read; i++) {
if (ov_streamfile->offset+i < 0x04) {
((uint8_t*)ptr)[i] = (uint8_t)header_id[(ov_streamfile->offset + i) % 4];
}
else {
((uint8_t*)ptr)[i] ^= 0xFF;
}
}
}
static const uint32_t xiph_mappings[] = { static const uint32_t xiph_mappings[] = {
0, 0,
@ -290,6 +156,9 @@ static const uint32_t xiph_mappings[] = {
/* Ogg Vorbis, by way of libvorbisfile; may contain loop comments */ /* Ogg Vorbis, by way of libvorbisfile; may contain loop comments */
VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) { VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
STREAMFILE *temp_streamFile = NULL;
ogg_vorbis_io_config_data cfg = {0};
ogg_vorbis_meta_info_t ovmi = {0}; ogg_vorbis_meta_info_t ovmi = {0};
off_t start_offset = 0; off_t start_offset = 0;
@ -320,7 +189,7 @@ VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) {
is_kovs = 1; is_kovs = 1;
} else if (check_extensions(streamFile,"sngw")) { /* .sngw: Capcom [Devil May Cry 4 SE (PC), Biohazard 6 (PC)] */ } else if (check_extensions(streamFile,"sngw")) { /* .sngw: Capcom [Devil May Cry 4 SE (PC), Biohazard 6 (PC)] */
is_sngw = 1; is_sngw = 1;
} else if (check_extensions(streamFile,"isd")) { /* .isd: Azure Striker Gunvolt (PC) */ } else if (check_extensions(streamFile,"isd")) { /* .isd: Inti Creates PC games */
is_isd = 1; is_isd = 1;
} else if (check_extensions(streamFile,"rpgmvo")) { /* .rpgmvo: RPG Maker MV games (PC) */ } else if (check_extensions(streamFile,"rpgmvo")) { /* .rpgmvo: RPG Maker MV games (PC) */
is_rpgmvo = 1; is_rpgmvo = 1;
@ -341,14 +210,21 @@ VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) {
ovmi.decryption_callback = psychic_ogg_decryption_callback; ovmi.decryption_callback = psychic_ogg_decryption_callback;
ovmi.meta_type = meta_OGG_encrypted; ovmi.meta_type = meta_OGG_encrypted;
} }
else if (read_32bitBE(0x00,streamFile) == 0x4C325344) { /* "L2SD" [Lineage II Chronicle 4 (PC)] */ else if (read_32bitBE(0x00,streamFile) == 0x4C325344) { /* "L2SD" instead of "OggS" [Lineage II Chronicle 4 (PC)] */
ovmi.decryption_callback = l2sd_ogg_decryption_callback; cfg.is_header_swap = 1;
ovmi.meta_type = meta_OGG_encrypted; cfg.is_encrypted = 1;
} }
else if (read_32bitBE(0x00,streamFile) == 0x048686C5) { /* "OggS" XOR'ed + bitswapped [Ys VIII (PC)] */ else if (read_32bitBE(0x00,streamFile) == 0x048686C5) { /* "OggS" XOR'ed + bitswapped [Ys VIII (PC)] */
ovmi.xor_value = 0xF0; cfg.key[0] = 0xF0;
ovmi.decryption_callback = ys8_ogg_decryption_callback; cfg.key_len = 1;
ovmi.meta_type = meta_OGG_encrypted; cfg.is_nibble_swap = 1;
cfg.is_encrypted = 1;
}
else if (read_32bitBE(0x00,streamFile) == 0x00000000 && /* null instead of "OggS" [Yuppie Psycho (PC)] */
read_32bitBE(0x3a,streamFile) == 0x4F676753) {
cfg.is_header_swap = 1;
cfg.is_encrypted = 1;
} }
else if (read_32bitBE(0x00,streamFile) == 0x4f676753) { /* "OggS" (standard) */ else if (read_32bitBE(0x00,streamFile) == 0x4f676753) { /* "OggS" (standard) */
ovmi.meta_type = meta_OGG_VORBIS; ovmi.meta_type = meta_OGG_VORBIS;
@ -365,7 +241,7 @@ VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) {
ovmi.meta_type = meta_OGG_encrypted; ovmi.meta_type = meta_OGG_encrypted;
} }
if (is_kovs) { /* Koei Tecmo PC games] */ if (is_kovs) { /* Koei Tecmo PC games */
if (read_32bitBE(0x00,streamFile) != 0x4b4f5653) { /* "KOVS" */ if (read_32bitBE(0x00,streamFile) != 0x4b4f5653) { /* "KOVS" */
goto fail; goto fail;
} }
@ -379,24 +255,107 @@ VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) {
if (is_sngw) { /* [Capcom's MT Framework PC games] */ if (is_sngw) { /* [Capcom's MT Framework PC games] */
if (read_32bitBE(0x00,streamFile) != 0x4f676753) { /* "OggS" (optionally encrypted) */ if (read_32bitBE(0x00,streamFile) != 0x4f676753) { /* "OggS" (optionally encrypted) */
ovmi.xor_value = read_32bitBE(0x00,streamFile); cfg.key_len = read_streamfile(cfg.key, 0x00, 0x04, streamFile);
ovmi.decryption_callback = sngw_ogg_decryption_callback; cfg.is_header_swap = 1;
cfg.is_nibble_swap = 1;
cfg.is_encrypted = 1;
} }
ovmi.disable_reordering = 1; /* must be an MT Framework thing */ ovmi.disable_reordering = 1; /* must be an MT Framework thing */
ovmi.meta_type = meta_OGG_encrypted;
} }
if (is_isd) { /* [Gunvolt (PC)] */ if (is_isd) { /* Inti Creates PC games */
ovmi.decryption_callback = isd_ogg_decryption_callback; const char *isl_name = NULL;
ovmi.meta_type = meta_OGG_encrypted;
//todo looping unknown, not in Ogg comments /* check various encrypted "OggS" values */
// game has sound/GV_steam.* files with info about sound/stream/*.isd if (read_32bitBE(0x00,streamFile) == 0xAF678753) { /* Azure Striker Gunvolt (PC) */
//- .ish: constant id/names static const uint8_t isd_gv_key[16] = {
//- .isl: unknown table, maybe looping? 0xe0,0x00,0xe0,0x00,0xa0,0x00,0x00,0x00,0xe0,0x00,0xe0,0x80,0x40,0x40,0x40,0x00
//- .isf: format table, ordered like file numbers, 0x18 header with: };
// 0x00(2): ?, 0x02(2): channels, 0x04: sample rate, 0x08: skip samples (in PCM bytes), always 32000 cfg.key_len = sizeof(isd_gv_key);
// 0x0c(2): PCM block size, 0x0e(2): PCM bps, 0x10: null, 0x18: samples (in PCM bytes) memcpy(cfg.key, isd_gv_key, cfg.key_len);
isl_name = "GV_steam.isl";
}
else if (read_32bitBE(0x00,streamFile) == 0x0FE787D3) { /* Mighty Gunvolt (PC) */
static const uint8_t isd_mgv_key[120] = {
0x40,0x80,0xE0,0x80,0x40,0x40,0xA0,0x00,0xA0,0x40,0x00,0x80,0x00,0x40,0xA0,0x00,
0xC0,0x40,0xE0,0x00,0x60,0x40,0x80,0x00,0xA0,0x00,0xE0,0x00,0x60,0x40,0xC0,0x00,
0xA0,0x40,0xC0,0x80,0xE0,0x00,0x60,0x00,0x00,0x40,0x00,0x80,0xE0,0x80,0x40,0x00,
0xA0,0x80,0xA0,0x80,0x80,0xC0,0x60,0x00,0xA0,0x00,0xA0,0x80,0x40,0x80,0x60,0x00,
0x40,0xC0,0x20,0x00,0x20,0xC0,0x00,0x00,0x00,0xC0,0x20,0x00,0xC0,0xC0,0x60,0x00,
0xE0,0xC0,0x80,0x80,0x20,0x00,0x60,0x00,0xE0,0xC0,0xC0,0x00,0x20,0xC0,0xC0,0x00,
0x60,0x00,0xE0,0x80,0x00,0xC0,0x00,0x00,0x60,0x80,0x40,0x80,0x20,0x80,0x20,0x00,
0x80,0x40,0xE0,0x00,0x20,0x00,0x20,0x00,
};
cfg.key_len = sizeof(isd_mgv_key);
memcpy(cfg.key, isd_mgv_key, cfg.key_len);
isl_name = "MGV_steam.isl";
}
else if (read_32bitBE(0x00,streamFile) == 0x0FA74753) { /* Blaster Master Zero (PC) */
static const uint8_t isd_bmz_key[120] = {
0x40,0xC0,0x20,0x00,0x40,0xC0,0xC0,0x00,0x00,0x80,0xE0,0x80,0x80,0x40,0x20,0x00,
0x60,0xC0,0xC0,0x00,0xA0,0x80,0x60,0x00,0x40,0x40,0x20,0x00,0x60,0x40,0xC0,0x00,
0x60,0x80,0xC0,0x80,0x40,0xC0,0x00,0x00,0xA0,0xC0,0x80,0x80,0x60,0x80,0xA0,0x00,
0x40,0x80,0x60,0x00,0x20,0x00,0xC0,0x00,0x60,0x00,0xA0,0x80,0x40,0x40,0xA0,0x00,
0x40,0x40,0xC0,0x80,0x00,0x80,0x60,0x00,0x80,0xC0,0xA0,0x00,0xE0,0x40,0xC0,0x00,
0x20,0x80,0xE0,0x00,0x40,0xC0,0xA0,0x00,0xE0,0xC0,0xC0,0x80,0xE0,0x80,0xC0,0x00,
0x40,0x40,0x00,0x00,0x20,0x40,0x80,0x00,0xE0,0x80,0x20,0x80,0x40,0x80,0xE0,0x00,
0xA0,0x00,0xC0,0x80,0xE0,0x00,0x20,0x00
};
cfg.key_len = sizeof(isd_bmz_key);
memcpy(cfg.key, isd_bmz_key, cfg.key_len);
isl_name = "output.isl";
}
else {
goto fail;
}
cfg.is_encrypted = 1;
/* .isd have companion files in the prev folder:
* - .ish: constant id/names (not always)
* - .isf: format table, ordered like file id/numbers, 0x18 header with:
* 0x00(2): ?, 0x02(2): channels, 0x04: sample rate, 0x08: skip samples (in PCM bytes), always 32000
* 0x0c(2): PCM block size, 0x0e(2): PCM bps, 0x10: null, 0x18: samples (in PCM bytes)
* - .isl: looping table (encrypted like the files) */
if (isl_name) {
STREAMFILE *islFile = NULL;
//todo could try in ../(file) first since that's how the .isl is stored
islFile = open_streamfile_by_filename(streamFile, isl_name);
if (islFile) {
STREAMFILE *dec_sf = NULL;
dec_sf = setup_ogg_vorbis_streamfile(islFile, cfg);
if (dec_sf) {
off_t loop_offset;
char basename[PATH_LIMIT];
/* has a bunch of tables then a list with file names without extension and loops */
loop_offset = read_32bitLE(0x18, dec_sf);
get_streamfile_basename(streamFile, basename, sizeof(basename));
while (loop_offset < get_streamfile_size(dec_sf)) {
char testname[0x20];
read_string(testname, sizeof(testname), loop_offset+0x2c, dec_sf);
if (strcmp(basename, testname) == 0) {
ovmi.loop_start = read_32bitLE(loop_offset+0x1c, dec_sf);
ovmi.loop_end = read_32bitLE(loop_offset+0x20, dec_sf);
ovmi.loop_end_found = 1;
ovmi.loop_flag = (ovmi.loop_end != 0);
break;
}
loop_offset += 0x50;
}
close_streamfile(dec_sf);
}
close_streamfile(islFile);
}
}
} }
if (is_rpgmvo) { /* [RPG Maker MV (PC)] */ if (is_rpgmvo) { /* [RPG Maker MV (PC)] */
@ -411,42 +370,63 @@ VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) {
} }
if (is_eno) { /* [Metronomicon (PC)] */ if (is_eno) { /* [Metronomicon (PC)] */
/* first byte probably derives into xor key, but this works too */ /* first byte probably derives into key, but this works too */
ovmi.xor_value = read_8bit(0x05,streamFile); /* always zero = easy key */ cfg.key[0] = (uint8_t)read_8bit(0x05,streamFile); /* regular ogg have a zero at this offset = easy key */;
ovmi.decryption_callback = eno_ogg_decryption_callback; cfg.key_len = 1;
ovmi.meta_type = meta_OGG_encrypted; cfg.is_encrypted = 1;
start_offset = 0x01; /* "OggS" starts after key-thing */
start_offset = 0x01;
} }
if (is_gwm) { /* [Adagio: Cloudburst (PC)] */ if (is_gwm) { /* [Adagio: Cloudburst (PC)] */
ovmi.xor_value = 0x5D; cfg.key[0] = 0x5D;
ovmi.decryption_callback = gwm_ogg_decryption_callback; cfg.key_len = 1;
ovmi.meta_type = meta_OGG_encrypted; cfg.is_encrypted = 1;
} }
if (is_mus) { /* [Redux - Dark Matters (PC)] */ if (is_mus) { /* [Redux - Dark Matters (PC)] */
ovmi.decryption_callback = mus_ogg_decryption_callback; static const uint8_t mus_key[16] = {
ovmi.meta_type = meta_OGG_encrypted; 0x21,0x4D,0x6F,0x01,0x20,0x4C,0x6E,0x02,0x1F,0x4B,0x6D,0x03,0x20,0x4C,0x6E,0x02
};
cfg.key_len = sizeof(mus_key);
memcpy(cfg.key, mus_key, cfg.key_len);
cfg.is_header_swap = 1; /* decrypted header gives "Mus " */
cfg.is_encrypted = 1;
} }
if (is_lse) { /* [Nippon Ichi PC games] */ if (is_lse) { /* [Nippon Ichi PC games] */
if (read_32bitBE(0x00,streamFile) == 0xFFFFFFFF) { /* [Operation Abyss: New Tokyo Legacy (PC)] */ if (read_32bitBE(0x00,streamFile) == 0xFFFFFFFF) { /* [Operation Abyss: New Tokyo Legacy (PC)] */
ovmi.decryption_callback = lse_ff_ogg_decryption_callback; cfg.key[0] = 0xFF;
ovmi.meta_type = meta_OGG_encrypted; cfg.key_len = 1;
cfg.is_header_swap = 1;
cfg.is_encrypted = 1;
} }
else { /* [Operation Babel: New Tokyo Legacy (PC), Labyrinth of Refrain: Coven of Dusk (PC)] */ else { /* [Operation Babel: New Tokyo Legacy (PC), Labyrinth of Refrain: Coven of Dusk (PC)] */
ovmi.decryption_callback = lse_add_ogg_decryption_callback; int i;
ovmi.xor_value = (uint8_t)read_8bit(0x04,streamFile) - 0x04; /* found at file_size-1 but this works too (same key for most files but can vary) */
ovmi.meta_type = meta_OGG_encrypted; uint8_t base_key = (uint8_t)read_8bit(0x04,streamFile) - 0x04;
/* key is found at file_size-1 but this works too (same key for most files but can vary) */
cfg.key_len = 256;
for (i = 0; i < cfg.key_len; i++) {
cfg.key[i] = (uint8_t)(base_key + i);
}
cfg.is_encrypted = 1;
} }
} }
if (cfg.is_encrypted) {
ovmi.meta_type = meta_OGG_encrypted;
return init_vgmstream_ogg_vorbis_callbacks(streamFile, NULL, start_offset, &ovmi); temp_streamFile = setup_ogg_vorbis_streamfile(streamFile, cfg);
if (!temp_streamFile) goto fail;
}
vgmstream = init_vgmstream_ogg_vorbis_callbacks(temp_streamFile != NULL ? temp_streamFile : streamFile, NULL, start_offset, &ovmi);
close_streamfile(temp_streamFile);
return vgmstream;
fail: fail:
close_streamfile(temp_streamFile);
return NULL; return NULL;
} }
@ -528,6 +508,7 @@ VGMSTREAM * init_vgmstream_ogg_vorbis_callbacks(STREAMFILE *streamFile, ov_callb
ovf = &data->ogg_vorbis_file; ovf = &data->ogg_vorbis_file;
} }
//todo could set bitstreams as subsongs?
/* get info from bitstream 0 */ /* get info from bitstream 0 */
data->bitstream = OGG_DEFAULT_BITSTREAM; data->bitstream = OGG_DEFAULT_BITSTREAM;
vi = ov_info(ovf,OGG_DEFAULT_BITSTREAM); vi = ov_info(ovf,OGG_DEFAULT_BITSTREAM);
@ -536,7 +517,7 @@ VGMSTREAM * init_vgmstream_ogg_vorbis_callbacks(STREAMFILE *streamFile, ov_callb
data->disable_reordering = ovmi->disable_reordering; data->disable_reordering = ovmi->disable_reordering;
/* search for loop comments */ /* search for loop comments */
{ {//todo ignore if loop flag already set?
int i; int i;
vorbis_comment *comment = ov_comment(ovf,OGG_DEFAULT_BITSTREAM); vorbis_comment *comment = ov_comment(ovf,OGG_DEFAULT_BITSTREAM);

View File

@ -0,0 +1,76 @@
#ifndef _OGG_VORBIS_STREAMFILE_H_
#define _OGG_VORBIS_STREAMFILE_H_
#include "../streamfile.h"
typedef struct {
int is_encrypted;
uint8_t key[0x100];
size_t key_len;
int is_nibble_swap;
int is_header_swap;
} ogg_vorbis_io_config_data;
typedef struct {
/* config */
ogg_vorbis_io_config_data cfg;
} ogg_vorbis_io_data;
static size_t ogg_vorbis_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length, ogg_vorbis_io_data* data) {
size_t bytes_read;
int i;
static const uint8_t header_swap[4] = { 0x4F,0x67,0x67,0x53 }; /* "OggS" */
static const size_t header_size = 0x04;
bytes_read = streamfile->read(streamfile, dest, offset, length);
if (data->cfg.is_encrypted) {
for (i = 0; i < bytes_read; i++) {
if (data->cfg.is_header_swap && (offset + i) < header_size) {
dest[i] = header_swap[(offset + i) % header_size];
}
else {
if (!data->cfg.key_len && !data->cfg.is_nibble_swap)
break;
if (data->cfg.key_len)
dest[i] ^= data->cfg.key[(offset + i) % data->cfg.key_len];
if (data->cfg.is_nibble_swap)
dest[i] = ((dest[i] << 4) & 0xf0) | ((dest[i] >> 4) & 0x0f);
}
}
}
return bytes_read;
}
//todo maybe use generic decryption streamfile
static STREAMFILE* setup_ogg_vorbis_streamfile(STREAMFILE *streamFile, ogg_vorbis_io_config_data cfg) {
STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL;
ogg_vorbis_io_data io_data = {0};
size_t io_data_size = sizeof(ogg_vorbis_io_data);
/* setup decryption */
io_data.cfg = cfg; /* memcpy */
/* setup custom streamfile */
new_streamFile = open_wrap_streamfile(streamFile);
if (!new_streamFile) goto fail;
temp_streamFile = new_streamFile;
//todo extension .ogg?
new_streamFile = open_io_streamfile(temp_streamFile, &io_data,io_data_size, ogg_vorbis_io_read,NULL);
if (!new_streamFile) goto fail;
temp_streamFile = new_streamFile;
return temp_streamFile;
fail:
close_streamfile(temp_streamFile);
return NULL;
}
#endif /* _OGG_VORBIS_STREAMFILE_H_ */

View File

@ -19,7 +19,8 @@ static VGMSTREAM * init_vgmstream_opus(STREAMFILE *streamFile, meta_t meta_type,
channel_count = read_8bit(offset + 0x09, streamFile); channel_count = read_8bit(offset + 0x09, streamFile);
/* 0x0a: packet size if CBR, 0 if VBR */ /* 0x0a: packet size if CBR, 0 if VBR */
data_offset = offset + read_32bitLE(offset + 0x10, streamFile); data_offset = offset + read_32bitLE(offset + 0x10, streamFile);
skip = read_32bitLE(offset + 0x1c, streamFile); skip = read_16bitLE(offset + 0x1c, streamFile);
/* 0x1e: ? (seen in Lego Movie 2 (Switch)) */
if ((uint32_t)read_32bitLE(data_offset, streamFile) != 0x80000004) if ((uint32_t)read_32bitLE(data_offset, streamFile) != 0x80000004)
goto fail; goto fail;

View File

@ -154,6 +154,7 @@ VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE *streamFile) {
ovmi.meta_type = meta_SQEX_SCD; ovmi.meta_type = meta_SQEX_SCD;
ovmi.total_subsongs = total_subsongs; ovmi.total_subsongs = total_subsongs;
ovmi.disable_reordering = 1; /* already ordered */
/* loop values are in bytes, let init_vgmstream_ogg_vorbis find loop comments instead */ /* loop values are in bytes, let init_vgmstream_ogg_vorbis find loop comments instead */
ogg_version = read_8bit(extradata_offset + 0x00, streamFile); ogg_version = read_8bit(extradata_offset + 0x00, streamFile);

View File

@ -70,6 +70,7 @@ typedef struct {
uint32_t skip_samples; uint32_t skip_samples;
uint32_t loop_flag; uint32_t loop_flag;
uint32_t loop_behavior;
int loop_flag_set; int loop_flag_set;
int loop_flag_auto; int loop_flag_auto;
@ -105,6 +106,11 @@ typedef struct {
int chunk_size_set; int chunk_size_set;
int chunk_count_set; int chunk_count_set;
uint32_t base_offset;
uint32_t name_values[16];
int name_values_count;
/* original STREAMFILE and its type (may be an unsupported "base" file or a .txth) */ /* original STREAMFILE and its type (may be an unsupported "base" file or a .txth) */
STREAMFILE *streamFile; STREAMFILE *streamFile;
int streamfile_is_txth; int streamfile_is_txth;
@ -565,6 +571,19 @@ static VGMSTREAM *init_subfile(txth_header * txth) {
//todo: other combos with subsongs + subfile? //todo: other combos with subsongs + subfile?
/* load some fields for possible calcs */
if (!txth->channels)
txth->channels = vgmstream->channels;
if (!txth->sample_rate)
txth->sample_rate = vgmstream->sample_rate;
if (!txth->interleave)
txth->interleave = vgmstream->interleave_block_size;
if (!txth->interleave_last)
txth->interleave_last = vgmstream->interleave_last_block_size;
//if (!txth->loop_flag) //?
// txth->loop_flag = vgmstream->loop_flag;
close_streamfile(streamSubfile); close_streamfile(streamSubfile);
return vgmstream; return vgmstream;
@ -695,8 +714,8 @@ static int parse_keyval(STREAMFILE * streamFile, txth_header * txth, const char
static int parse_num(STREAMFILE * streamFile, txth_header * txth, const char * val, uint32_t * out_value); static int parse_num(STREAMFILE * streamFile, txth_header * txth, const char * val, uint32_t * out_value);
static int parse_string(STREAMFILE * streamFile, txth_header * txth, const char * val, char * str); static int parse_string(STREAMFILE * streamFile, txth_header * txth, const char * val, char * str);
static int parse_coef_table(STREAMFILE * streamFile, txth_header * txth, const char * val, uint8_t * out_value, size_t out_size); static int parse_coef_table(STREAMFILE * streamFile, txth_header * txth, const char * val, uint8_t * out_value, size_t out_size);
static int parse_name_table(txth_header * txth, char * val);
static int is_string(const char * val, const char * cmp); static int is_string(const char * val, const char * cmp);
static int is_substring(const char * val, const char * cmp);
static int get_bytes_to_samples(txth_header * txth, uint32_t bytes); static int get_bytes_to_samples(txth_header * txth, uint32_t bytes);
static int get_padding_size(txth_header * txth, int discard_empty); static int get_padding_size(txth_header * txth, int discard_empty);
@ -979,11 +998,32 @@ static int parse_keyval(STREAMFILE * streamFile_, txth_header * txth, const char
else { else {
if (!parse_num(txth->streamHead,txth,val, &txth->loop_flag)) goto fail; if (!parse_num(txth->streamHead,txth,val, &txth->loop_flag)) goto fail;
txth->loop_flag_set = 1; txth->loop_flag_set = 1;
if (txth->loop_flag == 0xFFFF || txth->loop_flag == 0xFFFFFFFF) { /* normally -1 = no loop */
txth->loop_flag = 0; if (txth->loop_behavior == 0) {
if ((txth->loop_flag == 0xFFFF || txth->loop_flag == 0xFFFFFFFF) )
txth->loop_flag = 0;
}
else if (txth->loop_behavior == 1) {
if (txth->loop_flag == 0xFF || txth->loop_flag == 0xFFFF || txth->loop_flag == 0xFFFFFFFF)
txth->loop_flag = 1;
}
else if (txth->loop_behavior == 2) {
if (txth->loop_flag == 0xFF || txth->loop_flag == 0xFFFF || txth->loop_flag == 0xFFFFFFFF)
txth->loop_flag = 0;
} }
} }
} }
else if (is_string(key,"loop_behavior")) {
if (is_string(val, "default"))
txth->loop_behavior = 0;
else if (is_string(val, "negative"))
txth->loop_behavior = 1;
else if (is_string(val, "positive"))
txth->loop_behavior = 2;
else
goto fail;
}
/* COEFS */ /* COEFS */
else if (is_string(key,"coef_offset")) { else if (is_string(key,"coef_offset")) {
@ -1139,6 +1179,17 @@ static int parse_keyval(STREAMFILE * streamFile_, txth_header * txth, const char
set_body_chunk(txth); set_body_chunk(txth);
} }
/* BASE OFFSET */
else if (is_string(key,"base_offset")) {
if (!parse_num(txth->streamHead,txth,val, &txth->base_offset)) goto fail;
}
/* NAME TABLE */
else if (is_string(key,"name_table")) {
if (!parse_name_table(txth,val)) goto fail;
}
/* DEFAULT */ /* DEFAULT */
else { else {
VGM_LOG("TXTH: unknown key=%s, val=%s\n", key,val); VGM_LOG("TXTH: unknown key=%s, val=%s\n", key,val);
@ -1152,8 +1203,28 @@ fail:
return 0; return 0;
} }
static int is_substring(const char * val, const char * cmp, int inline_field) {
char chr;
int len = strlen(cmp);
/* "val" must contain "cmp" entirely */
if (strncmp(val, cmp, len) != 0)
return 0;
chr = val[len];
/* "val" can end with math for inline fields (like interleave*0x10) */
if (inline_field && (chr == '+' || chr == '-' || chr == '*' || chr == '/'))
return len;
/* otherwise "val" ends in space or eof (to tell "interleave" and "interleave_last" apart) */
if (chr != '\0' && chr != ' ')
return 0;
return len;
}
static int is_string(const char * val, const char * cmp) { static int is_string(const char * val, const char * cmp) {
int len = is_substring(val, cmp); int len = is_substring(val, cmp, 0);
if (!len) return 0; if (!len) return 0;
/* also test that after string there aren't other values /* also test that after string there aren't other values
@ -1166,19 +1237,8 @@ static int is_string(const char * val, const char * cmp) {
return len; return len;
} }
static int is_string_field(const char * val, const char * cmp) {
static int is_substring(const char * val, const char * cmp) { return is_substring(val, cmp, 1);
int len = strlen(cmp);
if (strncmp(val, cmp, len) != 0)
return 0;
/* string in val must be a full word (end with null or space) to
* avoid mistaking stuff like "interleave" with "interleave_last"
* (could also check , except when used for math */
if (val[len] != '\0' && val[len] != ' ')
return 0;
return len;
} }
static int parse_string(STREAMFILE * streamFile, txth_header * txth, const char * val, char * str) { static int parse_string(STREAMFILE * streamFile, txth_header * txth, const char * val, char * str) {
@ -1218,6 +1278,113 @@ fail:
return 0; return 0;
} }
static int parse_name_table(txth_header * txth, char * name_list) {
STREAMFILE *nameFile = NULL;
off_t txt_offset, file_size;
char filename[PATH_LIMIT];
char basename[PATH_LIMIT];
/* just in case */
if (txth->streamfile_is_txth || !txth->streamText || !txth->streamFile)
goto fail;
/* trim name_list just in case */
{
int name_list_len = strlen(name_list);
int i;
for (i = name_list_len - 1; i >= 0; i--) {
if (name_list[i] != ' ')
break;
name_list[i] = '\0';
}
}
//;VGM_LOG("TXTH: name_list2='%s'\n", name_list);
/* open companion file near .txth */
nameFile = open_streamfile_by_filename(txth->streamText, name_list);
if (!nameFile) goto fail;
get_streamfile_filename(txth->streamFile, filename, sizeof(filename));
get_streamfile_basename(txth->streamFile, basename, sizeof(basename));
//;VGM_LOG("TXTH: filename=%s, basename=%s\n", filename, basename);
txt_offset = 0x00;
file_size = get_streamfile_size(nameFile);
/* skip BOM if needed */
if ((uint16_t)read_16bitLE(0x00, nameFile) == 0xFFFE ||
(uint16_t)read_16bitLE(0x00, nameFile) == 0xFEFF) {
txt_offset = 0x02;
}
else if (((uint32_t)read_32bitBE(0x00, nameFile) & 0xFFFFFF00) == 0xEFBBBF00) {
txt_offset = 0x03;
}
/* in case of repeated name_lists */
memset(txth->name_values, 0, sizeof(txth->name_values));
txth->name_values_count = 0;
/* read lines and find target filename, format is (filename): value1, ... valueN */
while (txt_offset < file_size) {
char line[TXT_LINE_MAX] = {0};
char key[TXT_LINE_MAX] = {0}, val[TXT_LINE_MAX] = {0};
int ok, bytes_read, line_done;
bytes_read = get_streamfile_text_line(TXT_LINE_MAX,line, txt_offset,nameFile, &line_done);
if (!line_done) goto fail;
//;VGM_LOG("TXTH: line=%s\n",line);
txt_offset += bytes_read;
/* get key/val (ignores lead spaces, stops at space/comment/separator) */
ok = sscanf(line, " %[^ \t#:] : %[^\t#\r\n] ", key,val);
if (ok != 2) { /* ignore line if no key=val (comment or garbage) */
/* try again with " (empty): (val)) */
key[0] = '\0';
ok = sscanf(line, " : %[^\t#\r\n] ", val);
if (ok != 1)
continue;
}
//;VGM_LOG("TXTH: compare name '%s'\n", key);
/* parse values if key (name) matches default ("") or filename with/without extension */
if (key[0]=='\0' || strcmpi(key, filename)==0 || strcmpi(key, basename)==0) {
int n;
char subval[TXT_LINE_MAX];
const char *current = val;
while (current[0] != '\0') {
ok = sscanf(current, " %[^\t#\r\n,]%n ", subval, &n);
if (ok != 1)
goto fail;
current += n;
if (current[0] == ',')
current++;
if (!parse_num(txth->streamHead,txth,subval, &txth->name_values[txth->name_values_count])) goto fail;
txth->name_values_count++;
if (txth->name_values_count >= 16) /* surely nobody needs that many */
goto fail;
}
//;VGM_LOG("TXTH: found name '%s'\n", key);
break; /* target found */
}
}
/* ignore if name is not actually found (values will return 0) */
close_streamfile(nameFile);
return 1;
fail:
close_streamfile(nameFile);
return 0;
}
static int parse_num(STREAMFILE * streamFile, txth_header * txth, const char * val, uint32_t * out_value) { static int parse_num(STREAMFILE * streamFile, txth_header * txth, const char * val, uint32_t * out_value) {
/* out_value can be these, save before modifying */ /* out_value can be these, save before modifying */
uint32_t value_mul = txth->value_mul; uint32_t value_mul = txth->value_mul;
@ -1278,6 +1445,9 @@ static int parse_num(STREAMFILE * streamFile, txth_header * txth, const char * v
if (sscanf(val, hex ? "@%x%n" : "@%u%n", &offset, &n) != 1) goto fail; if (sscanf(val, hex ? "@%x%n" : "@%u%n", &offset, &n) != 1) goto fail;
} }
/* adjust offset */
offset += txth->base_offset;
if (/*offset < 0 ||*/ offset > get_streamfile_size(streamFile)) if (/*offset < 0 ||*/ offset > get_streamfile_size(streamFile))
goto fail; goto fail;
@ -1290,7 +1460,7 @@ static int parse_num(STREAMFILE * streamFile, txth_header * txth, const char * v
offset = offset + subsong_offset * (txth->target_subsong - 1); offset = offset + subsong_offset * (txth->target_subsong - 1);
switch(size) { switch(size) {
case 1: value = read_8bit(offset,streamFile); break; case 1: value = (uint8_t)read_8bit(offset,streamFile); break;
case 2: value = big_endian ? (uint16_t)read_16bitBE(offset,streamFile) : (uint16_t)read_16bitLE(offset,streamFile); break; case 2: value = big_endian ? (uint16_t)read_16bitBE(offset,streamFile) : (uint16_t)read_16bitLE(offset,streamFile); break;
case 3: value = (big_endian ? (uint32_t)read_32bitBE(offset,streamFile) : (uint32_t)read_32bitLE(offset,streamFile)) & 0x00FFFFFF; break; case 3: value = (big_endian ? (uint32_t)read_32bitBE(offset,streamFile) : (uint32_t)read_32bitLE(offset,streamFile)) & 0x00FFFFFF; break;
case 4: value = big_endian ? (uint32_t)read_32bitBE(offset,streamFile) : (uint32_t)read_32bitLE(offset,streamFile); break; case 4: value = big_endian ? (uint32_t)read_32bitBE(offset,streamFile) : (uint32_t)read_32bitLE(offset,streamFile); break;
@ -1306,17 +1476,35 @@ static int parse_num(STREAMFILE * streamFile, txth_header * txth, const char * v
value_read = 1; value_read = 1;
} }
else { /* known field */ else { /* known field */
if ((n = is_substring(val,"interleave"))) value = txth->interleave; if ((n = is_string_field(val,"interleave"))) value = txth->interleave;
else if ((n = is_substring(val,"interleave_last"))) value = txth->interleave_last; else if ((n = is_string_field(val,"interleave_last"))) value = txth->interleave_last;
else if ((n = is_substring(val,"channels"))) value = txth->channels; else if ((n = is_string_field(val,"channels"))) value = txth->channels;
else if ((n = is_substring(val,"sample_rate"))) value = txth->sample_rate; else if ((n = is_string_field(val,"sample_rate"))) value = txth->sample_rate;
else if ((n = is_substring(val,"start_offset"))) value = txth->start_offset; else if ((n = is_string_field(val,"start_offset"))) value = txth->start_offset;
else if ((n = is_substring(val,"data_size"))) value = txth->data_size; else if ((n = is_string_field(val,"data_size"))) value = txth->data_size;
else if ((n = is_substring(val,"num_samples"))) value = txth->num_samples; else if ((n = is_string_field(val,"num_samples"))) value = txth->num_samples;
else if ((n = is_substring(val,"loop_start_sample"))) value = txth->loop_start_sample; else if ((n = is_string_field(val,"loop_start_sample"))) value = txth->loop_start_sample;
else if ((n = is_substring(val,"loop_end_sample"))) value = txth->loop_end_sample; else if ((n = is_string_field(val,"loop_end_sample"))) value = txth->loop_end_sample;
else if ((n = is_substring(val,"subsong_count"))) value = txth->subsong_count; else if ((n = is_string_field(val,"subsong_count"))) value = txth->subsong_count;
else if ((n = is_substring(val,"subsong_offset"))) value = txth->subsong_offset; else if ((n = is_string_field(val,"subsong_offset"))) value = txth->subsong_offset;
//todo whatever, improve
else if ((n = is_string_field(val,"name_value"))) value = txth->name_values[0];
else if ((n = is_string_field(val,"name_value1"))) value = txth->name_values[0];
else if ((n = is_string_field(val,"name_value2"))) value = txth->name_values[1];
else if ((n = is_string_field(val,"name_value3"))) value = txth->name_values[2];
else if ((n = is_string_field(val,"name_value4"))) value = txth->name_values[3];
else if ((n = is_string_field(val,"name_value5"))) value = txth->name_values[4];
else if ((n = is_string_field(val,"name_value6"))) value = txth->name_values[5];
else if ((n = is_string_field(val,"name_value7"))) value = txth->name_values[6];
else if ((n = is_string_field(val,"name_value8"))) value = txth->name_values[7];
else if ((n = is_string_field(val,"name_value9"))) value = txth->name_values[8];
else if ((n = is_string_field(val,"name_value10"))) value = txth->name_values[9];
else if ((n = is_string_field(val,"name_value11"))) value = txth->name_values[10];
else if ((n = is_string_field(val,"name_value12"))) value = txth->name_values[11];
else if ((n = is_string_field(val,"name_value13"))) value = txth->name_values[12];
else if ((n = is_string_field(val,"name_value14"))) value = txth->name_values[13];
else if ((n = is_string_field(val,"name_value15"))) value = txth->name_values[14];
else if ((n = is_string_field(val,"name_value16"))) value = txth->name_values[15];
else goto fail; else goto fail;
value_read = 1; value_read = 1;
} }

View File

@ -160,11 +160,28 @@ VGMSTREAM * init_vgmstream_xvag(STREAMFILE *streamFile) {
if (xvag.layers > 1 && !(xvag.layers*1 == vgmstream->channels || xvag.layers*2 == vgmstream->channels)) goto fail; if (xvag.layers > 1 && !(xvag.layers*1 == vgmstream->channels || xvag.layers*2 == vgmstream->channels)) goto fail;
/* "mpin": mpeg info */ /* "mpin": mpeg info */
/* 0x00/04: mpeg version/layer? other: unknown or repeats of "fmat" */
if (!find_chunk(streamFile, 0x6D70696E,first_offset,0, &chunk_offset,NULL, xvag.big_endian, 1)) /*"mpin"*/ if (!find_chunk(streamFile, 0x6D70696E,first_offset,0, &chunk_offset,NULL, xvag.big_endian, 1)) /*"mpin"*/
goto fail; goto fail;
cfg.chunk_size = read_32bit(chunk_offset+0x1c,streamFile); /* fixed frame size */ /* all layers/subsongs share the same config; not very useful but for posterity:
* - 0x00: mpeg version
* - 0x04: mpeg layer
* - 0x08: bit rate
* - 0x0c: sample rate
* - 0x10: some version? (0x01-0x03)?
* - 0x14: channels per stream?
* - 0x18: channels per stream or total channels?
* - 0x1c: fixed frame size (always CBR)
* - 0x20: encoder delay (usually but not always 1201)
* - 0x24: number of samples
* - 0x28: some size?
* - 0x2c: ? (0x02)
* - 0x30: ? (0x00, 0x80)
* - 0x34: data size
* (rest is padding)
* */
cfg.chunk_size = read_32bit(chunk_offset+0x1c,streamFile);
cfg.skip_samples = read_32bit(chunk_offset+0x20,streamFile);
cfg.interleave = cfg.chunk_size * xvag.factor; cfg.interleave = cfg.chunk_size * xvag.factor;
vgmstream->codec_data = init_mpeg_custom(streamFile, start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_XVAG, &cfg); vgmstream->codec_data = init_mpeg_custom(streamFile, start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_XVAG, &cfg);

View File

@ -345,8 +345,10 @@ VGMSTREAM * init_vgmstream_xwb(STREAMFILE *streamFile) {
/* 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 && xwb.codec != ATRAC9_RIFF) { 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.stream_size > get_streamfile_size(streamFile))
goto fail; goto fail;
//if (xwb.data_offset + xwb.data_size > get_streamfile_size(streamFile)) /* badly split */
// goto fail;
} }

View File

@ -480,6 +480,7 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = {
init_vgmstream_xwma_konami, init_vgmstream_xwma_konami,
init_vgmstream_9tav, init_vgmstream_9tav,
init_vgmstream_fsb5_fev_bank, init_vgmstream_fsb5_fev_bank,
init_vgmstream_bwav,
/* 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

@ -742,6 +742,7 @@ typedef enum {
meta_MSF_KONAMI, meta_MSF_KONAMI,
meta_XWMA_KONAMI, meta_XWMA_KONAMI,
meta_9TAV, meta_9TAV,
meta_BWAV,
} meta_t; } meta_t;
@ -1042,6 +1043,7 @@ typedef struct {
int interleave; /* size of stream interleave */ int interleave; /* size of stream interleave */
int encryption; /* encryption mode */ int encryption; /* encryption mode */
int big_endian; int big_endian;
int skip_samples;
/* for AHX */ /* for AHX */
int cri_type; int cri_type;
uint16_t cri_key1; uint16_t cri_key1;