Merge pull request #327 from bnnm/zsnd-opusx-lopus

zsnd opusx lopus
This commit is contained in:
Christopher Snowhill 2018-12-08 19:55:40 -08:00 committed by GitHub
commit 79d523a4db
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 641 additions and 94 deletions

View File

@ -31,6 +31,7 @@ static const char* extension_list[] = {
"adm",
"adp",
"adpcm",
"adpcmx",
"ads",
"adw",
"adx",
@ -128,7 +129,9 @@ static const char* extension_list[] = {
"e4x",
"eam",
"emff",
"enm",
"eno",
"ens",
"enth",
"exa",
"ezw",
@ -270,6 +273,7 @@ static const char* extension_list[] = {
"oma", //FFmpeg/not parsed (ATRAC3/ATRAC3PLUS/MP3/LPCM/WMA)
"omu",
//"opus", //common
"opusx",
"otm",
"ovb",
@ -483,6 +487,8 @@ static const char* extension_list[] = {
"ymf",
"zsd",
"zsm",
"zss",
"zwdsp",
"vgmstream" /* fake extension, catch-all for FFmpeg/txth/etc */
@ -721,7 +727,7 @@ static const meta_info meta_info_list[] = {
{meta_RWAV, "Nintendo RWAV header"},
{meta_CWAV, "Nintendo CWAV header"},
{meta_FWAV, "Nintendo FWAV header"},
{meta_PSX_XA, "RIFF/CDXA header"},
{meta_XA, "Sony XA RIFF header"},
{meta_PS2_RXWS, "Sony RXWS header"},
{meta_PS2_RAW, ".int PCM raw header"},
{meta_PS2_OMU, "Alter Echo OMU Header"},
@ -826,7 +832,7 @@ static const meta_info meta_info_list[] = {
{meta_KRAW, "Geometry Wars: Galaxies KRAW header"},
{meta_NGC_YMF, "YMF DSP Header"},
{meta_PS2_CCC, "CCC Header"},
{meta_PSX_FAG, "FAG Header"},
{meta_FAG, "Radical .FAG Header"},
{meta_PS2_MIHB, "Sony MultiStream MIC header"},
{meta_DSP_WII_MUS, "mus header"},
{meta_WII_SNG, "SNG DSP Header"},
@ -1046,12 +1052,12 @@ static const meta_info meta_info_list[] = {
{meta_NGC_VID1, "Neversoft VID1 header"},
{meta_PC_FLX, "Ultima IX .FLX header"},
{meta_MOGG, "Harmonix Music Systems MOGG Vorbis"},
{meta_OGG_VORBIS, "Ogg Vorbis"},
{meta_OGG_SLI, "Ogg Vorbis with .sli looping"},
{meta_OPUS_SLI, "Ogg Opus with .sli looping"},
{meta_OGG_SFL, "Ogg Vorbis with SFPL looping"},
{meta_OGG_KOVS, "Ogg Vorbis (KOVS header)"},
{meta_OGG_encrypted, "Ogg Vorbis (encrypted)"},
{meta_OGG_VORBIS, "Ogg Vorbis header"},
{meta_OGG_SLI, "Ogg Vorbis header (.sli looping)"},
{meta_OPUS_SLI, "Ogg Opus header (.sli looping)"},
{meta_OGG_SFL, "Ogg Vorbis header (SFPL looping)"},
{meta_OGG_KOVS, "Ogg Vorbis header (KOVS)"},
{meta_OGG_encrypted, "Ogg Vorbis header (encrypted)"},
{meta_KMA9, "Koei Tecmo KMA9 header"},
{meta_XWC, "Starbreeze XWC header"},
{meta_SQEX_SAB, "Square-Enix SAB header"},
@ -1115,6 +1121,9 @@ static const meta_info meta_info_list[] = {
{meta_XPCM, "Circus XPCM header"},
{meta_MSF_TAMASOFT, "Tama-Soft MSF header"},
{meta_XPS_DAT, "From Software .XPS+DAT header"},
{meta_ZSND, "Vicarious Visions ZSND header"},
{meta_DSP_ADPCMX, "AQUASTYLE ADPY header"},
{meta_OGG_OPUS, "Ogg Opus header"},
};

View File

@ -279,6 +279,10 @@
<File
RelativePath=".\meta\xvag_streamfile.h"
>
</File>
<File
RelativePath=".\meta\zsnd_streamfile.h"
>
</File>
</Filter>
<Filter
@ -861,6 +865,10 @@
<File
RelativePath=".\meta\nxap.c"
>
</File>
<File
RelativePath=".\meta\ogg_opus.c"
>
</File>
<File
RelativePath=".\meta\ogg_vorbis.c"
@ -1271,11 +1279,11 @@
>
</File>
<File
RelativePath=".\meta\psx_cdxa.c"
RelativePath=".\meta\xa.c"
>
</File>
<File
RelativePath=".\meta\psx_fag.c"
RelativePath=".\meta\fag.c"
>
</File>
<File
@ -1662,6 +1670,10 @@
RelativePath=".\meta\zsd.c"
>
</File>
<File
RelativePath=".\meta\zsnd.c"
>
</File>
<File
RelativePath=".\meta\zwdsp.c"
>

View File

@ -324,6 +324,7 @@
<ClCompile Include="meta\nwav.c" />
<ClCompile Include="meta\nxa.c" />
<ClCompile Include="meta\nxap.c" />
<ClCompile Include="meta\ogg_opus.c" />
<ClCompile Include="meta\ogg_vorbis.c" />
<ClCompile Include="meta\ogl.c" />
<ClCompile Include="meta\omu.c" />
@ -406,8 +407,8 @@
<ClCompile Include="meta\ps3_cps.c" />
<ClCompile Include="meta\ps3_msf.c" />
<ClCompile Include="meta\ps3_mta2.c" />
<ClCompile Include="meta\psx_cdxa.c" />
<ClCompile Include="meta\psx_fag.c" />
<ClCompile Include="meta\xa.c" />
<ClCompile Include="meta\fag.c" />
<ClCompile Include="meta\psx_gms.c" />
<ClCompile Include="meta\ea_swvr.c" />
<ClCompile Include="meta\raw.c" />
@ -490,6 +491,7 @@
<ClCompile Include="meta\xwma.c" />
<ClCompile Include="meta\ydsp.c" />
<ClCompile Include="meta\zsd.c" />
<ClCompile Include="meta\zsnd.c" />
<ClCompile Include="meta\zwdsp.c" />
<ClCompile Include="coding\acm_decoder.c" />
<ClCompile Include="coding\acm_decoder_decode.c" />

View File

@ -110,6 +110,9 @@
<ClInclude Include="meta\xvag_streamfile.h">
<Filter>meta\Header Files</Filter>
</ClInclude>
<ClInclude Include="meta\zsnd_streamfile.h">
<Filter>meta\Header Files</Filter>
</ClInclude>
<ClInclude Include="meta\meta.h">
<Filter>meta\Header Files</Filter>
</ClInclude>
@ -532,6 +535,9 @@
<ClCompile Include="meta\nxap.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\ogg_opus.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\ogg_vorbis.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
@ -778,10 +784,10 @@
<ClCompile Include="meta\ps3_mta2.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\psx_cdxa.c">
<ClCompile Include="meta\xa.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\psx_fag.c">
<ClCompile Include="meta\fag.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\psx_gms.c">
@ -1015,6 +1021,9 @@
<ClCompile Include="meta\zsd.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\zsnd.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\zwdsp.c">
<Filter>meta\Source Files</Filter>
</ClCompile>

View File

@ -396,6 +396,9 @@ VGMSTREAM * init_vgmstream_bnk_sony(STREAMFILE *streamFile) {
vgmstream->loop_start_sample = loop_start;
vgmstream->loop_end_sample = loop_end;
break;
default:
goto fail;
}
if (name_offset)

52
src/meta/fag.c Normal file
View File

@ -0,0 +1,52 @@
#include "meta.h"
#include "../coding/coding.h"
/* .FAG - from Jackie Chan: Stuntmaster (PS1) */
VGMSTREAM * init_vgmstream_fag(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
off_t start_offset;
size_t stream_size;
int loop_flag, channel_count;
int total_subsongs, target_subsong = streamFile->stream_index;
/* checks */
if (!check_extensions(streamFile,"fag"))
goto fail;
total_subsongs = read_32bitLE(0x00,streamFile);
if (target_subsong == 0) target_subsong = 1;
if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail;
if (total_subsongs > 2)
goto fail;
loop_flag = 0;
channel_count = 2;
start_offset = read_32bitLE(0x04 + 0x04*(target_subsong-1),streamFile);
stream_size = read_32bitLE(0x04 + 0x04*total_subsongs + 0x04*(target_subsong-1),streamFile) - start_offset; /* end offset */
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail;
vgmstream->meta_type = meta_FAG;
vgmstream->sample_rate = 24000;
vgmstream->num_streams = total_subsongs;
vgmstream->stream_size = stream_size;
vgmstream->coding_type = coding_PSX;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = 0x8000;
vgmstream->num_samples = ps_bytes_to_samples(stream_size, channel_count);
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
goto fail;
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;
}

View File

@ -48,6 +48,7 @@ VGMSTREAM * init_vgmstream_dsp_mcadpcm(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_dsp_switch_audio(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_dsp_sps_n1(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_dsp_itl_ch(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_dsp_adpcmx(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_csmp(STREAMFILE *streamFile);
@ -64,7 +65,7 @@ VGMSTREAM * init_vgmstream_rsf(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_rwsd(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_cdxa(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_xa(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_ps2_rxws(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_ps2_rxw(STREAMFILE *streamFile);
@ -289,7 +290,7 @@ VGMSTREAM * init_vgmstream_sadl(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_ps2_ccc(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_psx_fag(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_fag(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_ps2_mihb(STREAMFILE * streamFile);
@ -663,6 +664,7 @@ VGMSTREAM * init_vgmstream_opus_shinen(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_opus_nus3(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_opus_sps_n1(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_opus_nxa(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_opus_opusx(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_pc_al2(STREAMFILE * streamFile);
@ -813,4 +815,8 @@ VGMSTREAM * init_vgmstream_msf_tamasoft(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_xps_dat(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_xps(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_zsnd(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_ogg_opus(STREAMFILE * streamFile);
#endif /*_META_H*/

View File

@ -1115,7 +1115,7 @@ fail:
}
/* .vag - Nippon Ichi SPS wrapper [Penny-Punching Princess (Switch), Ys VIII (Switch)] */
VGMSTREAM * init_vgmstream_dsp_sps_n1(STREAMFILE *streamFile) {//todo rename
VGMSTREAM * init_vgmstream_dsp_sps_n1(STREAMFILE *streamFile) {
dsp_meta dspm = {0};
/* checks */
@ -1168,3 +1168,31 @@ VGMSTREAM * init_vgmstream_dsp_itl_ch(STREAMFILE *streamFile) {
fail:
return NULL;
}
/* .adpcmx - AQUASTYLE wrapper [Touhou Genso Wanderer -Reloaded- (Switch)] */
VGMSTREAM * init_vgmstream_dsp_adpcmx(STREAMFILE *streamFile) {
dsp_meta dspm = {0};
/* checks */
if (!check_extensions(streamFile, "adpcmx"))
goto fail;
if (read_32bitBE(0x00,streamFile) != 0x41445059) /* "ADPY" */
goto fail;
/* 0x04(2): 1? */
/* 0x08: some size? */
/* 0x0c: null */
dspm.channel_count = read_16bitLE(0x06,streamFile);
dspm.max_channels = 2;
dspm.little_endian = 1;
dspm.header_offset = 0x10;
dspm.header_spacing = 0x60;
dspm.start_offset = dspm.header_offset + dspm.header_spacing*dspm.channel_count;
dspm.interleave = 0x08;
dspm.meta_type = meta_DSP_ADPCMX;
return init_vgmstream_dsp_common(streamFile, &dspm);
fail:
return NULL;
}

173
src/meta/ogg_opus.c Normal file
View File

@ -0,0 +1,173 @@
#include "meta.h"
#include "../coding/coding.h"
static int get_ogg_page_size(STREAMFILE *streamFile, off_t page_offset, off_t *out_data_offset, size_t *out_page_size);
static int ogg_get_num_samples(STREAMFILE *streamFile, off_t start_offset);
/* Ogg Opus - standard Opus with optional looping comments [The Pillars of Earth (PC), Monster Boy and the Cursed Kingdom (Switch)] */
VGMSTREAM * init_vgmstream_ogg_opus(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
off_t start_offset, data_offset = 0;
size_t page_size = 0;
int loop_flag, channel_count, original_rate;
int loop_start = 0, loop_end = 0;
/* checks */
/* .opus: standard, .lopus: fake extension for plugins
* .ogg: less common, .logg: same */
if (!check_extensions(streamFile, "opus,lopus,ogg,logg"))
goto fail;
if (read_32bitBE(0x00,streamFile) != 0x4F676753) /* "OggS" */
goto fail;
/* see: https://tools.ietf.org/html/rfc7845.html */
start_offset = 0x00;
/* parse 1st page: opus head */
if (!get_ogg_page_size(streamFile, start_offset, &data_offset, &page_size))
goto fail;
if (read_32bitBE(data_offset+0x00,streamFile) != 0x4F707573 && /* "Opus" */
read_32bitBE(data_offset+0x04,streamFile) != 0x48656164) /* "Head" */
goto fail;
/* 0x01: version 1, fixed */
channel_count = read_8bit(data_offset+0x09,streamFile);
/* 0x0A: skip samples */
original_rate = read_32bitLE(data_offset+0x0c,streamFile);
/* 0x10: gain */
/* 0x12: mapping family */
/* parse 2nd page: opus tags (also mandatory) */
if (!get_ogg_page_size(streamFile, start_offset+page_size, &data_offset, &page_size))
goto fail;
if (read_32bitBE(data_offset+0x00,streamFile) != 0x4F707573 && /* "Opus" */
read_32bitBE(data_offset+0x04,streamFile) != 0x54616773) /* "Tags" */
goto fail;
loop_flag = 0;
{
char user_comment[1024+1];
off_t offset;
int vendor_size, comment_count, user_comment_size, user_comment_max;
int i;
int has_encoder_options = 0, has_title = 0;
vendor_size = read_32bitLE(data_offset+0x08,streamFile);
comment_count = read_32bitLE(data_offset+0x0c+vendor_size,streamFile);
/* parse comments */
offset = data_offset + 0x0c + vendor_size + 0x04;
for (i = 0; i < comment_count; i++) {
user_comment_size = read_32bitLE(offset+0x00,streamFile);
user_comment_max = user_comment_size > 1024 ? 1024 : user_comment_size;
read_string(user_comment,user_comment_max+1, offset+0x04,streamFile);
/* parse loop strings */
if (strstr(user_comment,"LOOP_START=")==user_comment) { /* Monster Boy and the Cursed Kingdom (Switch) */
loop_start = atol(strrchr(user_comment,'=')+1);
loop_flag = (loop_start >= 0);
}
else if (strstr(user_comment,"LOOP_END=")==user_comment) { /* LOOP_START pair */
loop_end = atol(strrchr(user_comment,'=')+1);
}
else if (strstr(user_comment,"ENCODER_OPTIONS=")==user_comment) { /* for detection */
has_encoder_options = 1;
}
else if (strstr(user_comment,"TITLE=")==user_comment) { /* for detection */
has_title = 1;
}
//;VGM_LOG("OggOpus: user_comment=%s\n", user_comment);
offset += 0x04 + user_comment_size;
}
/* Monster Boy has loop points for 44100hz (what), but Opus is resampled so
* they must be adjusted (with extra checks just in case). */
if (loop_flag && original_rate < 48000 && has_encoder_options && has_title) {
float modifier = 48000.0f / (float)original_rate;
loop_start = loop_start * modifier;
loop_end = loop_end * modifier;
}
}
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count, loop_flag);
if (!vgmstream) goto fail;
vgmstream->meta_type = meta_OGG_OPUS;
vgmstream->sample_rate = 48000; /* Opus always resamples to this */
vgmstream->num_samples = ogg_get_num_samples(streamFile, 0x00);
vgmstream->loop_start_sample = loop_start;
vgmstream->loop_end_sample = loop_end;
#ifdef VGM_USE_FFMPEG
{
vgmstream->codec_data = init_ffmpeg_offset(streamFile, start_offset, get_streamfile_size(streamFile));
if (!vgmstream->codec_data) goto fail;
vgmstream->coding_type = coding_FFmpeg;
vgmstream->layout_type = layout_none;
/* FFmpeg+libopus handles skip samples ok, FFmpeg+opus doesn't */
}
#else
goto fail;
#endif
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
goto fail;
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;
}
/* parse OggS's bizarre segment table */
static int get_ogg_page_size(STREAMFILE *streamFile, off_t page_offset, off_t *out_data_offset, size_t *out_page_size) {
uint8_t segments;
size_t page_size = 0;
int i;
if (read_32bitBE(page_offset+0x00,streamFile) != 0x4F676753) /* "OggS" */
goto fail;
/* read all segment sizes */
segments = (uint8_t)read_8bit(page_offset+0x1a, streamFile);
for (i = 0; i < segments; i++) {
page_size += (uint8_t)read_8bit(page_offset + 0x1b + i, streamFile);
}
page_size += 0x1b + segments;
if (out_data_offset) *out_data_offset = page_offset + 0x1b + segments;
if (out_page_size) *out_page_size = page_size;
return 1;
fail:
return 0;
}
/* Ogg doesn't have num_samples info, must manually seek+read last granule
* (Xiph is insistent this is the One True Way). */
static int ogg_get_num_samples(STREAMFILE *streamFile, off_t start_offset) {
uint32_t expected_id = 0x4F676753;
off_t offset = get_streamfile_size(streamFile) - 0x04-0x01-0x01-0x08-0x04-0x04-0x04;
//todo better buffer reads (Ogg page max is 0xFFFF)
//lame way to force buffer, assuming it's around that
read_32bitBE(offset - 0x4000, streamFile);
while (offset >= start_offset) {
uint32_t current_id = read_32bitBE(offset, streamFile);
if (current_id == expected_id) { /* if more checks are needed last page starts with 0x0004 */
return read_32bitLE(offset+0x04+0x01+0x01, streamFile); /* get last granule = total samples (64b but whatevs) */
}
offset--;
}
return 0;
}

View File

@ -57,7 +57,6 @@ static VGMSTREAM * init_vgmstream_opus(STREAMFILE *streamFile, meta_t meta_type,
goto fail;
#endif
/* open the file for reading */
if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) )
goto fail;
return vgmstream;
@ -308,3 +307,36 @@ VGMSTREAM * init_vgmstream_opus_sps_n1(STREAMFILE *streamFile) {
fail:
return NULL;
}
/* AQUASTYLE wrapper [Touhou Genso Wanderer -Reloaded- (Switch)] */
VGMSTREAM * init_vgmstream_opus_opusx(STREAMFILE *streamFile) {
off_t offset;
int num_samples, loop_start = 0, loop_end = 0;
float modifier;
/* checks */
if (!check_extensions(streamFile, "opusx"))
goto fail;
if (read_32bitBE(0x00, streamFile) != 0x4F505553) /* "OPUS" */
goto fail;
offset = 0x10;
/* values are for the original 44100 files, but Opus resamples to 48000 */
modifier = 48000.0f / 44100.0f;
num_samples = 0;//read_32bitLE(0x04, streamFile) * modifier; /* better use calc'd num_samples */
loop_start = read_32bitLE(0x08, streamFile) * modifier;
loop_end = read_32bitLE(0x0c, streamFile) * modifier;
/* resampling calcs are slighly off and may to over num_samples, but by removing delay seems ok */
if (loop_start >= 120) {
loop_start -= 128;
loop_end -= 128;
}
else {
loop_end = 0;
}
return init_vgmstream_opus(streamFile, meta_OPUS, offset, num_samples, loop_start, loop_end);
fail:
return NULL;
}

View File

@ -1,66 +0,0 @@
#include "meta.h"
#include "../util.h"
/* FAG (Jackie Chan - Stuntmaster) */
VGMSTREAM * init_vgmstream_psx_fag(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
char filename[PATH_LIMIT];
off_t start_offset;
int loop_flag = 0;
int channel_count;
/* check extension, case insensitive */
streamFile->get_name(streamFile,filename,sizeof(filename));
if (strcasecmp("fag",filename_extension(filename))) goto fail;
/* check header */
/* Look if there's more than 1 one file... */
if (read_32bitBE(0x00,streamFile) != 0x01000000)
goto fail;
loop_flag = 0;
channel_count = 2;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail;
/* fill in the vital statistics */
start_offset = read_32bitLE(0x04,streamFile);
vgmstream->channels = channel_count;
vgmstream->sample_rate = 24000;
vgmstream->coding_type = coding_PSX;
vgmstream->num_samples = (read_32bitLE(0x08,streamFile))/channel_count/32*28;
if (loop_flag) {
vgmstream->loop_start_sample = 0;
vgmstream->loop_end_sample = (read_32bitLE(0x08,streamFile))/channel_count/32*28;
}
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = 0x8000;
vgmstream->meta_type = meta_PSX_FAG;
/* open the file for reading */
{
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;
vgmstream->ch[i].channel_start_offset=
vgmstream->ch[i].offset=start_offset+
vgmstream->interleave_block_size*i;
}
}
return vgmstream;
/* clean up anything we may have opened */
fail:
if (vgmstream) close_vgmstream(vgmstream);
return NULL;
}

View File

@ -808,7 +808,7 @@ static int parse_num(STREAMFILE * streamFile, txth_header * txth, const char * v
if (sscanf(val, hex ? "@%x" : "@%u", &offset) != 1) goto fail;
}
if (offset < 0 || offset > get_streamfile_size(streamFile))
if (/*offset < 0 ||*/ offset > get_streamfile_size(streamFile))
goto fail;
if (ed1 == 'B' && ed2 == 'E')

View File

@ -25,8 +25,8 @@ typedef struct {
size_t entry_count;
size_t entry_max;
size_t loop_start_segment;
size_t loop_end_segment;
uint32_t loop_start_segment;
uint32_t loop_end_segment;
size_t is_layered;
} txtp_header;

View File

@ -3,7 +3,7 @@
#include "../coding/coding.h"
/* CD-XA - from Sony PS1 CDs */
VGMSTREAM * init_vgmstream_cdxa(STREAMFILE *streamFile) {
VGMSTREAM * init_vgmstream_xa(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
off_t start_offset;
int loop_flag = 0, channel_count, sample_rate;
@ -145,7 +145,7 @@ VGMSTREAM * init_vgmstream_cdxa(STREAMFILE *streamFile) {
vgmstream->sample_rate = sample_rate;
vgmstream->meta_type = meta_PSX_XA;
vgmstream->meta_type = meta_XA;
vgmstream->coding_type = coding_XA;
vgmstream->layout_type = is_blocked ? layout_blocked_xa : layout_none;

222
src/meta/zsnd.c Normal file
View File

@ -0,0 +1,222 @@
#include "meta.h"
#include "../coding/coding.h"
#include "zsnd_streamfile.h"
/* ZSND - Vicarious Visions games [X-Men Legends II (multi), Marvel Ultimate Alliance (multi)] */
VGMSTREAM * init_vgmstream_zsnd(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
STREAMFILE *temp_streamFile = NULL;
off_t start_offset, name_offset;
size_t stream_size, name_size;
int loop_flag, channel_count, sample_rate, layers;
uint32_t codec;
int total_subsongs, target_subsong = streamFile->stream_index;
int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL;
int16_t (*read_16bit)(off_t,STREAMFILE*) = NULL;
/* checks */
/* .zss/zsm: standard, .ens/enm: same for PS2 */
if (!check_extensions(streamFile, "zss,zsm,ens,enm"))
goto fail;
if (read_32bitBE(0x00,streamFile) != 0x5A534E44) /* "ZSND" */
goto fail;
/* probably zss=stream, zsm=memory; no diffs other than size */
codec = read_32bitBE(0x04,streamFile);
/* 0x08: file size, but slightly bigger (+0x01~04) in some platforms */
/* 0x0c: header end/first stream start (unneeded as all offsets are absolute) */
if (codec == 0x47435542) { /* "GCUB" */
read_32bit = read_32bitBE;
read_16bit = read_16bitBE;
}
else {
read_32bit = read_32bitLE;
read_16bit = read_16bitLE;
}
/* parse header tables (7*0x0c) */
{
off_t header2_offset, header3_offset;
/* table2: stream head */
int table2_entries = read_32bit(0x1c,streamFile);
//off_t table2_head = read_32bit(0x20,streamFile);
off_t table2_body = read_32bit(0x24,streamFile);
/* table3: stream body */
int table3_entries = read_32bit(0x28,streamFile);
//off_t table3_head = read_32bit(0x2c,streamFile);
off_t table3_body = read_32bit(0x30,streamFile);
/* table1: stream cues? (entry=0x18)
* tables 4-7 seem reserved with 0 entries and offsets to header end,
* though table5 can be seen in boss4_m.zsm (1 entry) */
/* table heads are always 0x08 * entries */
/* 0x00 = ? (varies between tables but consistent between platforms) */
/* 0x04 = id? (also in table2_body at 0x00?) */
/* table1 may have more entries than table2/3 */
if (table2_entries != table3_entries) {
VGM_LOG("ZSND: table2/3 entries don't match\n");
goto fail;
}
total_subsongs = table2_entries;
if (target_subsong == 0) target_subsong = 1;
if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail;
switch (codec) {
case 0x50432020: /* "PC " */
header2_offset = table2_body + 0x18*(target_subsong-1);
header3_offset = table3_body + 0x4c*(target_subsong-1);
layers = read_16bit(header2_offset + 0x02,streamFile);
sample_rate = read_32bit(header2_offset + 0x04,streamFile);
start_offset = read_32bit(header3_offset + 0x00,streamFile);
stream_size = read_32bit(header3_offset + 0x04,streamFile);
name_offset = header3_offset + 0x0c;
name_size = 0x40;
break;
case 0x58424F58: /* "XBOX" */
header2_offset = table2_body + 0x1c*(target_subsong-1);
header3_offset = table3_body + 0x54*(target_subsong-1);
layers = read_16bit(header2_offset + 0x02,streamFile);
sample_rate = read_32bit(header2_offset + 0x04,streamFile);
start_offset = read_32bit(header3_offset + 0x00,streamFile);
stream_size = read_32bit(header3_offset + 0x04,streamFile);
name_offset = header3_offset + 0x14;
name_size = 0x40;
break;
case 0x50533220: /* "PS2 " (also for PSP) */
header2_offset = table2_body + 0x10*(target_subsong-1);
header3_offset = table3_body + 0x08*(target_subsong-1);
sample_rate = read_16bit(header2_offset + 0x02,streamFile);
layers = read_16bit(header2_offset + 0x04,streamFile);
start_offset = read_32bit(header3_offset + 0x00,streamFile);
stream_size = read_32bit(header3_offset + 0x04,streamFile);
name_offset = 0;
name_size = 0;
switch(sample_rate) {
case 0x0800: sample_rate = 22050; break;
case 0x0400: sample_rate = 11025; break;
default:
VGM_LOG("ZSND: unknown sample_rate\n");
goto fail;
}
break;
case 0x47435542: /* "GCUB" (also for Wii) */
header2_offset = table2_body + 0x18*(target_subsong-1);
header3_offset = table3_body + 0x0c*(target_subsong-1);
layers = read_16bit(header2_offset + 0x02,streamFile);
sample_rate = read_32bit(header2_offset + 0x04,streamFile);
start_offset = read_32bit(header3_offset + 0x00,streamFile);
stream_size = read_32bit(header3_offset + 0x04,streamFile);
/* 0x08: "DSP " for some reason */
name_offset = 0;
name_size = 0;
break;
default:
goto fail;
}
/* maybe flags? */
switch (layers) {
case 0x00: channel_count = 1; break;
case 0x01: channel_count = 1; break; /* related to looping? */
case 0x02: channel_count = 2; break;
case 0x22: channel_count = 4; break;
default:
VGM_LOG("ZSND: unknown layers\n");
goto fail;
}
loop_flag = 0;
}
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count, loop_flag);
if (!vgmstream) goto fail;
vgmstream->meta_type = meta_ZSND;
vgmstream->sample_rate = sample_rate;
switch (codec) {
case 0x50432020: /* "PC " */
vgmstream->coding_type = coding_IMA;
vgmstream->layout_type = layout_none;
//todo interleaved stereo (needs to adapt decoder)
//vgmstream->layout_type = layout_interleave; /* interleaved stereo for >2ch*/
//vgmstream->interleave_block_size = 0x2000 * 2 / channel_count;
vgmstream->num_samples = ima_bytes_to_samples(stream_size, channel_count);
break;
case 0x58424F58: /* "XBOX" */
vgmstream->coding_type = coding_XBOX_IMA;
vgmstream->layout_type = layout_interleave; /* interleaved stereo for >2ch*/
vgmstream->interleave_block_size = 0x9000 * 2 / channel_count;
vgmstream->num_samples = xbox_ima_bytes_to_samples(stream_size, channel_count);
break;
case 0x50533220: /* "PS2 " (also for PSP) */
vgmstream->coding_type = coding_PSX;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = 0x800;
vgmstream->num_samples = ps_bytes_to_samples(stream_size, channel_count);
break;
case 0x47435542: /* "GCUB" (also for Wii) */
vgmstream->coding_type = coding_NGC_DSP;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = 0x8000;
/* has a full DSP header, but num_samples may vary slighly between channels, so calc manually */
dsp_read_coefs_be(vgmstream, streamFile, start_offset+0x1c,0x60);
dsp_read_hist_be(vgmstream, streamFile, start_offset+0x40, 0x60);
start_offset += 0x60*channel_count;
stream_size -= 0x60*channel_count;
vgmstream->num_samples = dsp_bytes_to_samples(stream_size, channel_count);
break;
default:
goto fail;
}
vgmstream->num_streams = total_subsongs;
vgmstream->stream_size = stream_size;
if (name_offset) {
read_string(vgmstream->stream_name,name_size, name_offset,streamFile);
}
temp_streamFile = setup_zsnd_streamfile(streamFile, start_offset, stream_size); /* fixes last interleave reads */
if (!temp_streamFile) goto fail;
if (!vgmstream_open_stream(vgmstream,temp_streamFile,start_offset))
goto fail;
close_streamfile(temp_streamFile);
return vgmstream;
fail:
close_streamfile(temp_streamFile);
close_vgmstream(vgmstream);
return NULL;
}

View File

@ -0,0 +1,58 @@
#ifndef _ZSND_STREAMFILE_H_
#define _ZSND_STREAMFILE_H_
#include "../streamfile.h"
typedef struct {
off_t max_offset;
} zsnd_io_data;
static size_t zsnd_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length, zsnd_io_data* data) {
size_t bytes_read, bytes_to_do;
int i;
/* clamp reads */
bytes_to_do = length;
if (offset > data->max_offset)
offset = data->max_offset;
if (offset + length > data->max_offset)
bytes_to_do = data->max_offset - offset;
bytes_read = streamfile->read(streamfile, dest, offset, bytes_to_do);
/* pretend we got data after max_offset */
if (bytes_read < length) {
for (i = bytes_read; i < length; i++) {
dest[i] = 0;
}
bytes_read = length;
}
return bytes_read;
}
/* ZSND removes last interleave data from the file if blank, but codecs still need to read after it.
* This data could be from next stream or from EOF, so return blank data instead. */
static STREAMFILE* setup_zsnd_streamfile(STREAMFILE *streamFile, off_t start_offset, size_t stream_size) {
STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL;
zsnd_io_data io_data = {0};
size_t io_data_size = sizeof(zsnd_io_data);
io_data.max_offset = start_offset + stream_size;
/* setup custom streamfile */
new_streamFile = open_wrap_streamfile(streamFile);
if (!new_streamFile) goto fail;
temp_streamFile = new_streamFile;
new_streamFile = open_io_streamfile(temp_streamFile, &io_data,io_data_size, zsnd_io_read,NULL);
if (!new_streamFile) goto fail;
temp_streamFile = new_streamFile;
return temp_streamFile;
fail:
close_streamfile(temp_streamFile);
return NULL;
}
#endif /* _ZSND_STREAMFILE_H_ */

View File

@ -39,7 +39,7 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = {
init_vgmstream_ps2_ads,
init_vgmstream_ps2_npsf,
init_vgmstream_rwsd,
init_vgmstream_cdxa,
init_vgmstream_xa,
init_vgmstream_ps2_rxws,
init_vgmstream_ps2_rxw,
init_vgmstream_ngc_dsp_stm,
@ -150,7 +150,7 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = {
init_vgmstream_ngc_ymf,
init_vgmstream_sadl,
init_vgmstream_ps2_ccc,
init_vgmstream_psx_fag,
init_vgmstream_fag,
init_vgmstream_ps2_mihb,
init_vgmstream_ngc_pdt_split,
init_vgmstream_ngc_pdt,
@ -452,6 +452,10 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = {
init_vgmstream_msf_tamasoft,
init_vgmstream_xps_dat,
init_vgmstream_xps,
init_vgmstream_zsnd,
init_vgmstream_opus_opusx,
init_vgmstream_dsp_adpcmx,
init_vgmstream_ogg_opus,
/* lowest priority metas (should go after all metas, and TXTH should go before raw formats) */
init_vgmstream_txth, /* proper parsers should supersede TXTH, once added */
@ -2187,7 +2191,7 @@ int vgmstream_do_loop(VGMSTREAM * vgmstream) {
/* Write a description of the stream into array pointed by desc, which must be length bytes long.
* Will always be null-terminated if length > 0 */
void describe_vgmstream(VGMSTREAM * vgmstream, char * desc, int length) {
#define TEMPSIZE 256
#define TEMPSIZE (256+32)
char temp[TEMPSIZE];
const char* description;

View File

@ -322,7 +322,7 @@ typedef enum {
meta_HIS, /* Her Ineractive .his */
meta_BNSF, /* Bandai Namco Sound Format */
meta_PSX_XA, /* CD-ROM XA */
meta_XA, /* CD-ROM XA */
meta_PS2_SShd, /* .ADS with SShd header */
meta_PS2_NPSF, /* Namco Production Sound File */
meta_PS2_RXWS, /* Sony games (Genji, Okage Shadow King, Arc The Lad Twilight of Spirits) */
@ -406,7 +406,7 @@ typedef enum {
meta_NGC_YMF, /* WWE WrestleMania X8 */
meta_SADL, /* .sad */
meta_PS2_CCC, /* Tokyo Xtreme Racer DRIFT 2 */
meta_PSX_FAG, /* Jackie Chan - Stuntmaster */
meta_FAG, /* Jackie Chan - Stuntmaster */
meta_PS2_MIHB, /* Merged MIH+MIB */
meta_NGC_PDT, /* Mario Party 6 */
meta_DC_ASD, /* Miss Moonligh */
@ -707,6 +707,9 @@ typedef enum {
meta_XPCM,
meta_MSF_TAMASOFT,
meta_XPS_DAT,
meta_ZSND,
meta_DSP_ADPCMX,
meta_OGG_OPUS
} meta_t;