From 3bac417a8cac38708f17f9d77f3e42d1b27541d5 Mon Sep 17 00:00:00 2001 From: bnnm Date: Sun, 11 Aug 2019 19:38:40 +0200 Subject: [PATCH] Add Pivotal .psf and decoder [The Great Escape, Conflict: Desert Storm] --- README.md | 6 +- src/coding/coding.h | 1 + src/coding/psx_decoder.c | 67 ++++++++- src/formats.c | 4 +- src/layout/segmented.c | 2 +- src/libvgmstream.vcproj | 12 +- src/libvgmstream.vcxproj | 1 + src/libvgmstream.vcxproj.filters | 3 + src/meta/meta.h | 4 +- src/meta/ngc_dsp_std.c | 27 ---- src/meta/psf.c | 242 +++++++++++++++++++++++++++++++ src/vgmstream.c | 14 +- src/vgmstream.h | 5 +- 13 files changed, 339 insertions(+), 49 deletions(-) create mode 100644 src/meta/psf.c diff --git a/README.md b/README.md index 1de80a91..c82368f0 100644 --- a/README.md +++ b/README.md @@ -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 @@ -308,7 +308,7 @@ the format defined. ## Supported codec types -Quick list of codecs vgmstream supports, including many obscure ones that +Quick list of codecs vgmstream supports, including many obscure ones that are used in few games. - PCM 16-bit @@ -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) diff --git a/src/coding/coding.h b/src/coding/coding.h index 2707b2d8..cd2b542a 100644 --- a/src/coding/coding.h +++ b/src/coding/coding.h @@ -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); diff --git a/src/coding/psx_decoder.c b/src/coding/psx_decoder.c index 47c66ac7..f5e7ebd4 100644 --- a/src/coding/psx_decoder.c +++ b/src/coding/psx_decoder.c @@ -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 */ diff --git a/src/formats.c b/src/formats.c index 2c43467c..6ca3635e 100644 --- a/src/formats.c +++ b/src/formats.c @@ -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"}, }; diff --git a/src/layout/segmented.c b/src/layout/segmented.c index 2c2e18c8..3477bf4c 100644 --- a/src/layout/segmented.c +++ b/src/layout/segmented.c @@ -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 diff --git a/src/libvgmstream.vcproj b/src/libvgmstream.vcproj index aa7aa84e..e04fbe58 100644 --- a/src/libvgmstream.vcproj +++ b/src/libvgmstream.vcproj @@ -1366,10 +1366,14 @@ RelativePath=".\meta\mta2.c" > - - + + + + diff --git a/src/libvgmstream.vcxproj b/src/libvgmstream.vcxproj index 892a9e48..de48b9dc 100644 --- a/src/libvgmstream.vcxproj +++ b/src/libvgmstream.vcxproj @@ -195,6 +195,7 @@ + diff --git a/src/libvgmstream.vcxproj.filters b/src/libvgmstream.vcxproj.filters index 454f77e6..0eecc019 100644 --- a/src/libvgmstream.vcxproj.filters +++ b/src/libvgmstream.vcxproj.filters @@ -1369,6 +1369,9 @@ meta\Source Files + + meta\Source Files + meta\Source Files diff --git a/src/meta/meta.h b/src/meta/meta.h index 8f128e9c..dfd6ad77 100644 --- a/src/meta/meta.h +++ b/src/meta/meta.h @@ -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*/ diff --git a/src/meta/ngc_dsp_std.c b/src/meta/ngc_dsp_std.c index f00024c4..e336e495 100644 --- a/src/meta/ngc_dsp_std.c +++ b/src/meta/ngc_dsp_std.c @@ -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}; diff --git a/src/meta/psf.c b/src/meta/psf.c new file mode 100644 index 00000000..a7f79ef4 --- /dev/null +++ b/src/meta/psf.c @@ -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 diff --git a/src/vgmstream.c b/src/vgmstream.c index 5781ca97..b5afd745 100644 --- a/src/vgmstream.c +++ b/src/vgmstream.c @@ -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, diff --git a/src/vgmstream.h b/src/vgmstream.h index 89c02e7e..bce2237d 100644 --- a/src/vgmstream.h +++ b/src/vgmstream.h @@ -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;