2020-10-01 21:22:34 +02:00
|
|
|
#include "meta.h"
|
|
|
|
#include "../layout/layout.h"
|
|
|
|
#include "../coding/coding.h"
|
|
|
|
|
|
|
|
/* also see init_vgmstream_dsp_sps_n1 and init_vgmstream_opus_sps_n1 */
|
|
|
|
|
|
|
|
/* Nippon Ichi SPS wrapper [ClaDun (PSP)] */
|
|
|
|
VGMSTREAM* init_vgmstream_sps_n1(STREAMFILE* sf) {
|
|
|
|
VGMSTREAM* vgmstream = NULL;
|
|
|
|
STREAMFILE* temp_sf = NULL;
|
|
|
|
int type, sample_rate;
|
|
|
|
off_t subfile_offset;
|
|
|
|
size_t subfile_size;
|
|
|
|
|
|
|
|
VGMSTREAM* (*init_vgmstream_subfile)(STREAMFILE*) = NULL;
|
|
|
|
const char* extension;
|
|
|
|
|
|
|
|
|
|
|
|
/* checks */
|
|
|
|
if (!check_extensions(sf,"sps"))
|
|
|
|
goto fail;
|
|
|
|
|
|
|
|
type = read_u32le(0x00,sf);
|
|
|
|
subfile_size = read_u32le(0x04,sf);
|
|
|
|
sample_rate = read_u16le(0x08,sf);
|
|
|
|
/* 0x0a: flag? (stereo?) */
|
|
|
|
/* 0x0b: flag? */
|
|
|
|
/* 0x0c: num_samples */
|
|
|
|
|
|
|
|
switch(type) {
|
|
|
|
case 1:
|
|
|
|
init_vgmstream_subfile = init_vgmstream_vag;
|
|
|
|
extension = "vag";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 2:
|
|
|
|
init_vgmstream_subfile = init_vgmstream_riff;
|
|
|
|
extension = "at3";
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
subfile_offset = 0x10;
|
|
|
|
if (subfile_size + subfile_offset != get_streamfile_size(sf))
|
|
|
|
goto fail;
|
|
|
|
|
|
|
|
/* init the VGMSTREAM */
|
|
|
|
temp_sf = setup_subfile_streamfile(sf, subfile_offset, subfile_size, extension);
|
|
|
|
if (!temp_sf) goto fail;
|
|
|
|
|
|
|
|
vgmstream = init_vgmstream_subfile(temp_sf);
|
|
|
|
if (!vgmstream) goto fail;
|
|
|
|
|
|
|
|
vgmstream->sample_rate = sample_rate; /* .vag header doesn't match */
|
|
|
|
|
|
|
|
close_streamfile(temp_sf);
|
|
|
|
return vgmstream;
|
|
|
|
|
|
|
|
fail:
|
|
|
|
close_streamfile(temp_sf);
|
|
|
|
close_vgmstream(vgmstream);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Nippon Ichi SPS wrapper (segmented) [Penny-Punching Princess (Switch), Disgaea 4 Complete (PC)] */
|
|
|
|
VGMSTREAM* init_vgmstream_sps_n1_segmented(STREAMFILE* sf) {
|
|
|
|
VGMSTREAM* vgmstream = NULL;
|
|
|
|
off_t segment_offset;
|
|
|
|
size_t data_size, max_size;
|
|
|
|
int loop_flag, type, sample_rate;
|
|
|
|
int i, segment;
|
|
|
|
|
|
|
|
VGMSTREAM* (*init_vgmstream_subfile)(STREAMFILE*) = NULL;
|
|
|
|
const char* extension;
|
|
|
|
segmented_layout_data* data = NULL;
|
|
|
|
int segment_count, loop_start_segment, loop_end_segment;
|
|
|
|
|
|
|
|
|
|
|
|
/* checks */
|
|
|
|
/* .at9: Penny-Punching Princess (Switch)
|
|
|
|
* .nlsd: Disgaea 4 Complete (PC) */
|
|
|
|
if (!check_extensions(sf, "at9,nlsd"))
|
|
|
|
goto fail;
|
|
|
|
|
|
|
|
type = read_u32le(0x00,sf);
|
|
|
|
data_size = read_u32le(0x04,sf);
|
|
|
|
sample_rate = read_u16le(0x08,sf);
|
|
|
|
/* 0x0a: flag? (stereo?) */
|
|
|
|
/* 0x0b: flag? */
|
|
|
|
/* 0x0c: num_samples (slightly smaller than added samples?) */
|
|
|
|
|
|
|
|
switch(type) {
|
2020-11-12 20:11:55 +01:00
|
|
|
#ifdef VGM_USE_VORBIS
|
2020-10-01 21:22:34 +02:00
|
|
|
case 7:
|
|
|
|
init_vgmstream_subfile = init_vgmstream_ogg_vorbis;
|
|
|
|
extension = "ogg";
|
|
|
|
break;
|
2020-11-12 20:11:55 +01:00
|
|
|
#endif
|
2020-10-01 21:22:34 +02:00
|
|
|
|
|
|
|
case 9:
|
|
|
|
init_vgmstream_subfile = init_vgmstream_opus_std;
|
|
|
|
extension = "opus";
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
segment_offset = 0x1c;
|
|
|
|
if (data_size + segment_offset != get_streamfile_size(sf))
|
|
|
|
goto fail;
|
|
|
|
|
|
|
|
/* segmented using 3 files (intro/loop/outro). non-segmented wrapper is the same
|
|
|
|
* but with loop samples instead of sub-sizes */
|
|
|
|
max_size = 0;
|
|
|
|
segment_count = 0;
|
|
|
|
for (i = 0; i < 3; i++) {
|
|
|
|
size_t segment_size = read_u32le(0x10 + 0x04*i,sf);
|
|
|
|
max_size += segment_size;
|
|
|
|
/* may only set 1 segment (Disgaea4's bgm_185) */
|
|
|
|
if (segment_size)
|
|
|
|
segment_count++;
|
|
|
|
}
|
|
|
|
if (data_size != max_size)
|
|
|
|
goto fail;
|
|
|
|
|
|
|
|
loop_flag = segment_count > 1; /* intro+loop section must exit */
|
|
|
|
loop_start_segment = 1;
|
|
|
|
loop_end_segment = 1;
|
|
|
|
|
|
|
|
/* init layout */
|
|
|
|
data = init_layout_segmented(segment_count);
|
|
|
|
if (!data) goto fail;
|
|
|
|
|
|
|
|
/* open each segment subfile */
|
|
|
|
segment = 0;
|
|
|
|
for (i = 0; i < 3; i++) {
|
|
|
|
STREAMFILE* temp_sf;
|
|
|
|
size_t segment_size = read_u32le(0x10 + 0x04*i,sf);
|
|
|
|
|
|
|
|
if (!segment_size)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
temp_sf = setup_subfile_streamfile(sf, segment_offset,segment_size, extension);
|
|
|
|
if (!temp_sf) goto fail;
|
|
|
|
|
|
|
|
data->segments[segment] = init_vgmstream_subfile(temp_sf);
|
|
|
|
close_streamfile(temp_sf);
|
|
|
|
if (!data->segments[segment]) goto fail;
|
|
|
|
|
|
|
|
segment_offset += segment_size;
|
|
|
|
segment++;
|
|
|
|
|
|
|
|
if (type == 9) {
|
|
|
|
//todo there are some trailing samples that must be removed for smooth loops, start skip seems ok
|
|
|
|
//not correct for all files, no idea how to calculate
|
|
|
|
data->segments[segment]->num_samples -= 374;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* setup segmented VGMSTREAMs */
|
|
|
|
if (!setup_layout_segmented(data))
|
|
|
|
goto fail;
|
|
|
|
|
|
|
|
vgmstream = allocate_segmented_vgmstream(data, loop_flag, loop_start_segment, loop_end_segment);
|
|
|
|
if (!vgmstream) goto fail;
|
|
|
|
|
|
|
|
vgmstream->sample_rate = sample_rate;
|
|
|
|
vgmstream->meta_type = meta_SPS_N1;
|
|
|
|
|
|
|
|
return vgmstream;
|
|
|
|
|
|
|
|
fail:
|
|
|
|
close_vgmstream(vgmstream);
|
|
|
|
free_layout_segmented(data);
|
|
|
|
return NULL;
|
|
|
|
}
|