Merge pull request #235 from bnnm/h4m-joe-aif

H4M, JOE, AIF
This commit is contained in:
Christopher Snowhill 2018-06-03 14:44:42 -07:00 committed by GitHub
commit 254791551b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 443 additions and 199 deletions

View File

@ -120,8 +120,9 @@ handling.
A few extensions that vgmstream supports clash with common ones. Since players
like foobar or Winamp don't react well to that, they may be renamed for
vgmstream (mainly to get looping in some cases).
- .ac3 to .lac3
- .aac to .laac
- .ac3 to .lac3
- .aif to .aiffl or .aifcl
- .asf to .sng (EA formats)
- .mp4 to .lmp4
- .ogg to .logg

View File

@ -28,9 +28,11 @@ static const char* extension_list[] = {
"afc",
"agsc",
"ahx",
"aifc",
"aifcl",
//"aif", //common
"aifc", //common?
"aifcl", //fake extension, for AIF???
//"aiff", //common
"aiffl", //fake extension, for AIF???
"aix",
"akb",
"al2",
@ -137,6 +139,7 @@ static const char* extension_list[] = {
"gtd",
"gwm",
"h4m",
"hca",
"hgc1",
"his",
@ -621,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[] = {
@ -687,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"},
@ -798,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"},
@ -1018,6 +1022,7 @@ static const meta_info meta_info_list[] = {
{meta_TA_AAC_VITA, "tri-Ace AAC (Vita) header"},
{meta_OGG_GWM, "Ogg Vorbis (GWM header)"},
{meta_DSP_SADF, "Procyon Studio SADF header"},
{meta_H4M, "Hudson HVQM4 header"},
#ifdef VGM_USE_FFMPEG
{meta_FFmpeg, "FFmpeg supported file format"},

View File

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

102
src/layout/blocked_h4m.c Normal file
View File

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

View File

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

View File

@ -483,6 +483,10 @@
<File
RelativePath=".\meta\gtd.c"
>
</File>
<File
RelativePath=".\meta\h4m.c"
>
</File>
<File
RelativePath=".\meta\halpst.c"
@ -1822,6 +1826,10 @@
RelativePath=".\layout\blocked_gsb.c"
>
</File>
<File
RelativePath=".\layout\blocked_h4m.c"
>
</File>
<File
RelativePath=".\layout\blocked_halpst.c"
>

View File

@ -235,6 +235,7 @@
<ClCompile Include="meta\bar.c" />
<ClCompile Include="meta\gsp_gsb.c" />
<ClCompile Include="meta\gtd.c" />
<ClCompile Include="meta\h4m.c" />
<ClCompile Include="meta\halpst.c" />
<ClCompile Include="meta\hca.c" />
<ClCompile Include="meta\his.c" />
@ -503,6 +504,7 @@
<ClCompile Include="layout\blocked_emff.c" />
<ClCompile Include="layout\blocked_filp.c" />
<ClCompile Include="layout\blocked_gsb.c" />
<ClCompile Include="layout\blocked_h4m.c" />
<ClCompile Include="layout\blocked_halpst.c" />
<ClCompile Include="layout\blocked_matx.c" />
<ClCompile Include="layout\interleave.c" />

View File

@ -295,6 +295,9 @@
<ClCompile Include="meta\gtd.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\h4m.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\halpst.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
@ -1075,6 +1078,9 @@
<ClCompile Include="layout\blocked_gsb.c">
<Filter>layout\Source Files</Filter>
</ClCompile>
<ClCompile Include="layout\blocked_h4m.c">
<Filter>layout\Source Files</Filter>
</ClCompile>
<ClCompile Include="layout\blocked_halpst.c">
<Filter>layout\Source Files</Filter>
</ClCompile>

View File

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

105
src/meta/h4m.c Normal file
View File

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

View File

@ -754,4 +754,7 @@ VGMSTREAM * init_vgmstream_ubi_bao_pk(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_dsp_switch_audio(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_dsp_sadf(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_h4m(STREAMFILE *streamFile);
#endif /*_META_H*/

View File

@ -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;
}

View File

@ -238,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) {

View File

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

View File

@ -410,6 +410,7 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = {
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
@ -936,6 +937,7 @@ void render_vgmstream(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstre
case layout_blocked_ea_wve_au00:
case layout_blocked_ea_wve_ad10:
case layout_blocked_sthd:
case layout_blocked_h4m:
render_vgmstream_blocked(buffer,sample_count,vgmstream);
break;
case layout_aix:

View File

@ -252,6 +252,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 */
@ -678,6 +679,7 @@ typedef enum {
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,