From 4c038cb0eab8d861a3afd970c1e0db0a40ae263e Mon Sep 17 00:00:00 2001 From: bnnm Date: Sun, 10 Feb 2019 01:36:05 +0100 Subject: [PATCH] Add Ocean .DSF, .208 and DSA decoder [Last Rites (PC)] --- src/coding/coding.h | 3 ++ src/coding/dsa_decoder.c | 52 ++++++++++++++++++++++++++++++++ src/formats.c | 5 +++ src/libvgmstream.vcproj | 20 +++++++++--- src/libvgmstream.vcxproj | 3 ++ src/libvgmstream.vcxproj.filters | 9 ++++++ src/meta/208.c | 48 +++++++++++++++++++++++++++++ src/meta/dsf.c | 51 +++++++++++++++++++++++++++++++ src/meta/meta.h | 4 +++ src/vgmstream.c | 12 ++++++++ src/vgmstream.h | 3 ++ 11 files changed, 206 insertions(+), 4 deletions(-) create mode 100644 src/coding/dsa_decoder.c create mode 100644 src/meta/208.c create mode 100644 src/meta/dsf.c diff --git a/src/coding/coding.h b/src/coding/coding.h index d9f0a4b3..43f17e90 100644 --- a/src/coding/coding.h +++ b/src/coding/coding.h @@ -161,6 +161,9 @@ void decode_fadpcm(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacin /* asf_decoder */ void decode_asf(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); +/* dsa_decoder */ +void decode_dsa(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); + /* xmd_decoder */ void decode_xmd(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, size_t frame_size); diff --git a/src/coding/dsa_decoder.c b/src/coding/dsa_decoder.c new file mode 100644 index 00000000..6f4f1294 --- /dev/null +++ b/src/coding/dsa_decoder.c @@ -0,0 +1,52 @@ +#include "coding.h" + + +static const int dsa_coefs[16] = { + 0x0, 0x1999, 0x3333, 0x4CCC, + 0x6666, 0x8000, 0x9999, 0xB333, + 0xCCCC, 0xE666, 0x10000, 0x11999, + 0x13333, 0x18000, 0x1CCCC, 0x21999 +}; + +/* Decodes Ocean DSA ADPCM codec from Last Rites (PC). + * Reverse engineered from daemon1's reverse engineering. */ +void decode_dsa(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) { + off_t frame_offset; + int i, frames_in, sample_count = 0; + size_t bytes_per_frame, samples_per_frame; + uint8_t header; + int shift, filter; + int32_t hist1 = stream->adpcm_history1_32; + + /* external interleave (fixed size), mono */ + bytes_per_frame = 0x08; + 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; + header = (uint8_t)read_8bit(frame_offset+0x00,stream->streamfile); + shift = 0x0c - ((header >> 4) & 0xf); + filter = dsa_coefs[header & 0xf]; + + /* decode nibbles */ + for (i = first_sample; i < first_sample + samples_to_do; i++) { + int32_t new_sample; + uint8_t nibbles = (uint8_t)read_8bit(frame_offset+0x01 + i/2,stream->streamfile); + + new_sample = i&1 ? /* high nibble first */ + (nibbles >> 0) & 0xf : + (nibbles >> 4) & 0xf; + + new_sample = ((int16_t)(new_sample << 0xC) >> shift); /* 16b sign extend + scale */ + new_sample = new_sample + ((hist1 * filter) >> 0x10); + + outbuf[sample_count] = (int16_t)(new_sample << 2); + sample_count += channelspacing; + + hist1 = new_sample; + } + + stream->adpcm_history1_32 = hist1; +} diff --git a/src/formats.c b/src/formats.c index 6e34c800..7c00a0a0 100644 --- a/src/formats.c +++ b/src/formats.c @@ -14,6 +14,7 @@ static const char* extension_list[] = { //"", /* vgmstream can play extensionless files too, but plugins must accept them manually */ "04sw", + "208", "2dx9", "2pfs", "800", @@ -122,6 +123,7 @@ static const char* extension_list[] = { "de2", "dec", "dmsg", + "dsf", "dsp", "dspw", "dtk", @@ -639,6 +641,7 @@ static const coding_info coding_info_list[] = { {coding_MC3, "Paradigm MC3 3-bit ADPCM"}, {coding_FADPCM, "FMOD FADPCM 4-bit ADPCM"}, {coding_ASF, "Argonaut ASF 4-bit ADPCM"}, + {coding_DSA, "Ocean DSA 4-bit ADPCM"}, {coding_XMD, "Konami XMD 4-bit ADPCM"}, {coding_PCFX, "PC-FX 4-bit ADPCM"}, {coding_OKI16, "OKI 4-bit ADPCM (16-bit output)"}, @@ -1164,6 +1167,8 @@ static const meta_info meta_info_list[] = { {meta_OGG_OPUS, "Ogg Opus header"}, {meta_IMC, "iNiS .IMC header"}, {meta_GIN, "Electronic Arts Gnsu header"}, + {meta_DSF, "Ocean DSF header"}, + {meta_208, "Ocean .208 header"}, }; diff --git a/src/libvgmstream.vcproj b/src/libvgmstream.vcproj index c8793926..ed34b53a 100644 --- a/src/libvgmstream.vcproj +++ b/src/libvgmstream.vcproj @@ -296,6 +296,10 @@ + + @@ -496,10 +500,14 @@ RelativePath=".\meta\derf.c" > - - + + + + @@ -1774,6 +1782,10 @@ RelativePath=".\coding\derf_decoder.c" > + + diff --git a/src/libvgmstream.vcxproj b/src/libvgmstream.vcxproj index 55cf8634..4200592f 100644 --- a/src/libvgmstream.vcxproj +++ b/src/libvgmstream.vcxproj @@ -201,6 +201,7 @@ + @@ -248,6 +249,7 @@ + @@ -504,6 +506,7 @@ + diff --git a/src/libvgmstream.vcxproj.filters b/src/libvgmstream.vcxproj.filters index fa2c2930..e2c8e194 100644 --- a/src/libvgmstream.vcxproj.filters +++ b/src/libvgmstream.vcxproj.filters @@ -184,6 +184,9 @@ Source Files + + meta\Source Files + meta\Source Files @@ -319,6 +322,9 @@ meta\Source Files + + meta\Source Files + meta\Source Files @@ -1054,6 +1060,9 @@ coding\Source Files + + coding\Source Files + coding\Source Files diff --git a/src/meta/208.c b/src/meta/208.c new file mode 100644 index 00000000..10e6461b --- /dev/null +++ b/src/meta/208.c @@ -0,0 +1,48 @@ +#include "meta.h" +#include "../coding/coding.h" + +/* .208 - from Ocean game(s?) [Last Rites (PC)] */ +VGMSTREAM * init_vgmstream_208(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + off_t start_offset; + int loop_flag, channel_count, sample_rate; + size_t data_size; + + + /* checks */ + if (!check_extensions(streamFile, "208")) + goto fail; + /* possible validation: (0x04 == 0 and 0xcc == 0x1F7D984D) or 0x04 == 0xf0 and 0xcc == 0) */ + if (!((read_32bitLE(0x04,streamFile) == 0x00 && read_32bitBE(0xcc,streamFile) == 0x1F7D984D) || + (read_32bitLE(0x04,streamFile) == 0xF0 && read_32bitBE(0xcc,streamFile) == 0x00000000))) + goto fail; + + start_offset = read_32bitLE(0x00,streamFile); + data_size = read_32bitLE(0x0c,streamFile); + sample_rate = read_32bitLE(0x34,streamFile); + channel_count = read_32bitLE(0x3C,streamFile); /* assumed */ + loop_flag = 0; + + if (start_offset + data_size != get_streamfile_size(streamFile)) + goto fail; + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count, loop_flag); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_208; + vgmstream->sample_rate = sample_rate; + vgmstream->num_samples = pcm_bytes_to_samples(data_size, channel_count, 8); + vgmstream->coding_type = coding_PCM8_U; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x1; + + if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + goto fail; + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} diff --git a/src/meta/dsf.c b/src/meta/dsf.c new file mode 100644 index 00000000..342ff10b --- /dev/null +++ b/src/meta/dsf.c @@ -0,0 +1,51 @@ +#include "meta.h" +#include "../coding/coding.h" + +/* .DSF - from Ocean game(s?) [Last Rites (PC)] */ +VGMSTREAM * init_vgmstream_dsf(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + off_t start_offset; + int loop_flag, channel_count, sample_rate; + size_t data_size; + + + /* checks */ + if (!check_extensions(streamFile, "dsf")) + goto fail; + + if (read_32bitBE(0x00,streamFile) != 0x4F434541 && /* "OCEA" */ + read_32bitBE(0x00,streamFile) != 0x4E204453 && /* "N DS" */ + read_32bitBE(0x00,streamFile) != 0x41000000) /* "A\0\0\0" */ + goto fail; + + /* 0x10(2): always 1? */ + /* 0x12(4): total nibbles / 0x10? */ + /* 0x16(4): always 0? */ + start_offset = read_32bitLE(0x1a,streamFile); + sample_rate = read_32bitLE(0x1e,streamFile); + channel_count = read_32bitLE(0x22,streamFile) + 1; + data_size = get_streamfile_size(streamFile) - start_offset; + loop_flag = 0; + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count, loop_flag); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_DSF; + vgmstream->sample_rate = sample_rate; + vgmstream->num_samples = ((data_size / 0x08 / channel_count) * 14); /* bytes-to-samples */ + vgmstream->coding_type = coding_DSA; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x08; + + read_string(vgmstream->stream_name,0x20+1, 0x26,streamFile); + + if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + goto fail; + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} diff --git a/src/meta/meta.h b/src/meta/meta.h index 7c32acf6..50ce55b0 100644 --- a/src/meta/meta.h +++ b/src/meta/meta.h @@ -826,4 +826,8 @@ VGMSTREAM * init_vgmstream_smp(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_gin(STREAMFILE * streamFile); +VGMSTREAM * init_vgmstream_dsf(STREAMFILE * streamFile); + +VGMSTREAM * init_vgmstream_208(STREAMFILE * streamFile); + #endif /*_META_H*/ diff --git a/src/vgmstream.c b/src/vgmstream.c index 31de8b51..cbc643c4 100644 --- a/src/vgmstream.c +++ b/src/vgmstream.c @@ -463,6 +463,8 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_imc_container, init_vgmstream_smp, init_vgmstream_gin, + init_vgmstream_dsf, + init_vgmstream_208, /* 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 */ @@ -1248,6 +1250,8 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) { return 256; /* (0x8c - 0xc) * 2 */ case coding_ASF: return 32; /* (0x11 - 0x1) * 2 */ + case coding_DSA: + return 14; /* (0x08 - 0x1) * 2 */ case coding_XMD: return (vgmstream->interleave_block_size - 0x06)*2 + 2; case coding_EA_MT: @@ -1424,6 +1428,8 @@ int get_vgmstream_frame_size(VGMSTREAM * vgmstream) { return 0x8c; case coding_ASF: return 0x11; + case coding_DSA: + return 0x08; case coding_XMD: return vgmstream->interleave_block_size; case coding_EA_MT: @@ -2047,6 +2053,12 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to vgmstream->channels,vgmstream->samples_into_block,samples_to_do); } break; + case coding_DSA: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_dsa(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do); + } + break; case coding_XMD: for (ch = 0; ch < vgmstream->channels; ch++) { decode_xmd(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, diff --git a/src/vgmstream.h b/src/vgmstream.h index da1b7ca3..899b8f9d 100644 --- a/src/vgmstream.h +++ b/src/vgmstream.h @@ -152,6 +152,7 @@ typedef enum { coding_MC3, /* Paradigm MC3 3-bit ADPCM */ coding_FADPCM, /* FMOD FADPCM 4-bit ADPCM */ coding_ASF, /* Argonaut ASF 4-bit ADPCM */ + coding_DSA, /* Ocean DSA 4-bit ADPCM */ coding_XMD, /* Konami XMD 4-bit ADPCM */ coding_PCFX, /* PC-FX 4-bit ADPCM */ coding_OKI16, /* OKI 4-bit ADPCM with 16-bit output */ @@ -714,6 +715,8 @@ typedef enum { meta_OGG_OPUS, meta_IMC, meta_GIN, + meta_DSF, + meta_208, } meta_t;