diff --git a/src/formats.c b/src/formats.c index 4f3ac938..54b6ec4f 100644 --- a/src/formats.c +++ b/src/formats.c @@ -1002,6 +1002,7 @@ static const meta_info meta_info_list[] = { {meta_SMC_SMH, "Genki SMC+SMH header"}, {meta_OGG_YS8, "Ogg Vorbis (Ys VIII header)"}, {meta_PPST, "Parappa PPST header"}, + {meta_OPUS_PPP, "AT9 OPUS header"}, #ifdef VGM_USE_FFMPEG {meta_FFmpeg, "FFmpeg supported file format"}, diff --git a/src/libvgmstream.vcproj b/src/libvgmstream.vcproj index 59f24e6c..300f2fe5 100644 --- a/src/libvgmstream.vcproj +++ b/src/libvgmstream.vcproj @@ -737,6 +737,10 @@ + + + diff --git a/src/libvgmstream.vcxproj.filters b/src/libvgmstream.vcxproj.filters index 741a060a..5db85d93 100644 --- a/src/libvgmstream.vcxproj.filters +++ b/src/libvgmstream.vcxproj.filters @@ -445,6 +445,9 @@ meta\Source Files + + meta\Source Files + meta\Source Files diff --git a/src/meta/meta.h b/src/meta/meta.h index 1e8e48a0..6eed12da 100644 --- a/src/meta/meta.h +++ b/src/meta/meta.h @@ -742,4 +742,6 @@ VGMSTREAM * init_vgmstream_ea_sps_fb(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_ppst(STREAMFILE *streamFile); +VGMSTREAM * init_vgmstream_opus_ppp(STREAMFILE *streamFile); + #endif /*_META_H*/ diff --git a/src/meta/opus_ppp.c b/src/meta/opus_ppp.c new file mode 100644 index 00000000..af764e94 --- /dev/null +++ b/src/meta/opus_ppp.c @@ -0,0 +1,120 @@ +#include "meta.h" +#include "../coding/coding.h" +#include "../layout/layout.h" + +static STREAMFILE* setup_opus_ppp_streamfile(STREAMFILE *streamFile, off_t subfile_offset, size_t subfile_size, const char* fake_ext); + +/* .AT9 Opus - from Penny-Punching Princess (Switch) */ +VGMSTREAM * init_vgmstream_opus_ppp(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + off_t segment_offset; + int loop_flag, channel_count; + int i; + + segmented_layout_data *data = NULL; + int segment_count, loop_start_segment = 0, loop_end_segment = 0; + int num_samples = 0, loop_start_sample = 0, loop_end_sample = 0; + + + /* checks */ + if (!check_extensions(streamFile, "at9")) + goto fail; + if (read_32bitBE(0x00,streamFile) != 0x09000000) /* file type? DSPs had 08 */ + goto fail; + if (read_32bitLE(0x04,streamFile) + 0x1c != get_streamfile_size(streamFile)) + goto fail; + /* 0x08(2): sample rate, 0x0a(2): loop flag?, 0x0c: num_samples (slightly smaller than added samples) */ + + segment_count = 3; /* intro/loop/end */ + loop_start_segment = 1; + loop_end_segment = 1; + loop_flag = (segment_count > 0); + + /* init layout */ + data = init_layout_segmented(segment_count); + if (!data) goto fail; + + /* open each segment subfile */ + segment_offset = 0x1c; + for (i = 0; i < segment_count; i++) { + STREAMFILE* temp_streamFile; + size_t segment_size = read_32bitLE(0x10+0x04*i,streamFile); + + if (!segment_size) + goto fail; + + temp_streamFile = setup_opus_ppp_streamfile(streamFile, segment_offset,segment_size, "opus"); + if (!temp_streamFile) goto fail; + + data->segments[i] = init_vgmstream_nsw_opus(temp_streamFile); + close_streamfile(temp_streamFile); + if (!data->segments[i]) goto fail; + + segment_offset += segment_size; + + //todo there are some trailing samples that must be removed for smooth loops, start skip seems ok + data->segments[i]->num_samples -= 374; //not correct for all files, no idea how to calculate + + /* get looping and samples */ + if (loop_flag && loop_start_segment == i) + loop_start_sample = num_samples; + + num_samples += data->segments[i]->num_samples; + + if (loop_flag && loop_end_segment == i) + loop_end_sample = num_samples; + } + + /* setup segmented VGMSTREAMs */ + if (!setup_layout_segmented(data)) + goto fail; + + + channel_count = data->segments[0]->channels; + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count,loop_flag); + if (!vgmstream) goto fail; + + vgmstream->sample_rate = (uint16_t)read_16bitLE(0x08,streamFile); + vgmstream->num_samples = num_samples; + vgmstream->loop_start_sample = loop_start_sample; + vgmstream->loop_end_sample = loop_end_sample; + + vgmstream->meta_type = meta_OPUS_PPP; + vgmstream->coding_type = data->segments[0]->coding_type; + vgmstream->layout_type = layout_segmented; + + vgmstream->layout_data = data; + data->loop_segment = loop_start_segment; + + return vgmstream; + +fail: + close_vgmstream(vgmstream); + free_layout_segmented(data); + return NULL; +} + +static STREAMFILE* setup_opus_ppp_streamfile(STREAMFILE *streamFile, off_t subfile_offset, size_t subfile_size, const char* fake_ext) { + STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL; + + /* setup subfile */ + new_streamFile = open_wrap_streamfile(streamFile); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + new_streamFile = open_clamp_streamfile(temp_streamFile, subfile_offset,subfile_size); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + new_streamFile = open_fakename_streamfile(temp_streamFile, NULL,fake_ext); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + return temp_streamFile; + +fail: + close_streamfile(temp_streamFile); + return NULL; +} diff --git a/src/vgmstream.c b/src/vgmstream.c index 6cd5f2b6..52fc9bac 100644 --- a/src/vgmstream.c +++ b/src/vgmstream.c @@ -401,6 +401,7 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_smc_smh, init_vgmstream_ea_sps_fb, init_vgmstream_ppst, + init_vgmstream_opus_ppp, init_vgmstream_txth, /* should go at the end (lower priority) */ #ifdef VGM_USE_FFMPEG diff --git a/src/vgmstream.h b/src/vgmstream.h index 29aaa820..851e2527 100644 --- a/src/vgmstream.h +++ b/src/vgmstream.h @@ -672,6 +672,7 @@ typedef enum { meta_SMC_SMH, /* Wangan Midnight (System 246) */ meta_OGG_YS8, /* Ogg Vorbis with encryption (Ys VIII PC) */ meta_PPST, /* PPST [Parappa the Rapper (PSP)] */ + meta_OPUS_PPP, /* .at9 Opus [Penny-Punching Princess (Switch)] */ #ifdef VGM_USE_FFMPEG meta_FFmpeg,