Updated VGMStream to r1050-1335-g25479155.
parent
3901305105
commit
a767189657
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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"},
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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_*/
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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*/
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
||||
readOffset = start_offset;
|
||||
do {
|
||||
off_t blockRead = (off_t)read_streamfile(testBuffer,readOffset,0x10,streamFile);
|
||||
vgmstream->num_samples = ps_bytes_to_samples(data_size, channel_count);
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -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") ) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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" */
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Reference in New Issue