mirror of
https://github.com/vgmstream/vgmstream.git
synced 2024-11-28 00:20:47 +01:00
Add Pivotal .psf and decoder [The Great Escape, Conflict: Desert Storm]
This commit is contained in:
parent
7f8138fbb5
commit
3bac417a8c
@ -191,7 +191,7 @@ left together.
|
||||
|
||||
Similarly some formats split header and/or data in separate files (.sgh+sgd,
|
||||
.wav.str+.wav, (file)_L.dsp+(file)_R.dsp, etc). vgmstream will also detect
|
||||
and use those as needed and must be tegether, even if only one of the two
|
||||
and use those as needed and must be together, even if only one of the two
|
||||
will be used to play.
|
||||
|
||||
.pos is a small file with 32 bit little endian values: loop start sample
|
||||
@ -322,7 +322,7 @@ are used in few games.
|
||||
- Nintendo AFC ADPCM
|
||||
- ITU-T G.721
|
||||
- CD-ROM XA ADPCM
|
||||
- Sony PSX ADPCM a.k.a VAG (standard, badflags, configurable)
|
||||
- Sony PSX ADPCM a.k.a VAG (standard, badflags, configurable, Pivotal)
|
||||
- Sony HEVAG
|
||||
- Electronic Arts EA-XA (stereo, mono, Maxis)
|
||||
- Electronic Arts EA-XAS (v0, v1)
|
||||
|
@ -83,6 +83,7 @@ size_t pcm_bytes_to_samples(size_t bytes, int channels, int bits_per_sample);
|
||||
/* psx_decoder */
|
||||
void decode_psx(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int is_badflags);
|
||||
void decode_psx_configurable(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int frame_size);
|
||||
void decode_psx_pivotal(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int frame_size);
|
||||
int ps_find_loop_offsets(STREAMFILE *streamFile, off_t start_offset, size_t data_size, int channels, size_t interleave, int32_t * out_loop_start, int32_t * out_loop_end);
|
||||
int ps_find_loop_offsets_full(STREAMFILE *streamFile, off_t start_offset, size_t data_size, int channels, size_t interleave, int32_t * out_loop_start, int32_t * out_loop_end);
|
||||
size_t ps_find_padding(STREAMFILE *streamFile, off_t start_offset, size_t data_size, int channels, size_t interleave, int discard_empty);
|
||||
|
@ -3,11 +3,11 @@
|
||||
|
||||
/* PS-ADPCM table, defined as rational numbers (as in the spec) */
|
||||
static const double ps_adpcm_coefs_f[5][2] = {
|
||||
{ 0.0 , 0.0 },
|
||||
{ 60.0 / 64.0 , 0.0 },
|
||||
{ 115.0 / 64.0 , -52.0 / 64.0 },
|
||||
{ 98.0 / 64.0 , -55.0 / 64.0 },
|
||||
{ 122.0 / 64.0 , -60.0 / 64.0 },
|
||||
{ 0.0 , 0.0 }, //{ 0.0 , 0.0 },
|
||||
{ 0.9375 , 0.0 }, //{ 60.0 / 64.0 , 0.0 },
|
||||
{ 1.796875 , -0.8125 }, //{ 115.0 / 64.0 , -52.0 / 64.0 },
|
||||
{ 1.53125 , -0.859375 }, //{ 98.0 / 64.0 , -55.0 / 64.0 },
|
||||
{ 1.90625 , -0.9375 }, //{ 122.0 / 64.0 , -60.0 / 64.0 },
|
||||
};
|
||||
|
||||
/* PS-ADPCM table, defined as spec_coef*64 (for int implementations) */
|
||||
@ -101,7 +101,7 @@ void decode_psx(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing
|
||||
|
||||
|
||||
/* PS-ADPCM with configurable frame size and no flag (int math version).
|
||||
* Found in some PC/PS3 games (FF XI in sizes 3/5/9/41, Afrika in size 4, Blur/James Bond in size 33, etc).
|
||||
* Found in some PC/PS3 games (FF XI in sizes 0x3/0x5/0x9/0x41, Afrika in size 0x4, Blur/James Bond in size 0x33, etc).
|
||||
*
|
||||
* Uses int math to decode, which seems more likely (based on FF XI PC's code in Moogle Toolbox). */
|
||||
void decode_psx_configurable(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int frame_size) {
|
||||
@ -114,7 +114,7 @@ void decode_psx_configurable(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int c
|
||||
|
||||
/* external interleave (variable size), mono */
|
||||
bytes_per_frame = frame_size;
|
||||
samples_per_frame = (bytes_per_frame - 0x01) * 2; /* always 28 */
|
||||
samples_per_frame = (bytes_per_frame - 0x01) * 2;
|
||||
frames_in = first_sample / samples_per_frame;
|
||||
first_sample = first_sample % samples_per_frame;
|
||||
|
||||
@ -152,6 +152,56 @@ void decode_psx_configurable(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int c
|
||||
stream->adpcm_history2_32 = hist2;
|
||||
}
|
||||
|
||||
/* PS-ADPCM from Pivotal games, exactly like psx_cfg but with float math (reverse engineered from the exe) */
|
||||
void decode_psx_pivotal(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int frame_size) {
|
||||
off_t frame_offset;
|
||||
int i, frames_in, sample_count = 0;
|
||||
size_t bytes_per_frame, samples_per_frame;
|
||||
uint8_t coef_index, shift_factor;
|
||||
int32_t hist1 = stream->adpcm_history1_32;
|
||||
int32_t hist2 = stream->adpcm_history2_32;
|
||||
float scale;
|
||||
|
||||
/* external interleave (variable size), mono */
|
||||
bytes_per_frame = frame_size;
|
||||
samples_per_frame = (bytes_per_frame - 0x01) * 2;
|
||||
frames_in = first_sample / samples_per_frame;
|
||||
first_sample = first_sample % samples_per_frame;
|
||||
|
||||
/* parse frame header */
|
||||
frame_offset = stream->offset + bytes_per_frame*frames_in;
|
||||
coef_index = ((uint8_t)read_8bit(frame_offset+0x00,stream->streamfile) >> 4) & 0xf;
|
||||
shift_factor = ((uint8_t)read_8bit(frame_offset+0x00,stream->streamfile) >> 0) & 0xf;
|
||||
|
||||
VGM_ASSERT_ONCE(coef_index > 5 || shift_factor > 12, "PS-ADPCM: incorrect coefs/shift at %x\n", (uint32_t)frame_offset);
|
||||
if (coef_index > 5) /* just in case */
|
||||
coef_index = 5;
|
||||
if (shift_factor > 12) /* same */
|
||||
shift_factor = 12;
|
||||
scale = (float)(1.0 / (double)(1 << shift_factor));
|
||||
|
||||
/* decode nibbles */
|
||||
for (i = first_sample; i < first_sample + samples_to_do; i++) {
|
||||
int32_t sample = 0;
|
||||
uint8_t nibbles = (uint8_t)read_8bit(frame_offset+0x01+i/2,stream->streamfile);
|
||||
|
||||
sample = !(i&1) ? /* low nibble first */
|
||||
(nibbles >> 0) & 0x0f :
|
||||
(nibbles >> 4) & 0x0f;
|
||||
sample = (int16_t)((sample << 12) & 0xf000); /* 16b sign extend + default scale */
|
||||
sample = sample*scale + ps_adpcm_coefs_f[coef_index][0]*hist1 + ps_adpcm_coefs_f[coef_index][1]*hist2; /* actually substracts negative coefs but whatevs */
|
||||
|
||||
outbuf[sample_count] = clamp16(sample);
|
||||
sample_count += channelspacing;
|
||||
|
||||
hist2 = hist1;
|
||||
hist1 = sample; /* not clamped but no difference */
|
||||
}
|
||||
|
||||
stream->adpcm_history1_32 = hist1;
|
||||
stream->adpcm_history2_32 = hist2;
|
||||
}
|
||||
|
||||
|
||||
/* Find loop samples in PS-ADPCM data and return if the file loops.
|
||||
*
|
||||
@ -339,7 +389,8 @@ size_t ps_bytes_to_samples(size_t bytes, int channels) {
|
||||
}
|
||||
|
||||
size_t ps_cfg_bytes_to_samples(size_t bytes, size_t frame_size, int channels) {
|
||||
return bytes / channels / frame_size * 28;
|
||||
int samples_per_frame = (frame_size - 0x01) * 2;
|
||||
return bytes / channels / frame_size * samples_per_frame;
|
||||
}
|
||||
|
||||
/* test PS-ADPCM frames for correctness */
|
||||
|
@ -333,6 +333,7 @@ static const char* extension_list[] = {
|
||||
"pona",
|
||||
"pos",
|
||||
"ps2stm", //fake extension for .stm (renamed? to be removed?)
|
||||
"psf",
|
||||
"psh", //fake extension for .vsv (to be removed)
|
||||
"psnd",
|
||||
"psw", //fake extension for .wam (renamed, to be removed)
|
||||
@ -616,6 +617,7 @@ static const coding_info coding_info_list[] = {
|
||||
{coding_PSX, "Playstation 4-bit ADPCM"},
|
||||
{coding_PSX_badflags, "Playstation 4-bit ADPCM (bad flags)"},
|
||||
{coding_PSX_cfg, "Playstation 4-bit ADPCM (configurable)"},
|
||||
{coding_PSX_pivotal, "Playstation 4-bit ADPCM (Pivotal)"},
|
||||
{coding_HEVAG, "Sony HEVAG 4-bit ADPCM"},
|
||||
|
||||
{coding_EA_XA, "Electronic Arts EA-XA 4-bit ADPCM v1"},
|
||||
@ -963,7 +965,6 @@ static const meta_info meta_info_list[] = {
|
||||
{meta_PS2_VGV, "Rune: Viking Warlord VGV Header"},
|
||||
{meta_NGC_GCUB, "GCub Header"},
|
||||
{meta_NGC_SCK_DSP, "The Scorpion King SCK Header"},
|
||||
{meta_NGC_SWD, "PSF + Standard DSP Headers"},
|
||||
{meta_CAFF, "Apple Core Audio Format File header"},
|
||||
{meta_PC_MXST, "Lego Island MxSt Header"},
|
||||
{meta_SAB, "Team17 SAB header"},
|
||||
@ -1187,6 +1188,7 @@ static const meta_info meta_info_list[] = {
|
||||
{meta_SMACKER, "RAD Game Tools SMACKER header"},
|
||||
{meta_MZRT, "id Software MZRT header"},
|
||||
{meta_XAVS, "Reflections XAVS header"},
|
||||
{meta_PSF, "Pivotal PSF header"},
|
||||
|
||||
};
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
#include "../vgmstream.h"
|
||||
#include "../mixing.h"
|
||||
|
||||
#define VGMSTREAM_MAX_SEGMENTS 255
|
||||
#define VGMSTREAM_MAX_SEGMENTS 512
|
||||
#define VGMSTREAM_SEGMENT_SAMPLE_BUFFER 8192
|
||||
|
||||
|
||||
|
@ -1366,10 +1366,14 @@
|
||||
RelativePath=".\meta\mta2.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\meta\ps3_past.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\meta\ps3_past.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\meta\psf.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\meta\sgxd.c"
|
||||
>
|
||||
|
@ -195,6 +195,7 @@
|
||||
<ClCompile Include="meta\ps2_wmus.c" />
|
||||
<ClCompile Include="meta\ps3_ivag.c" />
|
||||
<ClCompile Include="meta\ps3_past.c" />
|
||||
<ClCompile Include="meta\psf.c" />
|
||||
<ClCompile Include="meta\sgxd.c" />
|
||||
<ClCompile Include="meta\sk_aud.c" />
|
||||
<ClCompile Include="meta\vawx.c" />
|
||||
|
@ -1369,6 +1369,9 @@
|
||||
<ClCompile Include="meta\ps3_past.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\psf.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\sgxd.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
|
@ -31,7 +31,6 @@ VGMSTREAM * init_vgmstream_ngc_dsp_std_int(STREAMFILE *streamFile);
|
||||
VGMSTREAM * init_vgmstream_idsp_nus3(STREAMFILE *streamFile);
|
||||
VGMSTREAM * init_vgmstream_sadb(STREAMFILE *streamFile);
|
||||
VGMSTREAM * init_vgmstream_sadf(STREAMFILE *streamFile);
|
||||
VGMSTREAM * init_vgmstream_ngc_swd(STREAMFILE * streamFile);
|
||||
VGMSTREAM * init_vgmstream_idsp_tt(STREAMFILE *streamFile);
|
||||
VGMSTREAM * init_vgmstream_idsp_nl(STREAMFILE * streamFile);
|
||||
VGMSTREAM * init_vgmstream_wii_wsd(STREAMFILE *streamFile);
|
||||
@ -852,4 +851,7 @@ VGMSTREAM * init_vgmstream_mzrt(STREAMFILE * streamFile);
|
||||
|
||||
VGMSTREAM * init_vgmstream_xavs(STREAMFILE * streamFile);
|
||||
|
||||
VGMSTREAM * init_vgmstream_psf_single(STREAMFILE * streamFile);
|
||||
VGMSTREAM * init_vgmstream_psf_segmented(STREAMFILE * streamFile);
|
||||
|
||||
#endif /*_META_H*/
|
||||
|
@ -690,33 +690,6 @@ fail:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* SWD - PSF chunks + interleaved dsps [Conflict: Desert Storm 1 & 2] */
|
||||
VGMSTREAM * init_vgmstream_ngc_swd(STREAMFILE *streamFile) {
|
||||
dsp_meta dspm = {0};
|
||||
|
||||
/* checks */
|
||||
if (!check_extensions(streamFile, "swd"))
|
||||
goto fail;
|
||||
|
||||
//todo blocked layout when first chunk is 0x50534631 (count + table of 0x0c with offset/sizes)
|
||||
|
||||
if (read_32bitBE(0x00,streamFile) != 0x505346d1) /* PSF\0xd1 */
|
||||
goto fail;
|
||||
|
||||
dspm.channel_count = 2;
|
||||
dspm.max_channels = 2;
|
||||
|
||||
dspm.header_offset = 0x08;
|
||||
dspm.header_spacing = 0x60;
|
||||
dspm.start_offset = dspm.header_offset + 0x60 * dspm.channel_count;
|
||||
dspm.interleave = 0x08;
|
||||
|
||||
dspm.meta_type = meta_NGC_SWD;
|
||||
return init_vgmstream_dsp_common(streamFile, &dspm);
|
||||
fail:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* IDSP - Traveller's Tales header + interleaved dsps [Lego Batman (Wii), Lego Dimensions (Wii U)] */
|
||||
VGMSTREAM * init_vgmstream_idsp_tt(STREAMFILE *streamFile) {
|
||||
dsp_meta dspm = {0};
|
||||
|
242
src/meta/psf.c
Normal file
242
src/meta/psf.c
Normal file
@ -0,0 +1,242 @@
|
||||
#include "meta.h"
|
||||
#include "../layout/layout.h"
|
||||
#include "../coding/coding.h"
|
||||
|
||||
|
||||
/* PSF single - Pivotal games single segment (external in some PC/Xbox or inside bigfiles) [The Great Escape, Conflict series] */
|
||||
VGMSTREAM * init_vgmstream_psf_single(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
off_t start_offset;
|
||||
int loop_flag, channel_count, sample_rate, rate_value, interleave;
|
||||
uint32_t psf_config;
|
||||
uint8_t flags;
|
||||
size_t data_size;
|
||||
coding_t codec;
|
||||
|
||||
|
||||
/* checks */
|
||||
/* .psf: actual extension
|
||||
* .swd: bigfile extension */
|
||||
if (!check_extensions(streamFile, "psf,swd"))
|
||||
goto fail;
|
||||
if ((read_32bitBE(0x00,streamFile) & 0xFFFFFF00) != 0x50534600) /* "PSF\00" */
|
||||
goto fail;
|
||||
|
||||
flags = read_8bit(0x03,streamFile);
|
||||
switch(flags) {
|
||||
case 0xC0: /* [The Great Escape (PS2), Conflict: Desert Storm (PS2)] */
|
||||
case 0xA1: /* [Conflict: Desert Storm 2 (PS2)] */
|
||||
case 0x21: /* [Conflict: Desert Storm 2 (PS2), Conflict: Global Storm (PS2)] */
|
||||
//case 0x22: /* [Conflict: Vietman (PS2)] */ //todo weird size value, stereo, only one found
|
||||
channel_count = 2;
|
||||
if (flags == 0x21)
|
||||
channel_count = 1;
|
||||
interleave = 0x10;
|
||||
codec = coding_PSX;
|
||||
start_offset = 0x08;
|
||||
break;
|
||||
|
||||
case 0x80: /* [The Great Escape (PC/Xbox), Conflict: Desert Storm (Xbox/GC), Conflict: Desert Storm 2 (Xbox)] */
|
||||
case 0x81: /* [Conflict: Desert Storm 2 (Xbox), Conflict: Vietnam (Xbox)] */
|
||||
case 0x01: /* [Conflict: Global Storm (Xbox)] */
|
||||
channel_count = 2;
|
||||
if (flags == 0x01)
|
||||
channel_count = 1;
|
||||
interleave = 0x10;
|
||||
codec = coding_PSX_pivotal;
|
||||
start_offset = 0x08;
|
||||
break;
|
||||
|
||||
case 0xD1: /* [Conflict: Desert Storm 2 (GC)] */
|
||||
channel_count = 2;
|
||||
interleave = 0x08;
|
||||
codec = coding_NGC_DSP;
|
||||
start_offset = 0x08 + 0x60 * channel_count;
|
||||
break;
|
||||
|
||||
default:
|
||||
goto fail;
|
||||
}
|
||||
|
||||
loop_flag = 0;
|
||||
|
||||
psf_config = read_32bitLE(0x04, streamFile);
|
||||
|
||||
/* pitch/cents? */
|
||||
rate_value = (psf_config >> 20) & 0xFFF;
|
||||
switch(rate_value) {
|
||||
//case 0xEB5:
|
||||
//case 0xEB4:
|
||||
case 0xEB3: sample_rate = 44100; break;
|
||||
case 0x555: sample_rate = 16000; break;
|
||||
case 0x355: sample_rate = 11050; break;
|
||||
case 0x1d5: sample_rate = 6000; break; /* ? */
|
||||
case 0x1cc: sample_rate = 5000; break;
|
||||
default:
|
||||
VGM_LOG("PSF: unknown rate value %x\n", rate_value);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
data_size = (psf_config & 0xFFFFF) * (interleave * channel_count); /* in blocks */
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channel_count, loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->meta_type = meta_PSF;
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
|
||||
switch(codec) {
|
||||
case coding_PSX:
|
||||
vgmstream->coding_type = coding_PSX;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = interleave;
|
||||
|
||||
vgmstream->num_samples = ps_bytes_to_samples(data_size, channel_count);
|
||||
break;
|
||||
|
||||
case coding_PSX_pivotal:
|
||||
vgmstream->coding_type = coding_PSX_pivotal;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = interleave;
|
||||
|
||||
vgmstream->num_samples = ps_cfg_bytes_to_samples(data_size, 0x10, channel_count);
|
||||
break;
|
||||
|
||||
case coding_NGC_DSP:
|
||||
vgmstream->coding_type = coding_NGC_DSP;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = interleave;
|
||||
/* has standard DSP headers at 0x08 */
|
||||
dsp_read_coefs_be(vgmstream,streamFile,0x08+0x1c,0x60);
|
||||
dsp_read_hist_be (vgmstream,streamFile,0x08+0x40,0x60);
|
||||
|
||||
vgmstream->num_samples = read_32bitBE(0x08, streamFile);//dsp_bytes_to_samples(data_size, channel_count);
|
||||
break;
|
||||
|
||||
default:
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
|
||||
goto fail;
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* PSF segmented - Pivotal games multiple segments (external in some PC/Xbox or inside bigfiles) [The Great Escape, Conflict series] */
|
||||
VGMSTREAM * init_vgmstream_psf_segmented(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
STREAMFILE* temp_streamFile = NULL;
|
||||
segmented_layout_data *data = NULL;
|
||||
int i, segment_count, loop_flag = 0, loop_start = 0, loop_end = 0;
|
||||
off_t offset;
|
||||
|
||||
|
||||
/* checks */
|
||||
/* .psf: actual extension
|
||||
* .swd: bigfile extension */
|
||||
if (!check_extensions(streamFile, "psf,swd"))
|
||||
goto fail;
|
||||
|
||||
if (read_32bitBE(0x00,streamFile) != 0x50534660 && /* "PSF\60" [The Great Escape (PC/Xbox/PS2), Conflict: Desert Storm (Xbox/GC)] */
|
||||
read_32bitBE(0x00,streamFile) != 0x50534631) /* "PSF\31" [Conflict: Desert Storm 2 (Xbox/GC/PS2)] */
|
||||
goto fail;
|
||||
|
||||
segment_count = read_32bitLE(0x04, streamFile);
|
||||
loop_flag = 0;
|
||||
|
||||
offset = 0x08;
|
||||
offset += 0x0c; /* first segment points to itself? */
|
||||
segment_count--;
|
||||
|
||||
/* build segments */
|
||||
data = init_layout_segmented(segment_count);
|
||||
if (!data) goto fail;
|
||||
|
||||
for (i = 0; i < segment_count; i++) {
|
||||
off_t psf_offset;
|
||||
size_t psf_size;
|
||||
uint32_t psf_id;
|
||||
|
||||
/* mini table */
|
||||
psf_offset = read_32bitLE(offset + 0x00, streamFile);
|
||||
/* 0x04-0c: 0x02*4 transition segments (possibly to 4 song variations) */
|
||||
|
||||
/* use last section transition as loop */
|
||||
if (i + 1 == segment_count) {
|
||||
loop_flag = 1;
|
||||
loop_start = read_16bitLE(offset + 0x0a, streamFile) - 1; /* also ignore first segment */
|
||||
loop_end = i;
|
||||
}
|
||||
|
||||
/* multiple segment can point to the same PSF offset (for repeated song sections) */
|
||||
//todo reuse repeated VGMSTREAMs to improve memory a bit
|
||||
|
||||
psf_id = read_32bitBE(psf_offset + 0x00, streamFile);
|
||||
psf_size = read_32bitLE(psf_offset + 0x04, streamFile);
|
||||
if (psf_id == 0x505346D1) //todo improve
|
||||
psf_size = (psf_size & 0xFFFFF) * 0x10;
|
||||
else
|
||||
psf_size = (psf_size & 0xFFFFF) * 0x20;
|
||||
//;VGM_LOG("PSF: offset=%lx, size=%x\n", psf_offset, psf_size);
|
||||
|
||||
temp_streamFile = setup_subfile_streamfile(streamFile, psf_offset, psf_size, "psf");
|
||||
if (!temp_streamFile) goto fail;
|
||||
|
||||
data->segments[i] = init_vgmstream_psf_single(temp_streamFile);
|
||||
if (!data->segments[i]) goto fail;
|
||||
|
||||
offset += 0x0c;
|
||||
}
|
||||
|
||||
/* setup VGMSTREAMs */
|
||||
if (!setup_layout_segmented(data))
|
||||
goto fail;
|
||||
vgmstream = allocate_segmented_vgmstream(data,loop_flag, loop_start, loop_end);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->stream_size = get_streamfile_size(streamFile);
|
||||
|
||||
return vgmstream;
|
||||
fail:
|
||||
if (!vgmstream) free_layout_segmented(data);
|
||||
close_streamfile(temp_streamFile);
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#if 0
|
||||
VGMSTREAM * init_vgmstream_sch(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
off_t start_offset;
|
||||
int loop_flag, channel_count;
|
||||
|
||||
|
||||
/* checks */
|
||||
if (!check_extensions(streamFile, "sch"))
|
||||
goto fail;
|
||||
|
||||
/* chunked format (id+size, GC pads to 0x20 and uses BE/inverted ids):
|
||||
* - SCH\0: start
|
||||
* - IMUS: points to a external .psf + segment table (same as in .psf, TGE only?)
|
||||
* - BANK: volume/etc info? points to something?
|
||||
* - PFSM: single .psf-like file (larger header)
|
||||
* - PFST: points to single PSF offset (.psf in TGE, or STREAMS.SWD); may be chained to next PFST?
|
||||
*
|
||||
* no other info so total subsongs would be count of usable chunks
|
||||
* in later games, segmented .psf seems to be removed and PFST is used instead
|
||||
*/
|
||||
|
||||
return vgmstream;
|
||||
fail:
|
||||
if (!vgmstream) free_layout_layered(data);
|
||||
close_streamfile(temp_streamFile);
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
@ -123,7 +123,6 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = {
|
||||
init_vgmstream_sdt,
|
||||
init_vgmstream_aix,
|
||||
init_vgmstream_ngc_tydsp,
|
||||
init_vgmstream_ngc_swd,
|
||||
init_vgmstream_capdsp,
|
||||
init_vgmstream_xbox_wvs,
|
||||
init_vgmstream_ngc_wvs,
|
||||
@ -467,6 +466,8 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = {
|
||||
init_vgmstream_smk,
|
||||
init_vgmstream_mzrt,
|
||||
init_vgmstream_xavs,
|
||||
init_vgmstream_psf_single,
|
||||
init_vgmstream_psf_segmented,
|
||||
|
||||
/* 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 */
|
||||
@ -1171,7 +1172,8 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) {
|
||||
case coding_HEVAG:
|
||||
return 28;
|
||||
case coding_PSX_cfg:
|
||||
return (vgmstream->interleave_block_size - 1) * 2; /* decodes 1 byte into 2 bytes */
|
||||
case coding_PSX_pivotal:
|
||||
return (vgmstream->interleave_block_size - 0x01) * 2; /* size 0x01 header */
|
||||
|
||||
case coding_EA_XA:
|
||||
case coding_EA_XA_int:
|
||||
@ -1359,6 +1361,7 @@ int get_vgmstream_frame_size(VGMSTREAM * vgmstream) {
|
||||
case coding_HEVAG:
|
||||
return 0x10;
|
||||
case coding_PSX_cfg:
|
||||
case coding_PSX_pivotal:
|
||||
return vgmstream->interleave_block_size;
|
||||
|
||||
case coding_EA_XA:
|
||||
@ -1674,6 +1677,13 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to
|
||||
vgmstream->channels,vgmstream->samples_into_block,samples_to_do, vgmstream->interleave_block_size);
|
||||
}
|
||||
break;
|
||||
case coding_PSX_pivotal:
|
||||
for (ch = 0; ch < vgmstream->channels; ch++) {
|
||||
decode_psx_pivotal(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch,
|
||||
vgmstream->channels,vgmstream->samples_into_block,samples_to_do,
|
||||
vgmstream->interleave_block_size);
|
||||
}
|
||||
break;
|
||||
case coding_HEVAG:
|
||||
for (ch = 0; ch < vgmstream->channels; ch++) {
|
||||
decode_hevag(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch,
|
||||
|
@ -104,7 +104,8 @@ typedef enum {
|
||||
coding_XA, /* CD-ROM XA */
|
||||
coding_PSX, /* Sony PS ADPCM (VAG) */
|
||||
coding_PSX_badflags, /* Sony PS ADPCM with custom flag byte */
|
||||
coding_PSX_cfg, /* Sony PS ADPCM with configurable frame size (FF XI, SGXD type 5, Bizarre Creations) */
|
||||
coding_PSX_cfg, /* Sony PS ADPCM with configurable frame size (int math) */
|
||||
coding_PSX_pivotal, /* Sony PS ADPCM with configurable frame size (float math) */
|
||||
coding_HEVAG, /* Sony PSVita ADPCM */
|
||||
|
||||
coding_EA_XA, /* Electronic Arts EA-XA ADPCM v1 (stereo) aka "EA ADPCM" */
|
||||
@ -389,7 +390,6 @@ typedef enum {
|
||||
meta_PS2_ENTH, /* Enthusia */
|
||||
meta_SDT, /* Baldur's Gate - Dark Alliance */
|
||||
meta_NGC_TYDSP, /* Ty - The Tasmanian Tiger */
|
||||
meta_NGC_SWD, /* Conflict - Desert Storm 1 & 2 */
|
||||
meta_CAPDSP, /* Capcom DSP Header [no header_id] */
|
||||
meta_DC_STR, /* SEGA Stream Asset Builder */
|
||||
meta_DC_STR_V2, /* variant of SEGA Stream Asset Builder */
|
||||
@ -712,6 +712,7 @@ typedef enum {
|
||||
meta_SMACKER,
|
||||
meta_MZRT,
|
||||
meta_XAVS,
|
||||
meta_PSF,
|
||||
|
||||
} meta_t;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user