From 4010c3bd1b3f0aebb2133619d4aedc0285606c8d Mon Sep 17 00:00:00 2001 From: bnnm Date: Tue, 24 Sep 2019 00:51:12 +0200 Subject: [PATCH 1/4] Fix companion files in relative folders for foobar [Rayman M (PS2)] Also extra relative check for .isd and Ubi SB --- src/meta/ogg_vorbis.c | 9 +++++++- src/meta/ubi_sb.c | 2 ++ src/streamfile.c | 54 ++++++++++++++++++++++++++++++++----------- src/streamfile.h | 5 ++-- 4 files changed, 53 insertions(+), 17 deletions(-) diff --git a/src/meta/ogg_vorbis.c b/src/meta/ogg_vorbis.c index dddaebbb..7f90b179 100644 --- a/src/meta/ogg_vorbis.c +++ b/src/meta/ogg_vorbis.c @@ -326,8 +326,15 @@ VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) { if (isl_name) { 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); + + 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) { STREAMFILE *dec_sf = NULL; diff --git a/src/meta/ubi_sb.c b/src/meta/ubi_sb.c index 87a6a587..b1528ee2 100644 --- a/src/meta/ubi_sb.c +++ b/src/meta/ubi_sb.c @@ -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 */ if (sb->version == 0x000A0007 && sb->platform == UBI_PS2) { 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) { is_bia_ps2 = 1; close_streamfile(streamTest); diff --git a/src/streamfile.c b/src/streamfile.c index 46d2c789..f6cde0d1 100644 --- a/src/streamfile.c +++ b/src/streamfile.c @@ -800,25 +800,51 @@ STREAMFILE * open_streamfile_by_ext(STREAMFILE *streamFile, const char * ext) { return streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); } -STREAMFILE * open_streamfile_by_filename(STREAMFILE *streamFile, const char * name) { - char foldername[PATH_LIMIT]; - char filename[PATH_LIMIT]; - const char *path; +STREAMFILE * open_streamfile_by_filename(STREAMFILE *streamFile, const char * filename) { + char fullname[PATH_LIMIT]; + char partname[PATH_LIMIT]; + char *path, *name; - streamFile->get_name(streamFile,foldername,sizeof(foldername)); - - path = strrchr(foldername,DIR_SEPARATOR); - if (path!=NULL) path = path+1; + streamFile->get_name(streamFile, fullname, sizeof(fullname)); + //todo normalize separators in a better way, safeops, improve copying + path = strrchr(fullname,DIR_SEPARATOR); if (path) { - strcpy(filename, foldername); - filename[path-foldername] = '\0'; - strcat(filename, name); - } else { - strcpy(filename, name); + path[1] = '\0'; /* remove name after separator */ + + strcpy(partname, filename); + fix_dir_separators(partname); + + /* 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) { diff --git a/src/streamfile.h b/src/streamfile.h index 3ed3e99e..9dbee9dd 100644 --- a/src/streamfile.h +++ b/src/streamfile.h @@ -119,8 +119,9 @@ STREAMFILE * open_streamfile(STREAMFILE *streamFile, const char * pathname); * Can be used to get companion headers. */ STREAMFILE * open_streamfile_by_ext(STREAMFILE *streamFile, const char * ext); -/* Opens a STREAMFILE from a base path + new filename - * Can be used to get companion files. */ +/* Opens a STREAMFILE from a base path + new filename. + * 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); /* Reopen a STREAMFILE with a different buffer size, for fine-tuned bigfile parsing. From f95d2e3e195b8473d02b3c2d0ca35481943c8075 Mon Sep 17 00:00:00 2001 From: bnnm Date: Tue, 24 Sep 2019 00:51:26 +0200 Subject: [PATCH 2/4] Add .vam extension [Rocket Power: Beach Bandits (PS2)] --- src/formats.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/formats.c b/src/formats.c index b122b403..e0591aaf 100644 --- a/src/formats.c +++ b/src/formats.c @@ -483,6 +483,7 @@ static const char* extension_list[] = { "va3", "vag", "vai", + "vam", //txth/reserved [Rocket Power: Beach Bandits (PS2)] "vas", "vawx", "vb", From 948aa267069a4880fd8afaa88369e5cf676fe06c Mon Sep 17 00:00:00 2001 From: bnnm Date: Tue, 24 Sep 2019 00:52:48 +0200 Subject: [PATCH 3/4] Fix FSB4 loop end +1 samples and tweak loop detection [Hard Reset (PC)] --- src/meta/fsb.c | 69 ++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 50 insertions(+), 19 deletions(-) diff --git a/src/meta/fsb.c b/src/meta/fsb.c index 80156dfd..870ff178 100644 --- a/src/meta/fsb.c +++ b/src/meta/fsb.c @@ -148,6 +148,7 @@ VGMSTREAM * init_vgmstream_fsb(STREAMFILE *streamFile) { fsb.loop_start = read_32bitLE(header_offset+0x38,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; if (fsb.loop_end > fsb.num_samples) /* this seems common... */ 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 */ if (fsb.mode & FSOUND_MPEG) fsb.codec = MPEG; 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 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 */ vgmstream = allocate_vgmstream(fsb.channels,fsb.loop_flag); From 8bbf38b20fdfb9633cdae7d80f9f92f1dc9c0576 Mon Sep 17 00:00:00 2001 From: bnnm Date: Tue, 24 Sep 2019 00:53:52 +0200 Subject: [PATCH 4/4] Add Edelweiss OPUSNX .opus/lopus [Astebreed (Switch)] --- src/meta/meta.h | 4 ++-- src/meta/opus.c | 21 +++++++++++++++++++++ src/vgmstream.c | 1 + 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/src/meta/meta.h b/src/meta/meta.h index ea962817..a8fc3886 100644 --- a/src/meta/meta.h +++ b/src/meta/meta.h @@ -643,6 +643,8 @@ VGMSTREAM * init_vgmstream_opus_nus3(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_opus_sps_n1(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_opus_nxa(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); @@ -832,8 +834,6 @@ VGMSTREAM * init_vgmstream_fsb5_fev_bank(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_memory(STREAMFILE * streamFile, STREAMFILE *acbFile); diff --git a/src/meta/opus.c b/src/meta/opus.c index 7127f7ac..b9e593ae 100644 --- a/src/meta/opus.c +++ b/src/meta/opus.c @@ -390,3 +390,24 @@ VGMSTREAM * init_vgmstream_opus_prototype(STREAMFILE *streamFile) { fail: 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; +} diff --git a/src/vgmstream.c b/src/vgmstream.c index 2327bd74..e1178cf3 100644 --- a/src/vgmstream.c +++ b/src/vgmstream.c @@ -479,6 +479,7 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_xmv_valve, init_vgmstream_ubi_hx, init_vgmstream_bmp_konami, + init_vgmstream_opus_opusnx, /* 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 */