mirror of
https://github.com/vgmstream/vgmstream.git
synced 2024-11-13 18:20:50 +01:00
Fix .vsv/psh decoding and mono files [Dawn of Mana (PS2)]
This commit is contained in:
parent
28c27bc12a
commit
78099c096b
@ -308,7 +308,7 @@ static const char* extension_list[] = {
|
|||||||
"pona",
|
"pona",
|
||||||
"pos",
|
"pos",
|
||||||
"ps2stm", //fake extension for .stm (renamed? to be removed?)
|
"ps2stm", //fake extension for .stm (renamed? to be removed?)
|
||||||
"psh", // fake extension for VSV(?) Dawn of Mana needs to be checked again
|
"psh", //fake extension for .vsv (to be removed)
|
||||||
"psnd",
|
"psnd",
|
||||||
"psw", //fake extension for .wam (renamed, to be removed)
|
"psw", //fake extension for .wam (renamed, to be removed)
|
||||||
|
|
||||||
@ -452,7 +452,7 @@ static const char* extension_list[] = {
|
|||||||
"vpk",
|
"vpk",
|
||||||
"vs",
|
"vs",
|
||||||
"vsf",
|
"vsf",
|
||||||
"vsv", // official extension for PSH? TODO: recheck Dawn of Mana
|
"vsv",
|
||||||
"vxn",
|
"vxn",
|
||||||
|
|
||||||
"waa",
|
"waa",
|
||||||
@ -833,7 +833,7 @@ static const meta_info meta_info_list[] = {
|
|||||||
{meta_MUS_ACM, "InterPlay MUS ACM header"},
|
{meta_MUS_ACM, "InterPlay MUS ACM header"},
|
||||||
{meta_PS2_KCES, "Konami KCES Header"},
|
{meta_PS2_KCES, "Konami KCES Header"},
|
||||||
{meta_PS2_DXH, "Tokobot Plus DXH Header"},
|
{meta_PS2_DXH, "Tokobot Plus DXH Header"},
|
||||||
{meta_PS2_PSH, "Square Enix PSH/VSV Header"},
|
{meta_VSV, "Square Enix .vsv Header"},
|
||||||
{meta_RIFF_WAVE_labl, "RIFF WAVE header with loop markers"},
|
{meta_RIFF_WAVE_labl, "RIFF WAVE header with loop markers"},
|
||||||
{meta_RIFF_WAVE_smpl, "RIFF WAVE header with sample looping info"},
|
{meta_RIFF_WAVE_smpl, "RIFF WAVE header with sample looping info"},
|
||||||
{meta_RIFF_WAVE_wsmp, "RIFF WAVE header with wsmp looping info"},
|
{meta_RIFF_WAVE_wsmp, "RIFF WAVE header with wsmp looping info"},
|
||||||
|
@ -227,7 +227,7 @@ VGMSTREAM * init_vgmstream_ps2_kces(STREAMFILE * streamFile);
|
|||||||
|
|
||||||
VGMSTREAM * init_vgmstream_ps2_dxh(STREAMFILE * streamFile);
|
VGMSTREAM * init_vgmstream_ps2_dxh(STREAMFILE * streamFile);
|
||||||
|
|
||||||
VGMSTREAM * init_vgmstream_ps2_psh(STREAMFILE * streamFile);
|
VGMSTREAM * init_vgmstream_vsv(STREAMFILE * streamFile);
|
||||||
|
|
||||||
VGMSTREAM * init_vgmstream_mus_acm(STREAMFILE * streamFile);
|
VGMSTREAM * init_vgmstream_mus_acm(STREAMFILE * streamFile);
|
||||||
|
|
||||||
|
@ -3,60 +3,73 @@
|
|||||||
#include "ps2_psh_streamfile.h"
|
#include "ps2_psh_streamfile.h"
|
||||||
|
|
||||||
|
|
||||||
/* PSH/VSV - from Square Enix games [Dawn of Mana: Seiken Densetsu 4 (PS2), Kingdom Hearts Re:Chain of Memories (PS2)] */
|
/* .VSV - from Square Enix games [Dawn of Mana: Seiken Densetsu 4 (PS2), Kingdom Hearts Re:Chain of Memories (PS2)] */
|
||||||
VGMSTREAM * init_vgmstream_ps2_psh(STREAMFILE *streamFile) {
|
VGMSTREAM * init_vgmstream_vsv(STREAMFILE *streamFile) {
|
||||||
VGMSTREAM * vgmstream = NULL;
|
VGMSTREAM * vgmstream = NULL;
|
||||||
STREAMFILE *temp_streamFile = NULL;
|
STREAMFILE *temp_streamFile = NULL;
|
||||||
off_t start_offset;
|
off_t start_offset;
|
||||||
int loop_flag, channel_count;
|
int loop_flag, channel_count, flags, sample_rate, is_rs;
|
||||||
size_t loop_start, adjust, data_size, interleave;
|
size_t loop_start, adjust, data_size, interleave;
|
||||||
|
|
||||||
|
|
||||||
/* checks */
|
/* checks */
|
||||||
/* .psh: assumed? [Romancing SaGa: Minstrel's Song (PS2)]
|
/* .vsv: extension from internal filenames [KH Re:CoM (PS2), DoM (PS2), KH HD I.5 + II.5 ReMIX (PS4)]
|
||||||
* .vsv: official? [Kingdom Hearts HD I.5 + II.5 ReMIX (PS4)] */
|
* .psh: fake */
|
||||||
if (!check_extensions(streamFile, "psh,vsv"))
|
if (!check_extensions(streamFile, "vsv,psh"))
|
||||||
goto fail;
|
|
||||||
/* 0x00(2): 0x0000 (RS:MS) / 0x6440 (KH:RCoM) / varies (DoM) */
|
|
||||||
if ((uint16_t)read_16bitBE(0x02,streamFile) != 0x6400)
|
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
||||||
channel_count = 2;
|
/* 0x00(1x4): flags/config? */
|
||||||
|
if ((uint8_t)read_8bit(0x03,streamFile) > 0x64) /* possibly volume */
|
||||||
|
goto fail;
|
||||||
|
if ((uint8_t)read_8bit(0x0a,streamFile) != 0) /* not seen */
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
/* Romancing SaGa (PS2) uses an earlier? version, this seems to work */
|
||||||
|
is_rs = ((uint16_t)read_16bitLE(0x00,streamFile) == 0);
|
||||||
|
|
||||||
start_offset = 0x00; /* correct, but needs some tricks to fix sound (see below) */
|
start_offset = 0x00; /* correct, but needs some tricks to fix sound (see below) */
|
||||||
interleave = 0x800;
|
interleave = 0x800;
|
||||||
|
|
||||||
adjust = (uint16_t)read_16bitLE(0x04,streamFile) & 0x7FF; /* upper bits = ??? */
|
adjust = (uint16_t)read_16bitLE(0x04,streamFile);
|
||||||
data_size = (uint16_t)read_16bitLE(0x0c,streamFile) * interleave;
|
loop_start = ((uint16_t)read_16bitLE(0x06,streamFile) & 0x7FFF) * interleave;
|
||||||
/* 0x0e: ? (may be 0x0001, or a low-ish value, not related to looping?) */
|
loop_flag = (uint16_t)read_16bitLE(0x06,streamFile) & 0x8000; /* loop_start != 0 works too, no files loop from beginning to end */
|
||||||
loop_start = ((uint16_t)read_16bitLE(0x06,streamFile) & 0x7FFF) * interleave; /* uper bit == loop flag? */
|
sample_rate = (uint16_t)read_16bitLE(0x08,streamFile);
|
||||||
loop_flag = (loop_start != 0); /* (no known files loop from beginning to end) */
|
flags = (uint8_t)read_8bit (0x0b,streamFile); /* values: 0x01=stereo, 0x10=mono */
|
||||||
|
data_size = (uint16_t)read_16bitLE(0x0c,streamFile) * interleave;
|
||||||
|
/* 0x0e: ? (may be a low-ish value) */
|
||||||
|
|
||||||
|
channel_count = (flags & 1) ? 2 : 1;
|
||||||
|
|
||||||
|
/* must discard to avoid wrong loops and unwanted data (easier to see in voices) */
|
||||||
|
if (!is_rs) { /* RS doesn't do this */
|
||||||
|
/* adjust & 0xF800 is unknown (values=0x0000|0x0800|0xF800, can be mono/stereo, loop/no, adjust/no) */
|
||||||
|
size_t discard = adjust & 0x07FF;
|
||||||
|
/* at (file_end - 0x800 + discard) is a 0x03 PS flag to check this (adjust 0 does discard full block) */
|
||||||
|
data_size -= (0x800 - discard) * channel_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* build the VGMSTREAM */
|
/* build the VGMSTREAM */
|
||||||
vgmstream = allocate_vgmstream(channel_count,loop_flag);
|
vgmstream = allocate_vgmstream(channel_count, loop_flag);
|
||||||
if (!vgmstream) goto fail;
|
if (!vgmstream) goto fail;
|
||||||
|
|
||||||
vgmstream->meta_type = meta_PS2_PSH;
|
vgmstream->meta_type = meta_VSV;
|
||||||
vgmstream->sample_rate = (uint16_t)read_16bitLE(0x08,streamFile);
|
vgmstream->sample_rate = sample_rate;
|
||||||
vgmstream->num_samples = ps_bytes_to_samples(data_size,channel_count);
|
vgmstream->num_samples = ps_bytes_to_samples(data_size, channel_count);
|
||||||
|
vgmstream->loop_start_sample = ps_bytes_to_samples(loop_start, channel_count);
|
||||||
vgmstream->loop_start_sample = ps_bytes_to_samples(loop_start,channel_count);
|
|
||||||
vgmstream->loop_end_sample = vgmstream->num_samples;
|
vgmstream->loop_end_sample = vgmstream->num_samples;
|
||||||
|
|
||||||
/* loops are odd, but comparing the audio wave with the OSTs these values seem correct */
|
/* these loops are odd, but comparing the audio wave with the OSTs values seem correct */
|
||||||
if (adjust == 0) { /* Romancing SaGa (PS2) */
|
if (is_rs) {
|
||||||
vgmstream->loop_start_sample -= ps_bytes_to_samples(channel_count*interleave,channel_count); /* maybe *before* loop block? */
|
vgmstream->loop_start_sample -= ps_bytes_to_samples(channel_count*interleave,channel_count); /* maybe *before* loop block? */
|
||||||
vgmstream->loop_start_sample -= ps_bytes_to_samples(0x200*channel_count,channel_count); /* maybe default adjust? */
|
vgmstream->loop_start_sample -= ps_bytes_to_samples(0x200*channel_count,channel_count); /* maybe default adjust? */
|
||||||
}
|
}
|
||||||
else { /* all others */
|
|
||||||
vgmstream->loop_end_sample -= ps_bytes_to_samples((0x800 - adjust)*channel_count,channel_count); /* at last block + adjust is a 0x03 flag */
|
|
||||||
}
|
|
||||||
|
|
||||||
vgmstream->coding_type = coding_PSX;
|
vgmstream->coding_type = coding_PSX;
|
||||||
vgmstream->layout_type = layout_interleave;
|
vgmstream->layout_type = layout_interleave;
|
||||||
vgmstream->interleave_block_size = interleave;
|
vgmstream->interleave_block_size = interleave;
|
||||||
|
|
||||||
temp_streamFile = setup_ps2_psh_streamfile(streamFile, start_offset, data_size);
|
temp_streamFile = setup_vsv_streamfile(streamFile, start_offset, data_size);
|
||||||
if (!temp_streamFile) goto fail;
|
if (!temp_streamFile) goto fail;
|
||||||
|
|
||||||
if (!vgmstream_open_stream(vgmstream, temp_streamFile, start_offset))
|
if (!vgmstream_open_stream(vgmstream, temp_streamFile, start_offset))
|
||||||
|
@ -1,31 +1,36 @@
|
|||||||
#ifndef _PS2_PSH_STREAMFILE_H_
|
#ifndef _VSV_STREAMFILE_H_
|
||||||
#define _PS2_PSH_STREAMFILE_H_
|
#define _VSV_STREAMFILE_H_
|
||||||
#include "../streamfile.h"
|
#include "../streamfile.h"
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
off_t null_offset;
|
off_t null_offset;
|
||||||
} ps2_psh_io_data;
|
} vsv_io_data;
|
||||||
|
|
||||||
static size_t ps2_psh_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length, ps2_psh_io_data* data) {
|
static size_t vsv_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length, vsv_io_data* data) {
|
||||||
size_t bytes_read;
|
size_t bytes_read;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
bytes_read = streamfile->read(streamfile, dest, offset, length);
|
bytes_read = streamfile->read(streamfile, dest, offset, length);
|
||||||
|
|
||||||
/* PSHs do start at 0x00, but first line is also the header; must null it to avoid clicks */
|
/* VSVs do start at 0x00, but first line is also the header; must null it to avoid clicks */
|
||||||
if (offset < data->null_offset) {
|
if (offset < data->null_offset) {
|
||||||
for (i = 0; i < data->null_offset - offset; i++) {
|
int max = data->null_offset - offset;
|
||||||
|
if (max > bytes_read)
|
||||||
|
max = bytes_read;
|
||||||
|
|
||||||
|
for (i = 0; i < max; i++) {
|
||||||
dest[i] = 0;
|
dest[i] = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/* VSV also has last 0x800 block with a PS-ADPCM flag of 0x10 (incorrect), but it's ignored by the decoder */
|
||||||
|
|
||||||
return bytes_read;
|
return bytes_read;
|
||||||
}
|
}
|
||||||
|
|
||||||
static STREAMFILE* setup_ps2_psh_streamfile(STREAMFILE *streamFile, off_t start_offset, size_t data_size) {
|
static STREAMFILE* setup_vsv_streamfile(STREAMFILE *streamFile, off_t start_offset, size_t data_size) {
|
||||||
STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL;
|
STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL;
|
||||||
ps2_psh_io_data io_data = {0};
|
vsv_io_data io_data = {0};
|
||||||
size_t io_data_size = sizeof(ps2_psh_io_data);
|
size_t io_data_size = sizeof(vsv_io_data);
|
||||||
|
|
||||||
io_data.null_offset = 0x10;
|
io_data.null_offset = 0x10;
|
||||||
|
|
||||||
@ -34,7 +39,7 @@ static STREAMFILE* setup_ps2_psh_streamfile(STREAMFILE *streamFile, off_t start_
|
|||||||
if (!new_streamFile) goto fail;
|
if (!new_streamFile) goto fail;
|
||||||
temp_streamFile = new_streamFile;
|
temp_streamFile = new_streamFile;
|
||||||
|
|
||||||
new_streamFile = open_io_streamfile(temp_streamFile, &io_data,io_data_size, ps2_psh_io_read,NULL);
|
new_streamFile = open_io_streamfile(temp_streamFile, &io_data,io_data_size, vsv_io_read,NULL);
|
||||||
if (!new_streamFile) goto fail;
|
if (!new_streamFile) goto fail;
|
||||||
temp_streamFile = new_streamFile;
|
temp_streamFile = new_streamFile;
|
||||||
|
|
||||||
@ -45,4 +50,4 @@ fail:
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* _PS2_PSH_STREAMFILE_H_ */
|
#endif /* _VSV_STREAMFILE_H_ */
|
||||||
|
@ -114,7 +114,7 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = {
|
|||||||
init_vgmstream_mus_acm,
|
init_vgmstream_mus_acm,
|
||||||
init_vgmstream_ps2_kces,
|
init_vgmstream_ps2_kces,
|
||||||
init_vgmstream_ps2_dxh,
|
init_vgmstream_ps2_dxh,
|
||||||
init_vgmstream_ps2_psh,
|
init_vgmstream_vsv,
|
||||||
init_vgmstream_scd_pcm,
|
init_vgmstream_scd_pcm,
|
||||||
init_vgmstream_ps2_pcm,
|
init_vgmstream_ps2_pcm,
|
||||||
init_vgmstream_ps2_rkv,
|
init_vgmstream_ps2_rkv,
|
||||||
|
@ -371,7 +371,7 @@ typedef enum {
|
|||||||
meta_PS2_RSTM, /* Midnight Club 3 */
|
meta_PS2_RSTM, /* Midnight Club 3 */
|
||||||
meta_PS2_KCES, /* Dance Dance Revolution */
|
meta_PS2_KCES, /* Dance Dance Revolution */
|
||||||
meta_PS2_DXH, /* Tokobot Plus - Myteries of the Karakuri */
|
meta_PS2_DXH, /* Tokobot Plus - Myteries of the Karakuri */
|
||||||
meta_PS2_PSH, /* Square Enix PSH/VSV (Dawn of Mana/KH Re:CoM) */
|
meta_VSV,
|
||||||
meta_SCD_PCM, /* Lunar - Eternal Blue */
|
meta_SCD_PCM, /* Lunar - Eternal Blue */
|
||||||
meta_PS2_PCM, /* Konami KCEJ East: Ephemeral Fantasia, Yu-Gi-Oh! The Duelists of the Roses, 7 Blades */
|
meta_PS2_PCM, /* Konami KCEJ East: Ephemeral Fantasia, Yu-Gi-Oh! The Duelists of the Roses, 7 Blades */
|
||||||
meta_PS2_RKV, /* Legacy of Kain - Blood Omen 2 (PS2) */
|
meta_PS2_RKV, /* Legacy of Kain - Blood Omen 2 (PS2) */
|
||||||
|
Loading…
Reference in New Issue
Block a user