mirror of
https://github.com/vgmstream/vgmstream.git
synced 2025-02-20 12:31:00 +01:00
Add Vicarious Visions .zss/zsm/ens/enm [X-Men Legends II (multi)]
This commit is contained in:
parent
95c6381b42
commit
60945577c0
@ -128,7 +128,9 @@ static const char* extension_list[] = {
|
||||
"e4x",
|
||||
"eam",
|
||||
"emff",
|
||||
"enm",
|
||||
"eno",
|
||||
"ens",
|
||||
"enth",
|
||||
"exa",
|
||||
"ezw",
|
||||
@ -483,6 +485,8 @@ static const char* extension_list[] = {
|
||||
"ymf",
|
||||
|
||||
"zsd",
|
||||
"zsm",
|
||||
"zss",
|
||||
"zwdsp",
|
||||
|
||||
"vgmstream" /* fake extension, catch-all for FFmpeg/txth/etc */
|
||||
@ -1115,6 +1119,7 @@ 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"},
|
||||
|
||||
};
|
||||
|
||||
|
@ -279,6 +279,10 @@
|
||||
<File
|
||||
RelativePath=".\meta\xvag_streamfile.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\meta\zsnd_streamfile.h"
|
||||
>
|
||||
</File>
|
||||
</Filter>
|
||||
<Filter
|
||||
@ -1662,6 +1666,10 @@
|
||||
RelativePath=".\meta\zsd.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\meta\zsnd.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\meta\zwdsp.c"
|
||||
>
|
||||
|
@ -490,6 +490,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" />
|
||||
|
@ -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>
|
||||
@ -1015,6 +1018,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>
|
||||
|
@ -813,4 +813,6 @@ 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);
|
||||
|
||||
#endif /*_META_H*/
|
||||
|
222
src/meta/zsnd.c
Normal file
222
src/meta/zsnd.c
Normal 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;
|
||||
}
|
58
src/meta/zsnd_streamfile.h
Normal file
58
src/meta/zsnd_streamfile.h
Normal 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_ */
|
@ -452,6 +452,7 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = {
|
||||
init_vgmstream_msf_tamasoft,
|
||||
init_vgmstream_xps_dat,
|
||||
init_vgmstream_xps,
|
||||
init_vgmstream_zsnd,
|
||||
|
||||
/* 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 */
|
||||
|
@ -707,6 +707,7 @@ typedef enum {
|
||||
meta_XPCM,
|
||||
meta_MSF_TAMASOFT,
|
||||
meta_XPS_DAT,
|
||||
meta_ZSND,
|
||||
|
||||
} meta_t;
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user