Updated VGMStream to r1050-1335-g25479155.

CQTexperiment
Christopher Snowhill 2018-06-03 18:51:00 -07:00
parent 3901305105
commit a767189657
29 changed files with 950 additions and 352 deletions

View File

@ -97,6 +97,8 @@
83345A501F8AEB2800B2EAA4 /* pc_al2.c in Sources */ = {isa = PBXBuildFile; fileRef = 83345A4C1F8AEB2700B2EAA4 /* pc_al2.c */; };
83345A521F8AEB2800B2EAA4 /* xvag.c in Sources */ = {isa = PBXBuildFile; fileRef = 83345A4E1F8AEB2800B2EAA4 /* xvag.c */; };
833A7A2E1ED11961003EC53E /* xau.c in Sources */ = {isa = PBXBuildFile; fileRef = 833A7A2D1ED11961003EC53E /* xau.c */; };
8342469420C4D23000926E48 /* h4m.c in Sources */ = {isa = PBXBuildFile; fileRef = 8342469020C4D22F00926E48 /* h4m.c */; };
8342469620C4D23D00926E48 /* blocked_h4m.c in Sources */ = {isa = PBXBuildFile; fileRef = 8342469520C4D23D00926E48 /* blocked_h4m.c */; };
8349A8DF1FE6251F00E26435 /* vorbis_custom_utils_vid1.c in Sources */ = {isa = PBXBuildFile; fileRef = 8349A8DC1FE6251E00E26435 /* vorbis_custom_utils_vid1.c */; };
8349A8E11FE6251F00E26435 /* ea_mt_decoder.c in Sources */ = {isa = PBXBuildFile; fileRef = 8349A8DE1FE6251F00E26435 /* ea_mt_decoder.c */; };
8349A8E81FE6253900E26435 /* blocked_dec.c in Sources */ = {isa = PBXBuildFile; fileRef = 8349A8E21FE6253800E26435 /* blocked_dec.c */; };
@ -678,6 +680,8 @@
83345A4C1F8AEB2700B2EAA4 /* pc_al2.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = pc_al2.c; sourceTree = "<group>"; };
83345A4E1F8AEB2800B2EAA4 /* xvag.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xvag.c; sourceTree = "<group>"; };
833A7A2D1ED11961003EC53E /* xau.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xau.c; sourceTree = "<group>"; };
8342469020C4D22F00926E48 /* h4m.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = h4m.c; sourceTree = "<group>"; };
8342469520C4D23D00926E48 /* blocked_h4m.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = blocked_h4m.c; sourceTree = "<group>"; };
8349A8DC1FE6251E00E26435 /* vorbis_custom_utils_vid1.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = vorbis_custom_utils_vid1.c; sourceTree = "<group>"; };
8349A8DE1FE6251F00E26435 /* ea_mt_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ea_mt_decoder.c; sourceTree = "<group>"; };
8349A8E21FE6253800E26435 /* blocked_dec.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = blocked_dec.c; sourceTree = "<group>"; };
@ -1276,6 +1280,7 @@
8306B09A20984550000302D4 /* blocked_emff.c */,
8306B0932098454F000302D4 /* blocked_filp.c */,
8306B0A020984551000302D4 /* blocked_gsb.c */,
8342469520C4D23D00926E48 /* blocked_h4m.c */,
8306B0962098454F000302D4 /* blocked_halpst.c */,
8306B09B20984550000302D4 /* blocked_hwas.c */,
8349A8E51FE6253800E26435 /* blocked_ivaud.c */,
@ -1379,6 +1384,7 @@
836F6E4F18BDC2180095E648 /* genh.c */,
836F6E5118BDC2180095E648 /* gsp_gsb.c */,
83709DFF1ECBC1A4005C03D3 /* gtd.c */,
8342469020C4D22F00926E48 /* h4m.c */,
836F6E5218BDC2180095E648 /* halpst.c */,
83AA5D211F6E2F9C0020821C /* hca_keys.h */,
8323894F1D2246C300482226 /* hca.c */,
@ -2229,6 +2235,7 @@
8306B0B520984552000302D4 /* blocked_emff.c in Sources */,
836F6FBD18BDC2190095E648 /* nwa.c in Sources */,
83A21F8C201D8982000F04B9 /* kma9.c in Sources */,
8342469420C4D23000926E48 /* h4m.c in Sources */,
836F6FC418BDC2190095E648 /* pc_snds.c in Sources */,
836F704E18BDC2190095E648 /* xss.c in Sources */,
836F6FD118BDC2190095E648 /* ps2_bg00.c in Sources */,
@ -2257,6 +2264,7 @@
836F700518BDC2190095E648 /* ps2_vbk.c in Sources */,
836F6FDF18BDC2190095E648 /* ps2_int.c in Sources */,
8306B0AC20984552000302D4 /* blocked_xa.c in Sources */,
8342469620C4D23D00926E48 /* blocked_h4m.c in Sources */,
836F6F7B18BDC2190095E648 /* dc_idvi.c in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;

View File

@ -190,7 +190,7 @@ int ffmpeg_make_riff_xma1(uint8_t * buf, size_t buf_size, size_t sample_count, s
put_32bitLE(buf+off+0x08, 0); /* loop start */
put_32bitLE(buf+off+0x0c, 0); /* loop end */
put_8bit (buf+off+0x10, 0); /* loop subframe */
put_8bit (buf+off+0x11, channels);
put_8bit (buf+off+0x11, stream_channels);
put_16bitLE(buf+off+0x12, speakers);
}

View File

@ -146,7 +146,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 < 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 */

View File

@ -28,9 +28,11 @@ static const char* extension_list[] = {
"afc",
"agsc",
"ahx",
"aifc",
"aifcl",
//"aif", //common
"aifc", //common?
"aifcl", //fake extension, for AIF???
//"aiff", //common
"aiffl", //fake extension, for AIF???
"aix",
"akb",
"al2",
@ -135,7 +137,9 @@ static const char* extension_list[] = {
"gms",
"gsb",
"gtd",
"gwm",
"h4m",
"hca",
"hgc1",
"his",
@ -258,6 +262,7 @@ static const char* extension_list[] = {
"rak",
"ras",
"raw",
"rda", //FFmpeg/reserved [Rhythm Destruction (PC)]
"rkv",
"rnd",
"rof",
@ -274,6 +279,7 @@ static const char* extension_list[] = {
"rwsd",
"rwx",
"rxw",
"rxx", //txth/reserved [Full Auto (X360)]
"s14",
"sab",
@ -292,7 +298,7 @@ static const char* extension_list[] = {
"scd",
"sck",
"sd9",
"sdf",
"sdf", //txth/reserved [Gummy Bears Mini Golf (3DS), Agent Hugo - Lemoon Twist (PS2)]
"sdt",
"seg",
"sf0",
@ -618,6 +624,7 @@ static const layout_info layout_info_list[] = {
{layout_blocked_ea_wve_au00, "blocked (EA WVE au00)"},
{layout_blocked_ea_wve_ad10, "blocked (EA WVE Ad10)"},
{layout_blocked_sthd, "blocked (STHD)"},
{layout_blocked_h4m, "blocked (H4M)"},
};
static const meta_info meta_info_list[] = {
@ -684,8 +691,8 @@ static const meta_info meta_info_list[] = {
{meta_SADL, "Procyon Studio SADL header"},
{meta_PS2_BMDX, "Beatmania .bmdx header"},
{meta_DSP_WSI, "Alone in the Dark .WSI header"},
{meta_AIFC, "Audio Interchange File Format AIFF-C"},
{meta_AIFF, "Audio Interchange File Format"},
{meta_AIFC, "Apple AIFF-C (Audio Interchange File Format) header"},
{meta_AIFF, "Apple AIFF (Audio Interchange File Format) header"},
{meta_STR_SNDS, ".str SNDS SHDR chunk"},
{meta_WS_AUD, "Westwood Studios .aud header"},
{meta_WS_AUD_old, "Westwood Studios .aud (old) header"},
@ -795,7 +802,7 @@ static const meta_info meta_info_list[] = {
{meta_YDSP, "Yuke's DSP (YDSP) Header"},
{meta_MSVP, "MSVP Header"},
{meta_NGC_SSM, "SSM DSP Header"},
{meta_PS2_JOE, "Disney/Pixar JOE Header"},
{meta_PS2_JOE, "Asobo Studio .JOE header"},
{meta_VGS, "Guitar Hero VGS Header"},
{meta_DC_DCSW_DCS, "Evil Twin DCS file with helper"},
{meta_WII_SMP, "SMP DSP Header"},
@ -1012,6 +1019,10 @@ static const meta_info meta_info_list[] = {
{meta_OPUS_PPP, "AT9 OPUS header"},
{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)"},
{meta_DSP_SADF, "Procyon Studio SADF header"},
{meta_H4M, "Hudson HVQM4 header"},
#ifdef VGM_USE_FFMPEG
{meta_FFmpeg, "FFmpeg supported file format"},

View File

@ -202,6 +202,9 @@ static void block_update(VGMSTREAM * vgmstream) {
case layout_blocked_sthd:
block_update_sthd(vgmstream->next_block_offset,vgmstream);
break;
case layout_blocked_h4m:
block_update_h4m(vgmstream->next_block_offset,vgmstream);
break;
default:
break;
}

View File

@ -82,7 +82,7 @@ void block_update_ea_swvr(off_t block_offset, VGMSTREAM * vgmstream) {
break;
default: /* ignore, 0 samples */
VGM_LOG("EA SWVR: ignored 0x%08x at 0x%lx\n", block_id, block_offset);
//;VGM_LOG("EA SWVR: ignored 0x%08x at 0x%lx\n", block_id, block_offset);
break;
}

View File

@ -0,0 +1,102 @@
#include "layout.h"
#include "../coding/coding.h"
/* H4M video blocks with audio frames, based on h4m_audio_decode */
void block_update_h4m(off_t block_offset, VGMSTREAM * vgmstream) {
STREAMFILE* streamFile = vgmstream->ch[0].streamfile;
int i;
size_t block_size, block_samples;
/* use full_block_size as counter (a bit hacky but whatevs) */
if (vgmstream->full_block_size <= 0) {
/* new full block */
/* 0x00: last_full_block_size */
uint32_t full_block_size = read_32bitBE(block_offset+0x04, streamFile);
/* 0x08: vid_frame_count */
/* 0x0c: aud_frame_count */
/* 0x10: block_header_unk (0x01000000, except 0 in a couple of Bomberman Jetters files) */
vgmstream->full_block_size = full_block_size; /* not including 0x14 block header */
block_size = 0x14; /* skip header and point to first frame in full block */
block_samples = 0; /* signal new block_update_h4m */
}
else {
/* new audio or video frames in the current full block */
uint16_t frame_type = read_16bitBE(block_offset+0x00, streamFile);
uint16_t frame_format = read_16bitBE(block_offset+0x02, streamFile);
uint32_t frame_size = read_32bitBE(block_offset+0x04, streamFile); /* not including 0x08 frame header */
if (frame_type == 0x00) {
/* HVQM4_AUDIO (there are more checks with frame_format but not too relevant for vgmstream) */
uint32_t frame_samples = read_32bitBE(block_offset+0x08, streamFile);
size_t block_skip;
if (vgmstream->codec_version & 0x80) {
frame_samples /= 2; /* ??? */
}
block_skip = 0x08 + 0x04;
block_size = 0x08 + frame_size;
block_samples = frame_samples;
/* skip data from other audio tracks */
if (vgmstream->num_streams) {
uint32_t audio_bytes = frame_size - 0x04;
block_skip += (audio_bytes / vgmstream->num_streams) * vgmstream->stream_index;
}
//VGM_ASSERT(frame_format < 1 && frame_format > 3, "H4M: unknown frame_format %x at %lx\n", frame_format, block_offset);
VGM_ASSERT(frame_format == 1, "H4M: unknown frame_format %x at %lx\n", frame_format, block_offset);
//todo handle in the decoder?
//todo right channel first?
/* get ADPCM hist (usually every new block) */
for (i = 0; i < vgmstream->channels; i++) {
if (frame_format == 1) { /* combined hist+index */
vgmstream->ch[i].adpcm_history1_32 = read_16bitBE(block_offset + block_skip + 0x02*i + 0x00,streamFile) & 0xFFFFFF80;
vgmstream->ch[i].adpcm_step_index = read_8bit(block_offset + block_skip + 0x02*i + 0x01,streamFile) & 0x7f;
vgmstream->ch[i].offset = block_offset + block_skip + 0x02*vgmstream->channels;
}
else if (frame_format == 3) { /* separate hist+index */
vgmstream->ch[i].adpcm_history1_32 = read_16bitBE(block_offset + block_skip + 0x03*i + 0x00,streamFile);
vgmstream->ch[i].adpcm_step_index = read_8bit(block_offset + block_skip + 0x03*i + 0x02,streamFile);
vgmstream->ch[i].offset = block_offset + block_skip + 0x03*vgmstream->channels;
}
else if (frame_format == 2) { /* no hist/index */
vgmstream->ch[i].offset = block_offset + block_skip;
}
}
//todo temp hack, at it must write header sample and ignore the last nibble to get fully correct output
if (frame_format == 1 || frame_format == 3) {
block_samples--;
}
}
else {
block_size = 0x08 + frame_size;
block_samples = 0; /* signal new block_update_h4m */
}
vgmstream->full_block_size -= block_size;
}
/* EOF check, there is some footer/garbage at the end */
if (block_offset == get_streamfile_size(streamFile)
|| block_offset + block_size > get_streamfile_size(streamFile)) {
//block_samples = -1; /* signal end block */
vgmstream->full_block_size = 0;
vgmstream->current_block_samples = 0;
vgmstream->current_block_offset = get_streamfile_size(streamFile);
vgmstream->next_block_offset = get_streamfile_size(streamFile);
return;
}
vgmstream->current_block_samples = block_samples;
vgmstream->current_block_offset = block_offset;
vgmstream->next_block_offset = block_offset + block_size;
}

View File

@ -43,6 +43,7 @@ void block_update_xvag_subsong(off_t block_offset, VGMSTREAM * vgmstream);
void block_update_ea_wve_au00(off_t block_offset, VGMSTREAM * vgmstream);
void block_update_ea_wve_ad10(off_t block_offset, VGMSTREAM * vgmstream);
void block_update_sthd(off_t block_offset, VGMSTREAM * vgmstream);
void block_update_h4m(off_t block_offset, VGMSTREAM * vgmstream);
/* other layouts */
void render_vgmstream_interleave(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstream);

View File

@ -1,11 +1,6 @@
#include "meta.h"
#include "../layout/layout.h"
#include "../util.h"
/* Audio Interchange File Format AIFF-C */
/* also plain AIFF, for good measure */
/* Included primarily for 3DO */
/* for reading integers inexplicably packed into 80 bit floats */
static uint32_t read80bitSANE(off_t offset, STREAMFILE *streamFile) {
@ -53,9 +48,10 @@ static uint32_t find_marker(STREAMFILE *streamFile, off_t MarkerChunkOffset,
return -1;
}
/* Audio Interchange File Format AIFF/AIFF-C - from Mac/3DO games */
VGMSTREAM * init_vgmstream_aifc(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
char filename[PATH_LIMIT];
off_t file_size = -1;
int channel_count = 0;
@ -82,22 +78,23 @@ VGMSTREAM * init_vgmstream_aifc(STREAMFILE *streamFile) {
int InstrumentChunkFound =0;
off_t InstrumentChunkOffset = -1;
/* check extension, case insensitive */
streamFile->get_name(streamFile,filename,sizeof(filename));
if (!strcasecmp("aifc",filename_extension(filename)) ||
!strcasecmp("afc",filename_extension(filename)) ||
!strcasecmp("aifcl",filename_extension(filename)) ||
!strcasecmp("cbd2",filename_extension(filename)))
{
/* checks */
/* .aif: common (AIFF or AIFC), .aiff: common AIFF, .aifc: common AIFC
* .cbd2: M2 games, .bgm: Super Street Fighter II Turbo (3DO), aifcl/aiffl: for plugins? */
if (check_extensions(streamFile, "aif")) {
AIFCext = 1;
}
else if (!strcasecmp("aiff",filename_extension(filename)) ||
!strcasecmp("aif",filename_extension(filename)) ||
!strcasecmp("aiffl",filename_extension(filename)))
{
AIFFext = 1;
}
else goto fail;
else if (check_extensions(streamFile, "aifc,aifcl,afc,cbd2,bgm")) {
AIFCext = 1;
}
else if (check_extensions(streamFile, "aiff,aiffl")) {
AIFFext = 1;
}
else {
goto fail;
}
/* check header */
if ((uint32_t)read_32bitBE(0,streamFile)==0x464F524D && /* "FORM" */
@ -115,13 +112,16 @@ VGMSTREAM * init_vgmstream_aifc(STREAMFILE *streamFile) {
AIFF = 1;
}
else goto fail;
} else goto fail;
}
else {
goto fail;
}
file_size = get_streamfile_size(streamFile);
/* read through chunks to verify format and find metadata */
{
off_t current_chunk = 0xc; /* start with first chunk within FORM */
off_t current_chunk = 0x0c; /* start with first chunk within FORM */
while (current_chunk < file_size) {
uint32_t chunk_type = read_32bitBE(current_chunk,streamFile);
@ -134,54 +134,55 @@ VGMSTREAM * init_vgmstream_aifc(STREAMFILE *streamFile) {
if (current_chunk+8+chunk_size > file_size) goto fail;
switch(chunk_type) {
case 0x46564552: /* FVER */
/* only one per file */
case 0x46564552: /* "FVER" (version info) */
if (FormatVersionChunkFound) goto fail;
/* plain AIFF shouldn't have */
if (AIFF) goto fail;
if (AIFF) goto fail; /* plain AIFF shouldn't have */
FormatVersionChunkFound = 1;
/* specific size */
if (chunk_size != 4) goto fail;
/* Version 1 of AIFF-C spec timestamp */
if ((uint32_t)read_32bitBE(current_chunk+8,streamFile) !=
0xA2805140) goto fail;
if ((uint32_t)read_32bitBE(current_chunk+8,streamFile) != 0xA2805140) goto fail;
break;
case 0x434F4D4D: /* COMM */
/* only one per file */
case 0x434F4D4D: /* "COMM" (main header) */
if (CommonChunkFound) goto fail;
CommonChunkFound = 1;
channel_count = read_16bitBE(current_chunk+8,streamFile);
if (channel_count <= 0) goto fail;
sample_count = (uint32_t)read_32bitBE(current_chunk+0xa,streamFile);
sample_size = read_16bitBE(current_chunk+0xe,streamFile);
sample_count = (uint32_t)read_32bitBE(current_chunk+0x0a,streamFile); /* number of blocks, actually */
sample_size = read_16bitBE(current_chunk+0x0e,streamFile);
sample_rate = read80bitSANE(current_chunk+0x10,streamFile);
if (AIFC) {
switch (read_32bitBE(current_chunk+0x1a,streamFile)) {
case 0x53445832: /* SDX2 */
case 0x53445832: /* "SDX2" [3DO games: Super Street Fighter II Turbo (3DO), etc] */
coding_type = coding_SDX2;
interleave = 1;
interleave = 0x01;
break;
case 0x43424432: /* CBD2 */
case 0x43424432: /* "CBD2" [M2 (arcade 3DO) games: IMSA Racing (M2), etc] */
coding_type = coding_CBD2;
interleave = 1;
interleave = 0x01;
break;
case 0x41445034: /* ADP4 */
case 0x41445034: /* "ADP4" */
coding_type = coding_DVI_IMA_int;
/* don't know how stereo DVI is laid out */
if (channel_count != 1) break;
if (channel_count != 1) break; /* don't know how stereo DVI is laid out */
break;
case 0x696D6134: /* "ima4" [Alida (PC) Lunar SSS (iOS)] */
coding_type = coding_APPLE_IMA4;
interleave = 0x22;
sample_count = sample_count * ((interleave-0x2)*2);
break;
default:
/* we should probably support uncompressed here */
VGM_LOG("AIFC: unknown codec\n");
goto fail;
}
} else if (AIFF) {
/* string size and human-readable AIFF-C codec follows */
}
else if (AIFF) {
switch (sample_size) {
case 8:
coding_type = coding_PCM8;
@ -191,39 +192,40 @@ VGMSTREAM * init_vgmstream_aifc(STREAMFILE *streamFile) {
coding_type = coding_PCM16BE;
interleave = 2;
break;
/* 32 is a possibility, but we don't see it and I
* don't have a reader for it yet */
default:
VGM_LOG("AIFF: unknown codec\n");
goto fail;
}
}
/* we don't check the human-readable portion of AIFF-C*/
break;
case 0x53534E44: /* SSND */
/* at most one per file */
case 0x53534E44: /* "SSND" (main data) */
if (SoundDataChunkFound) goto fail;
SoundDataChunkFound = 1;
start_offset = current_chunk + 16 + read_32bitBE(current_chunk+8,streamFile);
break;
case 0x4D41524B: /* MARK */
case 0x4D41524B: /* "MARK" (loops) */
if (MarkerChunkFound) goto fail;
MarkerChunkFound = 1;
MarkerChunkOffset = current_chunk;
break;
case 0x494E5354: /* INST */
case 0x494E5354: /* "INST" (loops) */
if (InstrumentChunkFound) goto fail;
InstrumentChunkFound = 1;
InstrumentChunkOffset = current_chunk;
break;
default:
/* spec says we can skip unrecognized chunks */
break;
}
current_chunk += 8+chunk_size;
current_chunk += 0x08+chunk_size;
}
}
@ -235,6 +237,7 @@ VGMSTREAM * init_vgmstream_aifc(STREAMFILE *streamFile) {
goto fail;
}
/* read loop points */
if (InstrumentChunkFound && MarkerChunkFound) {
int start_marker;
@ -262,49 +265,31 @@ VGMSTREAM * init_vgmstream_aifc(STREAMFILE *streamFile) {
}
}
/* build the VGMSTREAM */
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail;
/* fill in the vital statistics */
vgmstream->num_samples = sample_count;
vgmstream->sample_rate = sample_rate;
vgmstream->coding_type = coding_type;
if (channel_count > 1)
vgmstream->layout_type = layout_interleave;
else
vgmstream->layout_type = layout_none;
vgmstream->interleave_block_size = interleave;
vgmstream->num_samples = sample_count;
vgmstream->loop_start_sample = loop_start;
vgmstream->loop_end_sample = loop_end;
vgmstream->coding_type = coding_type;
vgmstream->layout_type = (channel_count > 1) ? layout_interleave : layout_none;
vgmstream->interleave_block_size = interleave;
if (AIFC)
vgmstream->meta_type = meta_AIFC;
else if (AIFF)
vgmstream->meta_type = meta_AIFF;
/* open the file, set up each channel */
{
int i;
vgmstream->ch[0].streamfile = streamFile->open(streamFile,filename,
STREAMFILE_DEFAULT_BUFFER_SIZE);
if (!vgmstream->ch[0].streamfile) goto fail;
for (i=0;i<channel_count;i++) {
vgmstream->ch[i].streamfile = vgmstream->ch[0].streamfile;
vgmstream->ch[i].offset = vgmstream->ch[i].channel_start_offset =
start_offset+i*interleave;
}
}
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
goto fail;
return vgmstream;
/* clean up anything we may have opened */
fail:
if (vgmstream) close_vgmstream(vgmstream);
close_vgmstream(vgmstream);
return NULL;
}

View File

@ -55,6 +55,9 @@ VGMSTREAM * init_vgmstream_ea_swvr(STREAMFILE *streamFile) {
else if (read_32bit(start_offset+0x10, streamFile) == 0x53484452) /* "SHDR" (Future Cop PC) */
start_offset += read_32bit(start_offset+0x04, streamFile);
if (read_32bit(start_offset+0x00, streamFile) == 0x46494C4C) /* "FILL" (Freekstyle) */
start_offset += read_32bit(start_offset+0x04, streamFile);
total_subsongs = 1;
block_id = read_32bit(start_offset, streamFile);
@ -65,7 +68,7 @@ VGMSTREAM * init_vgmstream_ea_swvr(STREAMFILE *streamFile) {
coding = coding_PSX;
if (read_16bit(start_offset+0x1a, streamFile) == 0x0024) {
total_subsongs = read_32bit(start_offset+0x0c, streamFile)+1;
sample_rate = 24000;
sample_rate = 22050;
}
else {
sample_rate = 14008;
@ -75,7 +78,7 @@ VGMSTREAM * init_vgmstream_ea_swvr(STREAMFILE *streamFile) {
case 0x56414742: /* "VAGB" */
coding = coding_PSX;
if (read_16bit(start_offset+0x1a, streamFile) == 0x6400) {
sample_rate = 24000;
sample_rate = 22050;
}
else {
sample_rate = 14008;
@ -85,13 +88,13 @@ VGMSTREAM * init_vgmstream_ea_swvr(STREAMFILE *streamFile) {
case 0x4453504D: /* "DSPM" */
coding = coding_NGC_DSP;
total_subsongs = read_32bit(start_offset+0x0c, streamFile)+1;
sample_rate = 24000;
sample_rate = 22050;
channel_count = 2;
break;
case 0x44535042: /* "DSPB" */
coding = coding_NGC_DSP;
channel_count = 1;
sample_rate = 24000;
sample_rate = 22050;
break;
case 0x4D534943: /* "MSIC" */
coding = coding_PCM8_U_int;

View File

@ -0,0 +1,105 @@
#include "meta.h"
#include "../layout/layout.h"
#include "../coding/coding.h"
/* H4M - from Hudson HVQM4 videos [Resident Evil 0 (GC), Tales of Symphonia (GC)]
* (info from hcs/Nisto's h4m_audio_decode) */
VGMSTREAM * init_vgmstream_h4m(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
off_t start_offset;
int loop_flag, channel_count;
int format, extra_tracks, sample_rate;
int total_subsongs, target_subsong = streamFile->stream_index;
/* checks */
if (!check_extensions(streamFile, "h4m"))
goto fail;
if (read_32bitBE(0x00,streamFile) != 0x4856514D && /* "HVQM" */
read_32bitBE(0x04,streamFile) != 0x3420312E) /* "4 1." */
goto fail;
if (read_32bitBE(0x08,streamFile) != 0x33000000 && /* "3\0\0\0" */
read_32bitBE(0x08,streamFile) != 0x35000000) /* "5\0\0\0" */
goto fail;
/* header */
start_offset = read_32bitBE(0x10, streamFile); /* header_size */
if (start_offset != 0x44) /* known size */
goto fail;
if (read_32bitBE(0x14, streamFile) != get_streamfile_size(streamFile) - start_offset) /* body_size */
goto fail;
if (read_32bitBE(0x18, streamFile) == 0) /* blocks */
goto fail;
/* 0x1c: video_frames */
if (read_32bitBE(0x20, streamFile) == 0) /* audio_frames */
goto fail;
/* 0x24: frame interval */
/* 0x28: max_video_frame_size */
/* 0x2c: unk2C (0) */
if (read_32bitBE(0x30, streamFile) == 0) /* max_audio_frame_size */
goto fail;
/* 0x34: hres */
/* 0x36: vres */
/* 0x38: h_srate */
/* 0x39: v_srate */
/* 0x3a: unk3A (0 or 0x12) */
/* 0x3b: unk3B (0) */
channel_count = read_8bit(0x3c,streamFile);
if (read_8bit(0x3d,streamFile) != 16) /* bitdepth */ //todo Pikmin not working
goto fail;
format = read_8bit(0x3e,streamFile); /* flags? */
extra_tracks = read_8bit(0x3f,streamFile);
sample_rate = read_32bitBE(0x40,streamFile);
loop_flag = 0;
total_subsongs = extra_tracks + 1; /* tracks for languages [Pokemon Channel], or sometimes used to fake multichannel [Tales of Symphonia] */
if (target_subsong == 0) target_subsong = 1;
if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count, loop_flag);
if (!vgmstream) goto fail;
vgmstream->sample_rate = sample_rate;
vgmstream->num_streams = total_subsongs;
vgmstream->stream_size = get_streamfile_size(streamFile) / total_subsongs; /* approx... */
vgmstream->codec_version = format; /* for blocks */
vgmstream->meta_type = meta_H4M;
vgmstream->layout_type = layout_blocked_h4m;
switch(format & 0x7F) {
case 0x00:
vgmstream->coding_type = coding_DVI_IMA; //todo H4M_IMA
break;
/* no games known to use this, h4m_audio_decode may decode them */
case 0x01: /* Uncompressed PCM */
case 0x04: /* 8-bit (A)DPCM */
default:
VGM_LOG("H4M: unknown codec %x\n", format);
goto fail;
}
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
goto fail;
/* calc num_samples manually */
{
vgmstream->next_block_offset = start_offset;
do {
block_update_h4m(vgmstream->next_block_offset,vgmstream);
vgmstream->num_samples += vgmstream->current_block_samples;
}
while (vgmstream->next_block_offset < get_streamfile_size(streamFile));
}
block_update_h4m(start_offset, vgmstream);
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;
}

View File

@ -231,6 +231,12 @@ static const hcakey_info hcakey_list[] = {
// Oira (Cygames) [iOS/Android]
{46460622}, // 0000000002C4EECE
// Dragon Ball Legends (Bandai Namco) [iOS/Android]
{7335633962698440504}, // 65CD683924EE7F38
// Princess Connect Re:Dive (iOS/Android/PC)
{3201512}, // 000000000030D9E8
};
#endif/*_HCA_KEYS_H_*/

View File

@ -9,6 +9,7 @@ VGMSTREAM * init_vgmstream_ktss(STREAMFILE *streamFile) {
int8_t version;
int32_t loop_length, coef_start_offset, coef_spacing;
off_t start_offset;
int8_t channelMultiplier;
if (!check_extensions(streamFile, "kns,ktss"))
goto fail;
@ -31,7 +32,15 @@ VGMSTREAM * init_vgmstream_ktss(STREAMFILE *streamFile) {
loop_length = read_32bitLE(0x38, streamFile);
loop_flag = loop_length > 0;
channel_count = read_8bit(0x29, streamFile);
// For unknown reasons, a channel multiplier is necessary in Hyrule Warriors (Switch)
// It seems to be present in other Koei Tecmo KNS but the channel count was always
// explicitly defined in the 0x29 byte. Here, 10 channel files have '2' in 0x29*
// and '5' in 0x28 whereas previous titles usually contained '1'
// This is super meh on KT's part but whatever
channelMultiplier = read_8bit(0x28, streamFile);
channel_count = read_8bit(0x29, streamFile) * channelMultiplier;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count, loop_flag);

View File

@ -647,6 +647,7 @@ VGMSTREAM * init_vgmstream_ta_aac_x360(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_ta_aac_ps3(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_ta_aac_mobile(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_ta_aac_mobile_vorbis(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_ta_aac_vita(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_ps3_mta2(STREAMFILE *streamFile);
@ -673,6 +674,7 @@ VGMSTREAM * init_vgmstream_opus_std(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_opus_n1(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_opus_capcom(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_opus_nop(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_opus_shinen(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_pc_al2(STREAMFILE * streamFile);
@ -750,4 +752,9 @@ VGMSTREAM * init_vgmstream_opus_ppp(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_ubi_bao_pk(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_dsp_switch_audio(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_dsp_sadf(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_h4m(STREAMFILE *streamFile);
#endif /*_META_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;
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 */
@ -688,6 +695,49 @@ fail:
return NULL;
}
/* sadf - Procyon Studio Header Variant [Xenoblade Chronicles 2 (Switch)] (sfx) */
VGMSTREAM * init_vgmstream_dsp_sadf(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
int channel_count, loop_flag;
off_t start_offset;
/* checks */
if (!check_extensions(streamFile, "sad"))
goto fail;
if (read_32bitBE(0x00, streamFile) != 0x73616466) /* "sadf" */
goto fail;
channel_count = read_8bit(0x18, streamFile);
loop_flag = read_8bit(0x19, streamFile);
start_offset = read_32bitLE(0x1C, streamFile);
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count, loop_flag);
if (!vgmstream) goto fail;
vgmstream->num_samples = read_32bitLE(0x28, streamFile);
vgmstream->sample_rate = read_32bitLE(0x24, streamFile);
if (loop_flag) {
vgmstream->loop_start_sample = read_32bitLE(0x2c, streamFile);
vgmstream->loop_end_sample = read_32bitLE(0x30, streamFile);
}
vgmstream->coding_type = coding_NGC_DSP;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = channel_count == 1 ? 0x8 :
read_32bitLE(0x20, streamFile) / channel_count;
vgmstream->meta_type = meta_DSP_SADF;
dsp_read_coefs_le(vgmstream, streamFile, 0x80, 0x80);
if (!vgmstream_open_stream(vgmstream, streamFile, start_offset))
goto fail;
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;
}
#define WSI_MAX_CHANNELS 2
/* .wsi - blocked dsp [Alone in the Dark (Wii)] */
VGMSTREAM * init_vgmstream_wsi(STREAMFILE *streamFile) {

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

@ -88,6 +88,7 @@ fail:
/* standard Switch Opus, Nintendo header + raw data (generated by opus_test.c?) [Lego City Undercover (Switch)] */
VGMSTREAM * init_vgmstream_opus_std(STREAMFILE *streamFile) {
STREAMFILE * PSIFile = NULL;
off_t offset = 0;
int num_samples = 0, loop_start = 0, loop_end = 0;
@ -95,10 +96,23 @@ VGMSTREAM * init_vgmstream_opus_std(STREAMFILE *streamFile) {
if (!check_extensions(streamFile,"opus,lopus"))
goto fail;
offset = 0x00;
num_samples = 0;
loop_start = 0;
loop_end = 0;
/* BlazBlue: Cross Tag Battle (Switch) PSI Metadata for corresponding Opus */
/* Maybe future Arc System Works games will use this too? */
PSIFile = open_streamfile_by_ext(streamFile, "psi");
offset = 0x00;
if (PSIFile){
num_samples = read_32bitLE(0x8C, PSIFile);
loop_start = read_32bitLE(0x84, PSIFile);
loop_end = read_32bitLE(0x88, PSIFile);
close_streamfile(PSIFile);
}
else {
num_samples = 0;
loop_start = 0;
loop_end = 0;
}
return init_vgmstream_opus(streamFile, meta_OPUS, offset, num_samples,loop_start,loop_end);
fail:
@ -233,3 +247,29 @@ VGMSTREAM * init_vgmstream_opus_nop(STREAMFILE *streamFile) {
fail:
return NULL;
}
/* Shin'en variation [Fast RMX (Switch)] */
VGMSTREAM * init_vgmstream_opus_shinen(STREAMFILE *streamFile) {
off_t offset = 0;
int num_samples = 0, loop_start = 0, loop_end = 0;
/* checks */
if ( !check_extensions(streamFile,"opus,lopus"))
goto fail;
if (read_32bitBE(0x08,streamFile) != 0x01000080)
goto fail;
offset = 0x08;
num_samples = 0;
loop_start = read_32bitLE(0x00,streamFile);
loop_end = read_32bitLE(0x04,streamFile); /* 0 if no loop */
if (loop_start > loop_end)
goto fail; /* just in case */
return init_vgmstream_opus(streamFile, meta_OPUS, offset, num_samples,loop_start,loop_end);
fail:
return NULL;
}

View File

@ -1,114 +1,119 @@
#include "meta.h"
#include "../util.h"
#include "../coding/coding.h"
/* JOE (found in Wall-E and some more Pixar games) */
/* .JOE - from Asobo Studio games [Up (PS2), Wall-E (PS2)] */
VGMSTREAM * init_vgmstream_ps2_joe(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
char filename[PATH_LIMIT];
off_t start_offset;
uint8_t testBuffer[0x10];
off_t loopStart = 0;
off_t loopEnd = 0;
off_t readOffset = 0;
off_t blockOffset = 0;
off_t sampleOffset = 0;
size_t fileLength;
size_t dataLength;
size_t dataInterleave;
size_t file_size, data_size, unknown1, unknown2, interleave;
int loop_flag;
int channel_count;
int channel_count;
/* check extension, case insensitive */
streamFile->get_name(streamFile,filename,sizeof(filename));
if (strcasecmp("joe",filename_extension(filename))) goto fail;
/* check header */
// if (read_32bitBE(0x0C,streamFile) != 0xCCCCCCCC)
// goto fail;
/* checks */
if (!check_extensions(streamFile, "joe"))
goto fail;
loop_flag = 1;
channel_count = 2;
/* build the VGMSTREAM */
file_size = get_streamfile_size(streamFile);
data_size = read_32bitLE(0x04,streamFile);
unknown1 = read_32bitLE(0x08,streamFile);
unknown2 = read_32bitLE(0x0c,streamFile);
/* detect version */
if (data_size/2 == file_size - 0x10
&& unknown1 == 0x0045039A && unknown2 == 0x00108920) { /* Super Farm */
data_size = data_size / 2;
interleave = 0x4000;
}
else if (data_size/2 == file_size - 0x10
&& unknown1 == 0xCCCCCCCC && unknown2 == 0xCCCCCCCC) { /* Sitting Ducks */
data_size = data_size / 2;
interleave = 0x8000;
}
else if (data_size == file_size - 0x10
&& unknown1 == 0xCCCCCCCC && unknown2 == 0xCCCCCCCC) { /* The Mummy: The Animated Series */
data_size = data_size / 2;
interleave = 0x8000;
}
else if (data_size == file_size - 0x4020) { /* CT Special Forces (and all games beyond) */
data_size = data_size / 2;
interleave = unknown1; /* always 0? */
if (!interleave)
interleave = 0x10;
/* header padding contains garbage */
}
else {
goto fail;
}
start_offset = file_size - data_size;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail;
fileLength = get_streamfile_size(streamFile);
dataLength = read_32bitLE(0x4,streamFile);
dataInterleave = read_32bitLE(0x8,streamFile);
if (!dataInterleave)
dataInterleave = 16; /* XXX */
/* fill in the vital statistics */
start_offset = fileLength - dataLength;
vgmstream->channels = channel_count;
vgmstream->sample_rate = read_32bitLE(0x0,streamFile);
vgmstream->sample_rate = read_32bitLE(0x00,streamFile);
vgmstream->coding_type = coding_PSX;
vgmstream->num_samples = dataLength*28/16/channel_count;
vgmstream->num_samples = ps_bytes_to_samples(data_size, channel_count);
readOffset = start_offset;
do {
off_t blockRead = (off_t)read_streamfile(testBuffer,readOffset,0x10,streamFile);
readOffset += blockRead;
blockOffset += blockRead;
if (blockOffset >= dataInterleave) {
readOffset += dataInterleave;
blockOffset -= dataInterleave;
}
/* Loop Start */
if(testBuffer[0x01]==0x06) {
if(loopStart == 0) loopStart = sampleOffset;
/* break; */
}
sampleOffset += 28;
/* Loop End */
if(testBuffer[0x01]==0x03) {
if(loopEnd == 0) loopEnd = sampleOffset;
/* break; */
}
} while (streamFile->get_offset(streamFile)<(int32_t)fileLength);
if(loopStart == 0 && loopEnd == 0) {
loop_flag = 0;
vgmstream->num_samples = dataLength*28/16/channel_count;
} else {
loop_flag = 1;
vgmstream->loop_start_sample = loopStart;
vgmstream->loop_end_sample = loopEnd;
}
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = dataInterleave;
vgmstream->meta_type = meta_PS2_JOE;
/* open the file for reading */
//todo improve, not working 100% with early .joe
{
int i;
STREAMFILE * file;
file = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE);
if (!file) goto fail;
for (i=0;i<channel_count;i++) {
vgmstream->ch[i].streamfile = file;
uint8_t testBuffer[0x10];
off_t blockOffset = 0;
off_t sampleOffset = 0;
off_t readOffset = 0;
off_t loopStart = 0, loopEnd = 0;
vgmstream->ch[i].channel_start_offset=
vgmstream->ch[i].offset=start_offset+
vgmstream->interleave_block_size*i;
readOffset = start_offset;
do {
off_t blockRead = (off_t)read_streamfile(testBuffer,readOffset,0x10,streamFile);
readOffset += blockRead;
blockOffset += blockRead;
if (blockOffset >= interleave) {
readOffset += interleave;
blockOffset -= interleave;
}
/* Loop Start */
if(testBuffer[0x01]==0x06) {
if(loopStart == 0)
loopStart = sampleOffset;
/* break; */
}
sampleOffset += 28;
/* Loop End */
if(testBuffer[0x01]==0x03) {
if(loopEnd == 0)
loopEnd = sampleOffset;
/* break; */
}
} while (streamFile->get_offset(streamFile)<(int32_t)file_size);
if (loopStart == 0 && loopEnd == 0) {
vgmstream->loop_flag = 0;
} else {
vgmstream->loop_start_sample = loopStart;
vgmstream->loop_end_sample = loopEnd;
}
}
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = interleave;
vgmstream->meta_type = meta_PS2_JOE;
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
goto fail;
return vgmstream;
/* clean up anything we may have opened */
fail:
if (vgmstream) close_vgmstream(vgmstream);
close_vgmstream(vgmstream);
return NULL;
}

View File

@ -257,8 +257,9 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) {
/* .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),
* .adx: Remember11 (PC) sfx
* .adp: Headhunter (DC) */
if ( check_extensions(streamFile, "wav,lwav,xwav,da,cd,med,snd,adx,adp") ) {
* .adp: Headhunter (DC)
* .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") ) {

View File

@ -261,8 +261,8 @@ VGMSTREAM * init_vgmstream_ta_aac_mobile(STREAMFILE *streamFile) {
/* check extension, case insensitive */
/* .aac: expected, .laac/ace: for players to avoid hijacking MP4/AAC */
if (!check_extensions(streamFile, "aac,laac,ace"))
/* .aac: expected, .laac: for players to avoid hijacking MP4/AAC */
if (!check_extensions(streamFile, "aac,laac"))
goto fail;
if (read_32bitLE(0x00, streamFile) != 0x41414320) /* "AAC " */
@ -297,8 +297,8 @@ VGMSTREAM * init_vgmstream_ta_aac_mobile(STREAMFILE *streamFile) {
vgmstream->layout_type = layout_none;
vgmstream->num_samples = yamaha_bytes_to_samples(data_size, channel_count);
vgmstream->loop_start_sample = yamaha_bytes_to_samples(read_32bitLE(0x130, streamFile), channel_count);;
vgmstream->loop_end_sample = yamaha_bytes_to_samples(read_32bitLE(0x134, streamFile), channel_count);;
vgmstream->loop_start_sample = yamaha_bytes_to_samples(read_32bitLE(0x130, streamFile), channel_count);
vgmstream->loop_end_sample = yamaha_bytes_to_samples(read_32bitLE(0x134, streamFile), channel_count);
break;
default:
@ -313,3 +313,66 @@ fail:
close_vgmstream(vgmstream);
return NULL;
}
/* Vita variants [Judas Code (Vita)] */
VGMSTREAM * init_vgmstream_ta_aac_vita(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
off_t start_offset;
int channel_count, loop_flag;
/* check extension, case insensitive */
/* .aac: expected, .laac: for players to avoid hijacking MP4/AAC */
if (!check_extensions(streamFile, "aac,laac"))
goto fail;
if (read_32bitLE(0x00, streamFile) != 0x41414320) /* "AAC " */
goto fail;
if (read_32bitLE(0x14, streamFile) != 0x56495441) /* "VITA" */
goto fail;
if (read_32bitLE(0x10d0, streamFile) != 0x57415645) /* "WAVE" */
goto fail;
/* there is a bunch of chunks but we simplify */
/* 0x10E4: codec 0x08? */
channel_count = read_8bit(0x10E5, streamFile);
start_offset = read_32bitLE(0x1100, streamFile);
loop_flag = (read_32bitLE(0x1114, streamFile) > 0);
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count, loop_flag);
if (!vgmstream) goto fail;
vgmstream->sample_rate = read_32bitLE(0x10e8, streamFile);
vgmstream->meta_type = meta_TA_AAC_VITA;
#ifdef VGM_USE_ATRAC9
{
atrac9_config cfg = {0};
cfg.channels = vgmstream->channels;
cfg.encoder_delay = read_32bitLE(0x1124,streamFile);
cfg.config_data = read_32bitBE(0x1128,streamFile);
vgmstream->codec_data = init_atrac9(&cfg);
if (!vgmstream->codec_data) goto fail;
vgmstream->coding_type = coding_ATRAC9;
vgmstream->layout_type = layout_none;
vgmstream->num_samples = atrac9_bytes_to_samples(read_32bitLE(0x10EC, streamFile), vgmstream->codec_data);
vgmstream->num_samples -= cfg.encoder_delay;
vgmstream->loop_start_sample = atrac9_bytes_to_samples(read_32bitLE(0x1110, streamFile), vgmstream->codec_data);
vgmstream->loop_end_sample = atrac9_bytes_to_samples(read_32bitLE(0x1114, streamFile), vgmstream->codec_data);
}
#endif
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
goto fail;
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;
}

View File

@ -63,6 +63,8 @@ typedef struct {
uint32_t coef_big_endian;
uint32_t coef_mode;
int num_samples_data_size;
} txth_header;
static STREAMFILE * open_txth(STREAMFILE * streamFile);
@ -343,25 +345,29 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) {
}
#ifdef VGM_USE_FFMPEG
if (txth.sample_type_bytes && (txth.codec == XMA1 || txth.codec == XMA2)) {
if ((txth.sample_type_bytes || txth.num_samples_data_size) && (txth.codec == XMA1 || txth.codec == XMA2)) {
/* manually find sample offsets */
ms_sample_data msd;
memset(&msd,0,sizeof(ms_sample_data));
ms_sample_data msd = {0};
msd.xma_version = 1;
msd.channels = txth.channels;
msd.data_offset = txth.start_offset;
msd.data_size = txth.data_size;
msd.loop_flag = txth.loop_flag;
msd.loop_start_b = txth.loop_start_sample;
msd.loop_end_b = txth.loop_end_sample;
msd.loop_start_subframe = txth.loop_adjust & 0xF; /* lower 4b: subframe where the loop starts, 0..4 */
msd.loop_end_subframe = txth.loop_adjust >> 4; /* upper 4b: subframe where the loop ends, 0..3 */
if (txth.sample_type_bytes) {
msd.loop_flag = txth.loop_flag;
msd.loop_start_b = txth.loop_start_sample;
msd.loop_end_b = txth.loop_end_sample;
msd.loop_start_subframe = txth.loop_adjust & 0xF; /* lower 4b: subframe where the loop starts, 0..4 */
msd.loop_end_subframe = txth.loop_adjust >> 4; /* upper 4b: subframe where the loop ends, 0..3 */
}
xma_get_samples(&msd, streamFile);
vgmstream->num_samples = msd.num_samples;
vgmstream->loop_start_sample = msd.loop_start_sample;
vgmstream->loop_end_sample = msd.loop_end_sample;
if (txth.sample_type_bytes) {
vgmstream->loop_start_sample = msd.loop_start_sample;
vgmstream->loop_end_sample = msd.loop_end_sample;
}
//skip_samples = msd.skip_samples; //todo add skip samples
}
#endif
@ -524,6 +530,7 @@ static int parse_keyval(STREAMFILE * streamFile, STREAMFILE * streamText, txth_h
else if (0==strcmp(key,"num_samples")) {
if (0==strcmp(val,"data_size")) {
txth->num_samples = get_bytes_to_samples(txth, txth->data_size);
txth->num_samples_data_size = 1;
}
else {
if (!parse_num(streamFile,val, &txth->num_samples)) goto fail;
@ -680,13 +687,15 @@ static int get_bytes_to_samples(txth_header * txth, uint32_t bytes) {
if (!txth->interleave) return 0;
return bytes / txth->interleave * 256 * txth->channels;
/* untested */
case 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:
return bytes;
case AICA:
return bytes * 2 / txth->channels;
case NGC_DTK:
return bytes / 32 * 28; /* always stereo? */
case APPLE_IMA4:

View File

@ -18,6 +18,8 @@ typedef struct {
size_t loop_start_segment;
size_t loop_end_segment;
size_t is_layered;
} txtp_header;
static txtp_header* parse_txtp(STREAMFILE* streamFile);
@ -28,7 +30,9 @@ static void clean_txtp(txtp_header* txtp);
VGMSTREAM * init_vgmstream_txtp(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
txtp_header* txtp = NULL;
segmented_layout_data *data = NULL;
segmented_layout_data *data_s = NULL;
layered_layout_data * data_l = NULL;
int i;
/* checks */
@ -56,16 +60,14 @@ VGMSTREAM * init_vgmstream_txtp(STREAMFILE *streamFile) {
vgmstream->channel_mask = txtp->entry[0].channel_mask;
}
else {
/* multi file */
int num_samples, loop_start_sample = 0, loop_end_sample = 0;
int i;
int loop_flag, channel_count;
else if (txtp->is_layered) {
/* layered multi file */
int channel_count = 0, loop_flag;
/* init layout */
data = init_layout_segmented(txtp->entry_count);
if (!data) goto fail;
data_l = init_layout_layered(txtp->entry_count);
if (!data_l) goto fail;
/* open each segment subfile */
for (i = 0; i < txtp->entry_count; i++) {
@ -73,15 +75,61 @@ VGMSTREAM * init_vgmstream_txtp(STREAMFILE *streamFile) {
if (!temp_streamFile) goto fail;
temp_streamFile->stream_index = txtp->entry[i].subsong;
data->segments[i] = init_vgmstream_from_STREAMFILE(temp_streamFile);
data_l->layers[i] = init_vgmstream_from_STREAMFILE(temp_streamFile);
close_streamfile(temp_streamFile);
if (!data->segments[i]) goto fail;
if (!data_l->layers[i]) goto fail;
data->segments[i]->channel_mask = txtp->entry[0].channel_mask;
channel_count += data_l->layers[i]->channels;
}
/* setup layered VGMSTREAMs */
if (!setup_layout_layered(data_l))
goto fail;
loop_flag = data_l->layers[0]->loop_flag;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail;
vgmstream->sample_rate = data_l->layers[0]->sample_rate;
vgmstream->num_samples = data_l->layers[0]->num_samples;
vgmstream->loop_start_sample = data_l->layers[0]->loop_start_sample;
vgmstream->loop_end_sample = data_l->layers[0]->loop_end_sample;
vgmstream->meta_type = meta_TXTP;
vgmstream->coding_type = data_l->layers[0]->coding_type;
vgmstream->layout_type = layout_layered;
vgmstream->channel_mask = txtp->entry[0].channel_mask;
vgmstream->layout_data = data_l;
}
else {
/* segmented multi file */
int num_samples, loop_start_sample = 0, loop_end_sample = 0;
int loop_flag, channel_count;
/* init layout */
data_s = init_layout_segmented(txtp->entry_count);
if (!data_s) goto fail;
/* open each segment subfile */
for (i = 0; i < txtp->entry_count; i++) {
STREAMFILE* temp_streamFile = open_streamfile_by_filename(streamFile, txtp->entry[i].filename);
if (!temp_streamFile) goto fail;
temp_streamFile->stream_index = txtp->entry[i].subsong;
data_s->segments[i] = init_vgmstream_from_STREAMFILE(temp_streamFile);
close_streamfile(temp_streamFile);
if (!data_s->segments[i]) goto fail;
data_s->segments[i]->channel_mask = txtp->entry[0].channel_mask;
}
/* setup segmented VGMSTREAMs */
if (!setup_layout_segmented(data))
if (!setup_layout_segmented(data_s))
goto fail;
/* get looping and samples */
@ -89,37 +137,37 @@ VGMSTREAM * init_vgmstream_txtp(STREAMFILE *streamFile) {
txtp->loop_end_segment = txtp->entry_count;
loop_flag = (txtp->loop_start_segment > 0 && txtp->loop_start_segment <= txtp->entry_count);
num_samples = 0;
for (i = 0; i < data->segment_count; i++) {
for (i = 0; i < data_s->segment_count; i++) {
if (loop_flag && txtp->loop_start_segment == i+1) {
loop_start_sample = num_samples;
}
num_samples += data->segments[i]->num_samples;
num_samples += data_s->segments[i]->num_samples;
if (loop_flag && txtp->loop_end_segment == i+1) {
loop_end_sample = num_samples;
}
}
channel_count = data->segments[0]->channels;
channel_count = data_s->segments[0]->channels;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail;
vgmstream->sample_rate = data->segments[0]->sample_rate;
vgmstream->sample_rate = data_s->segments[0]->sample_rate;
vgmstream->num_samples = num_samples;
vgmstream->loop_start_sample = loop_start_sample;
vgmstream->loop_end_sample = loop_end_sample;
vgmstream->meta_type = meta_TXTP;
vgmstream->coding_type = data->segments[0]->coding_type;
vgmstream->coding_type = data_s->segments[0]->coding_type;
vgmstream->layout_type = layout_segmented;
vgmstream->layout_data = data;
vgmstream->layout_data = data_s;
if (loop_flag)
data->loop_segment = txtp->loop_start_segment-1;
data_s->loop_segment = txtp->loop_start_segment-1;
}
@ -129,7 +177,8 @@ VGMSTREAM * init_vgmstream_txtp(STREAMFILE *streamFile) {
fail:
clean_txtp(txtp);
close_vgmstream(vgmstream);
free_layout_segmented(data);
free_layout_segmented(data_s);
free_layout_layered(data_l);
return NULL;
}
@ -189,7 +238,7 @@ static int add_filename(txtp_header * txtp, char *filename) {
if (sscanf(config, "%d~%d", &subsong_start, &subsong_end) == 2) {
if (subsong_start > 0 && subsong_end > 0) {
range_start = subsong_start-1;
range_end = subsong_end-1;
range_end = subsong_end;
}
}
else if (sscanf(config, "%u", &subsong_start) == 1) {
@ -267,6 +316,14 @@ static int parse_keyval(txtp_header * txtp, const char * key, const char * val)
else if (0==strcmp(key,"loop_end_segment")) {
if (!parse_num(val, &txtp->loop_end_segment)) goto fail;
}
else if (0==strcmp(key,"mode")) {
if (0==strcmp(val,"layers")) {
txtp->is_layered = 1;
}
else {
goto fail;
}
}
else {
VGM_LOG("TXTP: unknown key=%s, val=%s\n", key,val);
goto fail;

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

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

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

@ -1,6 +1,7 @@
#include "meta.h"
#include "../util.h"
#include "../coding/coding.h"
#include <string.h>
/* most info from XWBtool, xactwb.h, xact2wb.h and xact3wb.h */
@ -25,7 +26,7 @@ static const int wma_block_align_index[17] = {
};
typedef enum { PCM, XBOX_ADPCM, MS_ADPCM, XMA1, XMA2, WMA, XWMA, ATRAC3, OGG } xact_codec;
typedef enum { PCM, XBOX_ADPCM, MS_ADPCM, XMA1, XMA2, WMA, XWMA, ATRAC3, OGG, DSP, ATRAC9_RIFF } xact_codec;
typedef struct {
int little_endian;
int version;
@ -69,6 +70,7 @@ typedef struct {
} xwb_header;
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) */
@ -292,33 +294,45 @@ 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 */
&& (xwb.block_align == 0x60 || xwb.block_align == 0x98 || xwb.block_align == 0xc0) ) {
xwb.codec = ATRAC3; /* standard ATRAC3 blocks sizes; no other way to identify (other than reading data) */
&& (xwb.block_align == 0x60 || xwb.block_align == 0x98 || xwb.block_align == 0xc0) ) { /* standard ATRAC3 blocks sizes */
/* 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)
* ignore for now and just calc max samples */
xwb.num_samples = atrac3_bytes_to_samples(xwb.stream_size, xwb.block_align * xwb.channels);
}
/* Oddworld: Stranger's Wrath iOS/Android format hijack, with changed meanings */
if (xwb.codec == OGG) {
else if (xwb.codec == OGG) {
/* Oddworld: Stranger's Wrath (iOS/Android) */
xwb.num_samples = xwb.stream_size / (2 * xwb.channels); /* uncompressed bytes */
xwb.stream_size = xwb.loop_end;
xwb.loop_start = 0;
xwb.loop_end = 0;
}
else if (xwb.version == XACT3_0_MAX && xwb.codec == XMA2
&& xwb.bits_per_sample == 0x01 && xwb.block_align == 0x04
&& xwb.data_size == 0x55951c1c) { /* some kind of id? */
/* Stardew Valley (Switch), full interleaved DSPs (including headers) */
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 */
xwb.loop_flag = (xwb.loop_end > 0 || xwb.loop_end_sample > xwb.loop_start)
&& !(xwb.entry_flags & WAVEBANKENTRY_FLAGS_IGNORELOOP);
if (xwb.codec != OGG) {
/* for Oddworld OGG the data_size value is size of uncompressed bytes instead */
/* some BlazBlue Centralfiction songs have padding after data size (maybe wrong rip?) */
/* 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) {
/* some low-q rips don't remove padding, relax validation a bit */
if (xwb.data_offset + xwb.data_size > get_streamfile_size(streamFile))
goto fail;
}
@ -462,7 +476,7 @@ VGMSTREAM * init_vgmstream_xwb(STREAMFILE *streamFile) {
break;
}
case XWMA: { /* WMAudio2 (WMA v2): BlazBlue (X360), WMAudio3 (WMA Pro): ? */
case XWMA: { /* WMAudio2 (WMA v2): BlazBlue (X360), WMAudio3 (WMA Pro): Bullet Witch (PC) voices */
uint8_t buf[100];
int bytes, bps_index, block_align, block_index, avg_bps, wma_codec;
@ -485,7 +499,7 @@ VGMSTREAM * init_vgmstream_xwb(STREAMFILE *streamFile) {
break;
}
case ATRAC3: { /* Techland PS3 extension: Sniper Ghost Warrior (PS3) */
case ATRAC3: { /* Techland PS3 extension [Sniper Ghost Warrior (PS3)] */
uint8_t buf[200];
int bytes;
@ -503,14 +517,48 @@ VGMSTREAM * init_vgmstream_xwb(STREAMFILE *streamFile) {
break;
}
case OGG: { /* Oddworld: Strangers Wrath iOS/Android extension */
case OGG: { /* Oddworld: Strangers Wrath (iOS/Android) extension */
vgmstream->codec_data = init_ffmpeg_offset(streamFile, xwb.stream_offset, xwb.stream_size);
if ( !vgmstream->codec_data ) goto fail;
vgmstream->coding_type = coding_FFmpeg;
vgmstream->layout_type = layout_none;
break;
}
#endif
case DSP: { /* Stardew Valley (Switch) extension */
vgmstream->coding_type = coding_NGC_DSP;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = xwb.stream_size / xwb.channels;
dsp_read_coefs(vgmstream,streamFile,xwb.stream_offset + 0x1c,vgmstream->interleave_block_size,!xwb.little_endian);
dsp_read_hist (vgmstream,streamFile,xwb.stream_offset + 0x3c,vgmstream->interleave_block_size,!xwb.little_endian);
xwb.stream_offset += 0x60; /* skip DSP header */
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:
@ -529,6 +577,30 @@ fail:
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;
}
/* ****************************************************************************** */
@ -591,7 +663,7 @@ typedef struct {
/* try to find the stream name in a companion XSB file, a comically complex cue format. */
static int get_xsb_name(char * buf, size_t maxsize, int target_subsong, xwb_header * xwb, STREAMFILE *streamXwb) {
static int get_xsb_name(char * buf, size_t maxsize, int target_subsong, xwb_header * xwb, STREAMFILE *streamXwb, char* filename) {
STREAMFILE *streamFile = NULL;
int i,j, start_sound, cfg__start_sound = 0, cfg__selected_wavebank = 0;
int xsb_version;
@ -601,7 +673,10 @@ static int get_xsb_name(char * buf, size_t maxsize, int target_subsong, xwb_head
xsb_header xsb = {0};
streamFile = open_streamfile_by_ext(streamXwb, "xsb");
if (filename)
streamFile = open_streamfile_by_filename(streamXwb, filename);
else
streamFile = open_streamfile_by_ext(streamXwb, "xsb");
if (!streamFile) goto fail;
/* check header */
@ -718,8 +793,10 @@ static int get_xsb_name(char * buf, size_t maxsize, int target_subsong, xwb_head
} else {
suboff = size - 0x08;
}
//} else if (flag==0x11) { /* Stardew Valley (Switch) */
// suboff = size; //???
} else {
VGM_LOG("XSB: xsb flag 0x%x at offset 0x%08lx not implemented\n", flag, off);
VGM_LOG("XSB: xsb flag 0x%x (size=%x) at offset 0x%08lx not implemented\n", flag, size, off);
goto fail;
}
}
@ -871,18 +948,34 @@ fail:
static void get_name(char * buf, size_t maxsize, int target_subsong, xwb_header * xwb, STREAMFILE *streamFile) {
int name_found;
char xwb_filename[PATH_LIMIT];
char xsb_filename[PATH_LIMIT];
/* try inside this xwb */
name_found = get_xwb_name(buf, maxsize, target_subsong, xwb, streamFile);
if (name_found) return;
/* try again in external .xsb */
get_xsb_name(buf, maxsize, target_subsong, xwb, streamFile);
/* try again in external .xsb, using a bunch of possible name pairs */
get_streamfile_filename(streamFile,xwb_filename,PATH_LIMIT);
if (strcmp(xwb_filename,"Wave Bank.xwb")==0) {
strcpy(xsb_filename,"Sound Bank.xsb");
}
else if (strcmp(xwb_filename,"UIMusicBank.xwb")==0) {
strcpy(xsb_filename,"UISoundBank.xsb");
}
else {
xsb_filename[0] = '\0';
}
//todo try others: InGameMusic.xwb + ingamemusic.xsb, NB_BGM_m0100_WB.xwb + NB_BGM_m0100_SB.xsb, etc
if (xsb_filename[0] != '\0') {
name_found = get_xsb_name(buf, maxsize, target_subsong, xwb, streamFile, xsb_filename);
if (name_found) return;
}
//todo try again with common names (xwb and xsb often are named slightly differently using a common convention):
// InGameMusic.xwb + ingamemusic.xsb
// UIMusicBank.xwb + UISoundBank.xsb
// NB_BGM_m0100_WB.xwb + NB_BGM_m0100_SB.xsb
// Wave Bank.xwb + Sound Bank.xsb
/* one last time with same name */
get_xsb_name(buf, maxsize, target_subsong, xwb, streamFile, NULL);
}

View File

@ -4,26 +4,44 @@
/* .XWC - Starbreeze games [Chronicles of Riddick: Assault on Dark Athena, Syndicate] */
VGMSTREAM * init_vgmstream_xwc(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
off_t start_offset;
off_t start_offset, extra_offset;
size_t data_size;
int loop_flag, channel_count, codec;
int loop_flag, channel_count, codec, num_samples;
/* check extensions (.xwc is the extension of the bigfile, individual files don't have one) */
if ( !check_extensions(streamFile,"xwc"))
goto fail;
if(read_32bitBE(0x00,streamFile) != 0x00040000 && /* version? */
read_32bitBE(0x04,streamFile) != 0x00900000)
goto fail;
data_size = read_32bitLE(0x08, streamFile); /* including subheader */
channel_count = read_32bitLE(0x0c, streamFile);
/* 0x10: num_samples */
/* 0x14: 0x8000? */
codec = read_32bitBE(0x24, streamFile);
/* 0x28: num_samples */
/* 0x2c: config data? (first nibble: 0x4=mono, 0x8=stereo) */
/* 0x30+: codec dependant */
/* version */
if (read_32bitBE(0x00,streamFile) == 0x00030000 &&
read_32bitBE(0x04,streamFile) == 0x00900000) { /* The Darkness */
data_size = read_32bitLE(0x08, streamFile); /* including subheader */
channel_count = read_32bitLE(0x0c, streamFile);
/* 0x10: num_samples */
/* 0x14: 0x8000? */
/* 0x18: null */
codec = read_32bitBE(0x1c, streamFile);
num_samples = read_32bitLE(0x20, streamFile);
/* 0x24: config data >> 2? (0x00(1): channels; 0x01(2): ?, 0x03(2): sample_rate) */
extra_offset = 0x28;
}
else if (read_32bitBE(0x00,streamFile) == 0x00040000 &&
read_32bitBE(0x04,streamFile) == 0x00900000) { /* Riddick, Syndicate */
data_size = read_32bitLE(0x08, streamFile); /* including subheader */
channel_count = read_32bitLE(0x0c, streamFile);
/* 0x10: num_samples */
/* 0x14: 0x8000? */
codec = read_32bitBE(0x24, streamFile);
num_samples = read_32bitLE(0x28, streamFile);
/* 0x2c: config data >> 2? (0x00(1): channels; 0x01(2): ?, 0x03(2): sample_rate) */
/* 0x30+: codec dependant */
extra_offset = 0x30;
}
else {
goto fail;
}
loop_flag = 0; /* seemingly not in the file */
@ -31,7 +49,7 @@ VGMSTREAM * init_vgmstream_xwc(STREAMFILE *streamFile) {
vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail;
vgmstream->num_samples = read_32bitLE(0x28, streamFile);
vgmstream->num_samples = num_samples;
vgmstream->meta_type = meta_XWC;
switch(codec) {
@ -40,8 +58,8 @@ VGMSTREAM * init_vgmstream_xwc(STREAMFILE *streamFile) {
mpeg_custom_config cfg = {0};
start_offset = 0x800;
vgmstream->num_samples = read_32bitLE(0x30, streamFile); /* with encoder delay */ //todo improve
cfg.data_size = read_32bitLE(0x34, streamFile); //data_size - 0x28;
vgmstream->num_samples = read_32bitLE(extra_offset+0x00, streamFile); /* with encoder delay */ //todo improve
cfg.data_size = read_32bitLE(extra_offset+0x04, streamFile); //data_size - 0x28;
vgmstream->codec_data = init_mpeg_custom(streamFile, start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_STANDARD, &cfg);
if (!vgmstream->codec_data) goto fail;
@ -56,13 +74,13 @@ VGMSTREAM * init_vgmstream_xwc(STREAMFILE *streamFile) {
uint8_t buf[0x100];
int32_t bytes, seek_size, block_size, block_count, sample_rate;
seek_size = read_32bitLE(0x30, streamFile);
start_offset = 0x34 + seek_size + read_32bitLE(0x34+seek_size, streamFile) + 0x08;
seek_size = read_32bitLE(extra_offset+0x00, streamFile);
start_offset = extra_offset+0x04 + seek_size + read_32bitLE(extra_offset+0x04+seek_size, streamFile) + 0x08;
start_offset += (start_offset % 0x800) ? 0x800 - (start_offset % 0x800) : 0; /* padded */
sample_rate = read_32bitBE(0x34+seek_size+0x10, streamFile);
block_size = read_32bitBE(0x34+seek_size+0x1c, streamFile);
block_count = read_32bitBE(0x34+seek_size+0x28, streamFile);
sample_rate = read_32bitBE(extra_offset+0x04+seek_size+0x10, streamFile);
block_size = read_32bitBE(extra_offset+0x04+seek_size+0x1c, streamFile);
block_count = read_32bitBE(extra_offset+0x04+seek_size+0x28, streamFile);
/* others: scrambled RIFF fmt BE values */
bytes = ffmpeg_make_riff_xma2(buf,0x100, vgmstream->num_samples, data_size, vgmstream->channels, sample_rate, block_count, block_size);
@ -93,10 +111,6 @@ VGMSTREAM * init_vgmstream_xwc(STREAMFILE *streamFile) {
goto fail;
}
if (vgmstream->sample_rate != 48000) { /* get from config data instead of codecs? */
VGM_LOG("XWC: unexpected sample rate %i\n",vgmstream->sample_rate);
goto fail;
}
if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) )
goto fail;

View File

@ -353,6 +353,7 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = {
init_vgmstream_ta_aac_ps3,
init_vgmstream_ta_aac_mobile,
init_vgmstream_ta_aac_mobile_vorbis,
init_vgmstream_ta_aac_vita,
init_vgmstream_ps3_mta2,
init_vgmstream_ngc_ulw,
init_vgmstream_pc_xa30,
@ -367,6 +368,7 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = {
init_vgmstream_opus_n1,
init_vgmstream_opus_capcom,
init_vgmstream_opus_nop,
init_vgmstream_opus_shinen,
init_vgmstream_pc_al2,
init_vgmstream_pc_ast,
init_vgmstream_naac,
@ -407,6 +409,8 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = {
init_vgmstream_opus_ppp,
init_vgmstream_ubi_bao_pk,
init_vgmstream_dsp_switch_audio,
init_vgmstream_dsp_sadf,
init_vgmstream_h4m,
init_vgmstream_txth, /* should go at the end (lower priority) */
#ifdef VGM_USE_FFMPEG
@ -933,6 +937,7 @@ void render_vgmstream(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstre
case layout_blocked_ea_wve_au00:
case layout_blocked_ea_wve_ad10:
case layout_blocked_sthd:
case layout_blocked_h4m:
render_vgmstream_blocked(buffer,sample_count,vgmstream);
break;
case layout_aix:

View File

@ -259,6 +259,7 @@ typedef enum {
layout_blocked_ea_wve_au00, /* EA WVE au00 blocks */
layout_blocked_ea_wve_ad10, /* EA WVE Ad10 blocks */
layout_blocked_sthd, /* Dream Factory STHD */
layout_blocked_h4m, /* H4M video */
/* otherwise odd */
layout_aix, /* CRI AIX's wheels within wheels */
@ -289,6 +290,7 @@ typedef enum {
meta_DSP_WII_WSD, /* Phantom Brave (WII) */
meta_WII_NDP, /* Vertigo (Wii) */
meta_DSP_YGO, /* Konami: Yu-Gi-Oh! The Falsebound Kingdom (NGC), Hikaru no Go 3 (NGC) */
meta_DSP_SADF, /* Procyon Studio SADF - Xenoblade Chronicles 2 (Switch) */
/* Nintendo */
meta_STRM, /* Nintendo STRM */
@ -682,6 +684,9 @@ typedef enum {
meta_OPUS_PPP, /* .at9 Opus [Penny-Punching Princess (Switch)] */
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)] */
meta_H4M, /* Hudson HVQM4 video [Resident Evil 0 (GC), Tales of Symphonia (GC)] */
#ifdef VGM_USE_FFMPEG
meta_FFmpeg,