2008-07-22 02:18:32 +00:00
|
|
|
#include "meta.h"
|
|
|
|
|
2018-08-26 02:26:38 +02:00
|
|
|
static void parse_adtl(off_t adtl_offset, off_t adtl_length, STREAMFILE *streamFile, long *loop_start, long *loop_end, int *loop_flag);
|
2008-07-22 02:18:32 +00:00
|
|
|
|
|
|
|
|
2018-08-26 02:26:38 +02:00
|
|
|
/* .sfl - odd RIFF-formatted files that go along with .ogg [Hanachirasu (PC), Touhou 10.5 (PC)] */
|
|
|
|
VGMSTREAM * init_vgmstream_sfl_ogg(STREAMFILE *streamFile) {
|
2008-07-22 02:18:32 +00:00
|
|
|
VGMSTREAM * vgmstream = NULL;
|
2018-08-26 02:26:38 +02:00
|
|
|
STREAMFILE * streamData = NULL;
|
2008-07-22 02:18:32 +00:00
|
|
|
int loop_flag = 0;
|
|
|
|
long loop_start_ms = -1;
|
|
|
|
long loop_end_ms = -1;
|
|
|
|
|
|
|
|
|
2018-08-26 02:26:38 +02:00
|
|
|
/* checks */
|
|
|
|
if (!check_extensions(streamFile, "sfl"))
|
2008-07-22 02:18:32 +00:00
|
|
|
goto fail;
|
2018-08-26 02:26:38 +02:00
|
|
|
if (read_32bitBE(0x00,streamFile) != 0x52494646) /* "RIFF" */
|
2008-07-22 02:18:32 +00:00
|
|
|
goto fail;
|
2018-08-26 02:26:38 +02:00
|
|
|
if (read_32bitBE(0x08,streamFile) != 0x5346504C) /* "SFPL" */
|
2008-07-22 02:18:32 +00:00
|
|
|
goto fail;
|
2018-08-26 02:26:38 +02:00
|
|
|
|
|
|
|
{
|
|
|
|
/* try with file.ogg.sfl=header and file.ogg=data [Hanachirasu (PC)] */
|
|
|
|
char basename[PATH_LIMIT];
|
|
|
|
get_streamfile_basename(streamFile,basename,PATH_LIMIT);
|
|
|
|
streamData = open_streamfile_by_filename(streamFile, basename);
|
|
|
|
if (!streamData) {
|
|
|
|
/* try again with file.sfl=header + file.ogg=daba */
|
|
|
|
streamData = open_streamfile_by_ext(streamFile,"ogg");
|
|
|
|
if (!streamData) goto fail;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
if (!check_extensions(streamData, "ogg"))
|
|
|
|
goto fail;
|
|
|
|
}
|
2008-07-22 02:18:32 +00:00
|
|
|
}
|
|
|
|
|
2018-08-26 02:26:38 +02:00
|
|
|
#ifdef VGM_USE_VORBIS
|
2008-07-22 02:18:32 +00:00
|
|
|
/* let the real initer do the parsing */
|
2018-08-26 02:26:38 +02:00
|
|
|
vgmstream = init_vgmstream_ogg_vorbis(streamData);
|
2008-07-22 02:18:32 +00:00
|
|
|
if (!vgmstream) goto fail;
|
2018-08-26 02:26:38 +02:00
|
|
|
vgmstream->meta_type = meta_OGG_SFL;
|
|
|
|
#else
|
|
|
|
goto fail;
|
|
|
|
#endif
|
2008-07-22 02:18:32 +00:00
|
|
|
|
|
|
|
/* read through chunks to verify format and find metadata */
|
|
|
|
{
|
2018-08-26 02:26:38 +02:00
|
|
|
size_t riff_size, file_size;
|
|
|
|
off_t current_chunk = 0x0c; /* start with first chunk */
|
|
|
|
|
|
|
|
riff_size = read_32bitLE(0x04,streamFile);
|
|
|
|
file_size = get_streamfile_size(streamFile);
|
|
|
|
if (file_size < riff_size+0x08)
|
|
|
|
goto fail;
|
2008-07-22 02:18:32 +00:00
|
|
|
|
|
|
|
while (current_chunk < file_size) {
|
2018-08-26 02:26:38 +02:00
|
|
|
uint32_t chunk_type = read_32bitBE(current_chunk+0x00,streamFile);
|
|
|
|
off_t chunk_size = read_32bitLE(current_chunk+0x04,streamFile);
|
2008-07-22 02:18:32 +00:00
|
|
|
|
|
|
|
/* There seem to be a few bytes left over, included in the
|
2018-08-26 02:26:38 +02:00
|
|
|
* RIFF but not enough to make a new chunk. */
|
|
|
|
if (current_chunk+0x08 > file_size) break;
|
2008-07-22 02:18:32 +00:00
|
|
|
|
2018-08-26 02:26:38 +02:00
|
|
|
if (current_chunk+0x08+chunk_size > file_size)
|
|
|
|
goto fail;
|
2008-07-22 02:18:32 +00:00
|
|
|
|
|
|
|
switch(chunk_type) {
|
2018-08-26 02:26:38 +02:00
|
|
|
case 0x4C495354: /* "LIST" */
|
|
|
|
switch (read_32bitBE(current_chunk+0x08, streamFile)) {
|
|
|
|
case 0x6164746C: /* "adtl" */
|
2008-07-22 02:18:32 +00:00
|
|
|
/* yay, atdl is its own little world */
|
2018-08-26 02:26:38 +02:00
|
|
|
parse_adtl(current_chunk + 0x08, chunk_size, streamFile,
|
2008-07-22 02:18:32 +00:00
|
|
|
&loop_start_ms,&loop_end_ms,&loop_flag);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2018-08-26 02:26:38 +02:00
|
|
|
current_chunk += 0x08+chunk_size;
|
2008-07-22 02:18:32 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-26 02:26:38 +02:00
|
|
|
/* install loops */
|
2008-07-22 02:18:32 +00:00
|
|
|
if (loop_flag) {
|
2018-08-26 02:26:38 +02:00
|
|
|
int loop_start = (long long)loop_start_ms * vgmstream->sample_rate / 1000;
|
|
|
|
int loop_end = (long long)loop_end_ms * vgmstream->sample_rate / 1000;
|
|
|
|
vgmstream_force_loop(vgmstream,loop_flag,loop_start, loop_end);
|
2008-07-22 02:18:32 +00:00
|
|
|
}
|
2018-08-26 02:26:38 +02:00
|
|
|
/* sfl .ogg often has song endings (use the base .ogg for those) */
|
2008-07-22 02:18:32 +00:00
|
|
|
|
2018-08-26 02:26:38 +02:00
|
|
|
close_streamfile(streamData);
|
2008-07-22 02:18:32 +00:00
|
|
|
return vgmstream;
|
|
|
|
|
|
|
|
fail:
|
2018-08-26 02:26:38 +02:00
|
|
|
close_streamfile(streamData);
|
|
|
|
close_vgmstream(vgmstream);
|
2008-07-22 02:18:32 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2018-08-26 02:26:38 +02:00
|
|
|
/* return milliseconds */
|
|
|
|
static long parse_adtl_marker(unsigned char * marker) {
|
|
|
|
long hh,mm,ss,ms;
|
|
|
|
|
|
|
|
if (memcmp("Marker ",marker,7)) return -1;
|
|
|
|
|
|
|
|
if (4 != sscanf((char*)marker+7,"%ld:%ld:%ld.%ld",&hh,&mm,&ss,&ms))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
return ((hh*60+mm)*60+ss)*1000+ms;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* return milliseconds */
|
|
|
|
static int parse_region(unsigned char * region, long *start, long *end) {
|
|
|
|
long start_hh,start_mm,start_ss,start_ms;
|
|
|
|
long end_hh,end_mm,end_ss,end_ms;
|
|
|
|
|
|
|
|
if (memcmp("Region ",region,7)) return -1;
|
|
|
|
|
|
|
|
if (8 != sscanf((char*)region+7,"%ld:%ld:%ld.%ld to %ld:%ld:%ld.%ld",
|
|
|
|
&start_hh,&start_mm,&start_ss,&start_ms,
|
|
|
|
&end_hh,&end_mm,&end_ss,&end_ms))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
*start = ((start_hh*60+start_mm)*60+start_ss)*1000+start_ms;
|
|
|
|
*end = ((end_hh*60+end_mm)*60+end_ss)*1000+end_ms;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* loop points have been found hiding here */
|
|
|
|
static void parse_adtl(off_t adtl_offset, off_t adtl_length, STREAMFILE *streamFile, long *loop_start, long *loop_end, int *loop_flag) {
|
|
|
|
int loop_start_found = 0;
|
|
|
|
int loop_end_found = 0;
|
|
|
|
off_t current_chunk = adtl_offset+0x04;
|
|
|
|
|
|
|
|
while (current_chunk < adtl_offset + adtl_length) {
|
|
|
|
uint32_t chunk_type = read_32bitBE(current_chunk+0x00,streamFile);
|
|
|
|
off_t chunk_size = read_32bitLE(current_chunk+0x04,streamFile);
|
|
|
|
|
|
|
|
if (current_chunk+0x08+chunk_size > adtl_offset+adtl_length)
|
|
|
|
return;
|
|
|
|
|
|
|
|
switch(chunk_type) {
|
|
|
|
case 0x6c61626c: { /* "labl" */
|
|
|
|
unsigned char *labelcontent = malloc(chunk_size-0x04);
|
|
|
|
if (!labelcontent) return;
|
|
|
|
if (read_streamfile(labelcontent,current_chunk+0x0c, chunk_size-0x04,streamFile) != chunk_size-0x04) {
|
|
|
|
free(labelcontent);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (read_32bitLE(current_chunk+8,streamFile)) {
|
|
|
|
case 1:
|
|
|
|
if (!loop_start_found && (*loop_start = parse_adtl_marker(labelcontent)) >= 0)
|
|
|
|
loop_start_found = 1;
|
|
|
|
|
|
|
|
if (!loop_start_found && !loop_end_found && parse_region(labelcontent,loop_start,loop_end) >= 0) {
|
|
|
|
loop_start_found = 1;
|
|
|
|
loop_end_found = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
if (!loop_end_found && (*loop_end = parse_adtl_marker(labelcontent)) >= 0)
|
|
|
|
loop_end_found = 1;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
free(labelcontent);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
current_chunk += 0x08 + chunk_size;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (loop_start_found && loop_end_found)
|
|
|
|
*loop_flag = 1;
|
|
|
|
|
|
|
|
/* labels don't seem to be consistently ordered */
|
|
|
|
if (*loop_start > *loop_end) {
|
|
|
|
long temp = *loop_start;
|
|
|
|
*loop_start = *loop_end;
|
|
|
|
*loop_end = temp;
|
|
|
|
}
|
|
|
|
}
|