vgmstream/src/meta/sps_n1.c
2021-06-07 00:50:54 +02:00

189 lines
5.1 KiB
C

#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), Legasista (PS3)] */
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;
uint32_t (*read_u32)(off_t,STREAMFILE*);
uint16_t (*read_u16)(off_t,STREAMFILE*);
/* checks */
if (!check_extensions(sf,"sps"))
goto fail;
if (guess_endianness32bit(0x00, sf)) { /* PS3 */
read_u32 = read_u32be;
read_u16 = read_u16be;
}
else {
read_u32 = read_u32le;
read_u16 = read_u16le;
}
type = read_u32(0x00,sf);
subfile_size = read_u32(0x04,sf);
sample_rate = read_u16(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) {
#ifdef VGM_USE_VORBIS
case 7:
init_vgmstream_subfile = init_vgmstream_ogg_vorbis;
extension = "ogg";
break;
#endif
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;
}