mirror of
https://github.com/vgmstream/vgmstream.git
synced 2024-12-11 06:15:59 +01:00
commit
d8fce9de7f
@ -483,6 +483,7 @@ static const char* extension_list[] = {
|
|||||||
"va3",
|
"va3",
|
||||||
"vag",
|
"vag",
|
||||||
"vai",
|
"vai",
|
||||||
|
"vam", //txth/reserved [Rocket Power: Beach Bandits (PS2)]
|
||||||
"vas",
|
"vas",
|
||||||
"vawx",
|
"vawx",
|
||||||
"vb",
|
"vb",
|
||||||
|
@ -148,6 +148,7 @@ VGMSTREAM * init_vgmstream_fsb(STREAMFILE *streamFile) {
|
|||||||
fsb.loop_start = read_32bitLE(header_offset+0x38,streamFile);
|
fsb.loop_start = read_32bitLE(header_offset+0x38,streamFile);
|
||||||
fsb.loop_end = read_32bitLE(header_offset+0x3c,streamFile);
|
fsb.loop_end = read_32bitLE(header_offset+0x3c,streamFile);
|
||||||
|
|
||||||
|
VGM_ASSERT(fsb.loop_end > fsb.num_samples, "FSB: loop end over samples (%i vs %i)\n", fsb.loop_end, fsb.num_samples);
|
||||||
fsb.channels = (fsb.mode & FSOUND_STEREO) ? 2 : 1;
|
fsb.channels = (fsb.mode & FSOUND_STEREO) ? 2 : 1;
|
||||||
if (fsb.loop_end > fsb.num_samples) /* this seems common... */
|
if (fsb.loop_end > fsb.num_samples) /* this seems common... */
|
||||||
fsb.num_samples = fsb.loop_end;
|
fsb.num_samples = fsb.loop_end;
|
||||||
@ -264,25 +265,6 @@ VGMSTREAM * init_vgmstream_fsb(STREAMFILE *streamFile) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* XOR encryption for some FSB4, though the flag is only seen after decrypting */
|
|
||||||
//;VGM_ASSERT(fsb.flags & FMOD_FSB_SOURCE_ENCRYPTED, "FSB ENCRYPTED found\n");
|
|
||||||
|
|
||||||
/* sometimes there is garbage at the end or missing bytes due to improper ripping */
|
|
||||||
VGM_ASSERT(fsb.base_header_size + fsb.sample_headers_size + fsb.sample_data_size != streamFile->get_size(streamFile),
|
|
||||||
"FSB wrong head/data_size found (expected 0x%x vs 0x%x)\n",
|
|
||||||
fsb.base_header_size + fsb.sample_headers_size + fsb.sample_data_size, streamFile->get_size(streamFile));
|
|
||||||
|
|
||||||
/* Loops unless disabled. FMOD default seems to be full loops (0/num_samples-1) without flags, for repeating tracks
|
|
||||||
* that should loop and jingles/sfx that shouldn't. We'll try to disable looping if it looks jingly enough. */
|
|
||||||
fsb.loop_flag = !(fsb.mode & FSOUND_LOOP_OFF);
|
|
||||||
if(!(fsb.mode & FSOUND_LOOP_NORMAL) /* rarely set */
|
|
||||||
&& fsb.loop_start+fsb.loop_end+1 == fsb.num_samples /* full loop */
|
|
||||||
&& fsb.num_samples < 20*fsb.sample_rate) /* in seconds (lame but no other way to know) */
|
|
||||||
fsb.loop_flag = 0;
|
|
||||||
|
|
||||||
/* ping-pong looping = no looping? (forward > reverse > forward) [ex. Biker Mice from Mars (PS2)] */
|
|
||||||
VGM_ASSERT(fsb.mode & FSOUND_LOOP_BIDI, "FSB BIDI looping found\n");
|
|
||||||
|
|
||||||
/* convert to clean some code */
|
/* convert to clean some code */
|
||||||
if (fsb.mode & FSOUND_MPEG) fsb.codec = MPEG;
|
if (fsb.mode & FSOUND_MPEG) fsb.codec = MPEG;
|
||||||
else if (fsb.mode & FSOUND_IMAADPCM) fsb.codec = IMA;
|
else if (fsb.mode & FSOUND_IMAADPCM) fsb.codec = IMA;
|
||||||
@ -293,6 +275,55 @@ VGMSTREAM * init_vgmstream_fsb(STREAMFILE *streamFile) {
|
|||||||
else if (fsb.mode & FSOUND_8BITS) fsb.codec = PCM8;
|
else if (fsb.mode & FSOUND_8BITS) fsb.codec = PCM8;
|
||||||
else fsb.codec = PCM16;
|
else fsb.codec = PCM16;
|
||||||
|
|
||||||
|
/* correct compared to FMOD's tools */
|
||||||
|
if (fsb.loop_end)
|
||||||
|
fsb.loop_end += 1;
|
||||||
|
|
||||||
|
/* ping-pong looping = no looping? (forward > reverse > forward) [ex. Biker Mice from Mars (PS2)] */
|
||||||
|
VGM_ASSERT(fsb.mode & FSOUND_LOOP_BIDI, "FSB BIDI looping found\n");
|
||||||
|
VGM_ASSERT(fsb.mode & FSOUND_LOOP_OFF, "FSB LOOP OFF found\n"); /* sometimes used */
|
||||||
|
VGM_ASSERT(fsb.mode & FSOUND_LOOP_NORMAL, "FSB LOOP NORMAL found\n"); /* very rarely set */
|
||||||
|
/* XOR encryption for some FSB4, though the flag is only seen after decrypting */
|
||||||
|
//;VGM_ASSERT(fsb.flags & FMOD_FSB_SOURCE_ENCRYPTED, "FSB ENCRYPTED found\n");
|
||||||
|
|
||||||
|
/* sometimes there is garbage at the end or missing bytes due to improper ripping */
|
||||||
|
VGM_ASSERT(fsb.base_header_size + fsb.sample_headers_size + fsb.sample_data_size != streamFile->get_size(streamFile),
|
||||||
|
"FSB wrong head/data_size found (expected 0x%x vs 0x%x)\n",
|
||||||
|
fsb.base_header_size + fsb.sample_headers_size + fsb.sample_data_size, streamFile->get_size(streamFile));
|
||||||
|
|
||||||
|
/* autodetect unwanted loops */
|
||||||
|
{
|
||||||
|
/* FMOD tool's default behaviour is creating files with full loops and no flags unless disabled
|
||||||
|
* manually (can be overriden during program too), for all FSB versions. This makes jingles/sfx/voices
|
||||||
|
* loop when they shouldn't, but most music does full loops seamlessly, so we only want to disable
|
||||||
|
* if it looks jingly enough. Incidentally, their tools can only make files with full loops. */
|
||||||
|
int enable_loop, full_loop, is_small;
|
||||||
|
|
||||||
|
/* seems to mean forced loop */
|
||||||
|
enable_loop = (fsb.mode & FSOUND_LOOP_NORMAL);
|
||||||
|
|
||||||
|
/* for MPEG and CELT sometimes full loops are given with around/exact 1 frame less than num_samples,
|
||||||
|
* probably to account for encoder/decoder delay (ex. The Witcher 2, Hard Reset, Timeshift) */
|
||||||
|
if (fsb.codec == CELT)
|
||||||
|
full_loop = fsb.loop_start == 0 && fsb.loop_end >= fsb.num_samples - 512; /* maybe around 300? */
|
||||||
|
else if (fsb.codec == MPEG)
|
||||||
|
full_loop = fsb.loop_start == 0 && fsb.loop_end >= fsb.num_samples - 1152;
|
||||||
|
else
|
||||||
|
full_loop = fsb.loop_start == 0 && fsb.loop_end == fsb.num_samples;
|
||||||
|
|
||||||
|
/* in seconds (lame but no better way) */
|
||||||
|
is_small = fsb.num_samples < 20 * fsb.sample_rate;
|
||||||
|
|
||||||
|
//;VGM_LOG("FSB loop start=%i, loop end=%i, samples=%i, mode=%x\n", fsb.loop_start, fsb.loop_end, fsb.num_samples, fsb.mode);
|
||||||
|
//;VGM_LOG("FSB: enable=%i, full=%i, small=%i\n",enable_loop,full_loop,is_small );
|
||||||
|
|
||||||
|
fsb.loop_flag = !(fsb.mode & FSOUND_LOOP_OFF); /* disabled manually */
|
||||||
|
if (fsb.loop_flag && !enable_loop && full_loop && is_small) {
|
||||||
|
VGM_LOG("FSB: disable unwanted loop\n");
|
||||||
|
fsb.loop_flag = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* build the VGMSTREAM */
|
/* build the VGMSTREAM */
|
||||||
vgmstream = allocate_vgmstream(fsb.channels,fsb.loop_flag);
|
vgmstream = allocate_vgmstream(fsb.channels,fsb.loop_flag);
|
||||||
|
@ -643,6 +643,8 @@ VGMSTREAM * init_vgmstream_opus_nus3(STREAMFILE * streamFile);
|
|||||||
VGMSTREAM * init_vgmstream_opus_sps_n1(STREAMFILE * streamFile);
|
VGMSTREAM * init_vgmstream_opus_sps_n1(STREAMFILE * streamFile);
|
||||||
VGMSTREAM * init_vgmstream_opus_nxa(STREAMFILE * streamFile);
|
VGMSTREAM * init_vgmstream_opus_nxa(STREAMFILE * streamFile);
|
||||||
VGMSTREAM * init_vgmstream_opus_opusx(STREAMFILE * streamFile);
|
VGMSTREAM * init_vgmstream_opus_opusx(STREAMFILE * streamFile);
|
||||||
|
VGMSTREAM * init_vgmstream_opus_prototype(STREAMFILE * streamFile);
|
||||||
|
VGMSTREAM * init_vgmstream_opus_opusnx(STREAMFILE * streamFile);
|
||||||
|
|
||||||
VGMSTREAM * init_vgmstream_pc_al2(STREAMFILE * streamFile);
|
VGMSTREAM * init_vgmstream_pc_al2(STREAMFILE * streamFile);
|
||||||
|
|
||||||
@ -832,8 +834,6 @@ VGMSTREAM * init_vgmstream_fsb5_fev_bank(STREAMFILE * streamFile);
|
|||||||
|
|
||||||
VGMSTREAM * init_vgmstream_bwav(STREAMFILE * streamFile);
|
VGMSTREAM * init_vgmstream_bwav(STREAMFILE * streamFile);
|
||||||
|
|
||||||
VGMSTREAM * init_vgmstream_opus_prototype(STREAMFILE * streamFile);
|
|
||||||
|
|
||||||
VGMSTREAM * init_vgmstream_awb(STREAMFILE * streamFile);
|
VGMSTREAM * init_vgmstream_awb(STREAMFILE * streamFile);
|
||||||
VGMSTREAM * init_vgmstream_awb_memory(STREAMFILE * streamFile, STREAMFILE *acbFile);
|
VGMSTREAM * init_vgmstream_awb_memory(STREAMFILE * streamFile, STREAMFILE *acbFile);
|
||||||
|
|
||||||
|
@ -326,8 +326,15 @@ VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) {
|
|||||||
if (isl_name) {
|
if (isl_name) {
|
||||||
STREAMFILE *islFile = NULL;
|
STREAMFILE *islFile = NULL;
|
||||||
|
|
||||||
//todo could try in ../(file) first since that's how the .isl is stored
|
|
||||||
islFile = open_streamfile_by_filename(streamFile, isl_name);
|
islFile = open_streamfile_by_filename(streamFile, isl_name);
|
||||||
|
|
||||||
|
if (!islFile) {
|
||||||
|
/* try in ../(file) too since that's how the .isl is stored on disc */
|
||||||
|
char isl_path[PATH_LIMIT];
|
||||||
|
snprintf(isl_path, sizeof(isl_path), "../%s", isl_name);
|
||||||
|
islFile = open_streamfile_by_filename(streamFile, isl_path);
|
||||||
|
}
|
||||||
|
|
||||||
if (islFile) {
|
if (islFile) {
|
||||||
STREAMFILE *dec_sf = NULL;
|
STREAMFILE *dec_sf = NULL;
|
||||||
|
|
||||||
|
@ -390,3 +390,24 @@ VGMSTREAM * init_vgmstream_opus_prototype(STREAMFILE *streamFile) {
|
|||||||
fail:
|
fail:
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Edelweiss variation [Astebreed (Switch)] */
|
||||||
|
VGMSTREAM * init_vgmstream_opus_opusnx(STREAMFILE *streamFile) {
|
||||||
|
off_t offset = 0;
|
||||||
|
int num_samples = 0, loop_start = 0, loop_end = 0;
|
||||||
|
|
||||||
|
/* checks */
|
||||||
|
if (!check_extensions(streamFile, "opus,lopus"))
|
||||||
|
goto fail;
|
||||||
|
if (read_64bitBE(0x00, streamFile) != 0x4F5055534E580000) /* "OPUSNX\0\0" */
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
offset = 0x10;
|
||||||
|
num_samples = 0; //read_32bitLE(0x08, streamFile); /* samples with encoder delay */
|
||||||
|
if (read_32bitLE(0x0c, streamFile) != 0)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
return init_vgmstream_opus(streamFile, meta_OPUS, offset, num_samples, loop_start, loop_end);
|
||||||
|
fail:
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
@ -2399,6 +2399,8 @@ static int config_sb_version(ubi_sb_header * sb, STREAMFILE *streamFile) {
|
|||||||
/* two configs with same id; use project file as identifier */
|
/* two configs with same id; use project file as identifier */
|
||||||
if (sb->version == 0x000A0007 && sb->platform == UBI_PS2) {
|
if (sb->version == 0x000A0007 && sb->platform == UBI_PS2) {
|
||||||
STREAMFILE * streamTest = open_streamfile_by_filename(streamFile, "BIAAUDIO.SP1");
|
STREAMFILE * streamTest = open_streamfile_by_filename(streamFile, "BIAAUDIO.SP1");
|
||||||
|
if (!streamTest) /* try again for localized subfolders */
|
||||||
|
streamTest = open_streamfile_by_filename(streamFile, "../BIAAUDIO.SP1");
|
||||||
if (streamTest) {
|
if (streamTest) {
|
||||||
is_bia_ps2 = 1;
|
is_bia_ps2 = 1;
|
||||||
close_streamfile(streamTest);
|
close_streamfile(streamTest);
|
||||||
|
@ -800,25 +800,51 @@ STREAMFILE * open_streamfile_by_ext(STREAMFILE *streamFile, const char * ext) {
|
|||||||
return streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE);
|
return streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
STREAMFILE * open_streamfile_by_filename(STREAMFILE *streamFile, const char * name) {
|
STREAMFILE * open_streamfile_by_filename(STREAMFILE *streamFile, const char * filename) {
|
||||||
char foldername[PATH_LIMIT];
|
char fullname[PATH_LIMIT];
|
||||||
char filename[PATH_LIMIT];
|
char partname[PATH_LIMIT];
|
||||||
const char *path;
|
char *path, *name;
|
||||||
|
|
||||||
streamFile->get_name(streamFile,foldername,sizeof(foldername));
|
streamFile->get_name(streamFile, fullname, sizeof(fullname));
|
||||||
|
|
||||||
path = strrchr(foldername,DIR_SEPARATOR);
|
|
||||||
if (path!=NULL) path = path+1;
|
|
||||||
|
|
||||||
|
//todo normalize separators in a better way, safeops, improve copying
|
||||||
|
path = strrchr(fullname,DIR_SEPARATOR);
|
||||||
if (path) {
|
if (path) {
|
||||||
strcpy(filename, foldername);
|
path[1] = '\0'; /* remove name after separator */
|
||||||
filename[path-foldername] = '\0';
|
|
||||||
strcat(filename, name);
|
strcpy(partname, filename);
|
||||||
} else {
|
fix_dir_separators(partname);
|
||||||
strcpy(filename, name);
|
|
||||||
|
/* normalize relative paths as don't work ok in some plugins */
|
||||||
|
if (partname[0]=='.' && partname[1] == DIR_SEPARATOR) { /* './name' */
|
||||||
|
name = partname + 2; /* ignore './' */
|
||||||
|
}
|
||||||
|
else if (partname[0]=='.' && partname[1]=='.' && partname[2] == DIR_SEPARATOR) { /* '../name' */
|
||||||
|
char *pathprev;
|
||||||
|
|
||||||
|
path[0] = '\0'; /* remove last separator so next call works */
|
||||||
|
pathprev = strrchr(fullname,DIR_SEPARATOR);
|
||||||
|
if (pathprev) {
|
||||||
|
pathprev[1] = '\0'; /* remove prev dir after separator */
|
||||||
|
name = partname + 3; /* ignore '../' */
|
||||||
|
}
|
||||||
|
else { /* let plugin handle? */
|
||||||
|
path[0] = DIR_SEPARATOR;
|
||||||
|
name = partname;
|
||||||
|
}
|
||||||
|
/* could work with more relative paths but whatevs */
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
name = partname;
|
||||||
|
}
|
||||||
|
|
||||||
|
strcat(fullname, name);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
strcpy(fullname, filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
return streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE);
|
return streamFile->open(streamFile, fullname, STREAMFILE_DEFAULT_BUFFER_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
STREAMFILE * reopen_streamfile(STREAMFILE *streamFile, size_t buffer_size) {
|
STREAMFILE * reopen_streamfile(STREAMFILE *streamFile, size_t buffer_size) {
|
||||||
|
@ -119,8 +119,9 @@ STREAMFILE * open_streamfile(STREAMFILE *streamFile, const char * pathname);
|
|||||||
* Can be used to get companion headers. */
|
* Can be used to get companion headers. */
|
||||||
STREAMFILE * open_streamfile_by_ext(STREAMFILE *streamFile, const char * ext);
|
STREAMFILE * open_streamfile_by_ext(STREAMFILE *streamFile, const char * ext);
|
||||||
|
|
||||||
/* Opens a STREAMFILE from a base path + new filename
|
/* Opens a STREAMFILE from a base path + new filename.
|
||||||
* Can be used to get companion files. */
|
* Can be used to get companion files. Relative paths like
|
||||||
|
* './filename', '../filename', 'dir/filename' also work. */
|
||||||
STREAMFILE * open_streamfile_by_filename(STREAMFILE *streamFile, const char * filename);
|
STREAMFILE * open_streamfile_by_filename(STREAMFILE *streamFile, const char * filename);
|
||||||
|
|
||||||
/* Reopen a STREAMFILE with a different buffer size, for fine-tuned bigfile parsing.
|
/* Reopen a STREAMFILE with a different buffer size, for fine-tuned bigfile parsing.
|
||||||
|
@ -479,6 +479,7 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = {
|
|||||||
init_vgmstream_xmv_valve,
|
init_vgmstream_xmv_valve,
|
||||||
init_vgmstream_ubi_hx,
|
init_vgmstream_ubi_hx,
|
||||||
init_vgmstream_bmp_konami,
|
init_vgmstream_bmp_konami,
|
||||||
|
init_vgmstream_opus_opusnx,
|
||||||
|
|
||||||
/* lowest priority metas (should go after all metas, and TXTH should go before raw formats) */
|
/* 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 */
|
init_vgmstream_txth, /* proper parsers should supersede TXTH, once added */
|
||||||
|
Loading…
Reference in New Issue
Block a user