Updated VGMStream to r1640-104-gc92e4f85

CQTexperiment
Christopher Snowhill 2021-10-05 20:31:37 -07:00
parent 3aa2e3149f
commit ac998301c6
14 changed files with 236 additions and 40 deletions

View File

@ -518,6 +518,8 @@
839933612591E8C1001855AF /* sbk.c in Sources */ = {isa = PBXBuildFile; fileRef = 8399335E2591E8C0001855AF /* sbk.c */; };
83997F5B22D9569E00633184 /* rad.c in Sources */ = {isa = PBXBuildFile; fileRef = 83997F5722D9569E00633184 /* rad.c */; };
839B54521EEE1D9600048A2D /* ngc_ulw.c in Sources */ = {isa = PBXBuildFile; fileRef = 831BD11F1EEE1CF200198540 /* ngc_ulw.c */; };
839C3D27270D49FF00E13653 /* lpcm_fb.c in Sources */ = {isa = PBXBuildFile; fileRef = 839C3D22270D49FF00E13653 /* lpcm_fb.c */; };
839C3D28270D49FF00E13653 /* lopu.c in Sources */ = {isa = PBXBuildFile; fileRef = 839C3D26270D49FF00E13653 /* lopu.c */; };
839E21E01F2EDAF100EE54D7 /* vorbis_custom_data_fsb.h in Headers */ = {isa = PBXBuildFile; fileRef = 839E21D61F2EDAF000EE54D7 /* vorbis_custom_data_fsb.h */; };
839E21E11F2EDAF100EE54D7 /* vorbis_custom_decoder.c in Sources */ = {isa = PBXBuildFile; fileRef = 839E21D71F2EDAF000EE54D7 /* vorbis_custom_decoder.c */; };
839E21E21F2EDAF100EE54D7 /* vorbis_custom_utils_wwise.c in Sources */ = {isa = PBXBuildFile; fileRef = 839E21D81F2EDAF000EE54D7 /* vorbis_custom_utils_wwise.c */; };
@ -1328,6 +1330,8 @@
8399335D2591E8C0001855AF /* ubi_sb_garbage_streamfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ubi_sb_garbage_streamfile.h; sourceTree = "<group>"; };
8399335E2591E8C0001855AF /* sbk.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = sbk.c; sourceTree = "<group>"; };
83997F5722D9569E00633184 /* rad.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = rad.c; sourceTree = "<group>"; };
839C3D22270D49FF00E13653 /* lpcm_fb.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = lpcm_fb.c; sourceTree = "<group>"; };
839C3D26270D49FF00E13653 /* lopu.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = lopu.c; sourceTree = "<group>"; };
839E21D61F2EDAF000EE54D7 /* vorbis_custom_data_fsb.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = vorbis_custom_data_fsb.h; sourceTree = "<group>"; };
839E21D71F2EDAF000EE54D7 /* vorbis_custom_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = vorbis_custom_decoder.c; sourceTree = "<group>"; };
839E21D81F2EDAF000EE54D7 /* vorbis_custom_utils_wwise.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = vorbis_custom_utils_wwise.c; sourceTree = "<group>"; };
@ -1945,6 +1949,8 @@
83D20074248DDB760048BD24 /* ktsr.c */,
830EBE122004656E0023AA10 /* ktss.c */,
8373342423F60CDB00DE14DC /* kwb.c */,
839C3D26270D49FF00E13653 /* lopu.c */,
839C3D22270D49FF00E13653 /* lpcm_fb.c */,
8373341F23F60CDB00DE14DC /* lrmd_streamfile.h */,
8373342223F60CDB00DE14DC /* lrmd.c */,
836F6E5A18BDC2180095E648 /* lsf.c */,
@ -2656,6 +2662,7 @@
834FE109215C79ED000A5D3D /* idsp_ie.c in Sources */,
83299FD01E7660C7003A3242 /* bik.c in Sources */,
8346D97C25BF838C00D1A8B0 /* mjb_mjh.c in Sources */,
839C3D27270D49FF00E13653 /* lpcm_fb.c in Sources */,
8373341823F60C7B00DE14DC /* tgcadpcm_decoder.c in Sources */,
83FC417326D3304D009A2022 /* xsh_xsd_xss.c in Sources */,
836F6F3318BDC2190095E648 /* ngc_dtk_decoder.c in Sources */,
@ -2756,6 +2763,7 @@
83AA7F812519C042004C5298 /* silence.c in Sources */,
834FE0B3215C798C000A5D3D /* acm_decoder_util.c in Sources */,
837CEA7A23487E2500E62A4A /* ffmpeg_decoder_utils.c in Sources */,
839C3D28270D49FF00E13653 /* lopu.c in Sources */,
837CEAF823487F2C00E62A4A /* xmv_valve.c in Sources */,
836F6F6718BDC2190095E648 /* acm.c in Sources */,
834FE0FD215C79ED000A5D3D /* vpk.c in Sources */,

View File

@ -404,9 +404,9 @@ ffmpeg_codec_data* init_ffmpeg_header_offset_subsong(STREAMFILE* sf, uint8_t* he
VGM_ASSERT(stream->codecpar->trailing_padding > 0, "FFMPEG: trailing_padding %i\n", (int)stream->codecpar->trailing_padding);
VGM_ASSERT(stream->codecpar->seek_preroll > 0, "FFMPEG: seek_preroll %i\n", (int)stream->codecpar->seek_preroll);//seek delay: OPUS
VGM_ASSERT(stream->start_time > 0, "FFMPEG: start_time %i\n", (int)stream->start_time); //delay
#if 0 //LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(58, 64, 100)
VGM_ASSERT(stream->first_discard_sample > 0, "FFMPEG: first_discard_sample %i\n", (int)stream->first_discard_sample); //padding: MP3
VGM_ASSERT(stream->last_discard_sample > 0, "FFMPEG: last_discard_sample %i\n", (int)stream->last_discard_sample); //padding: MP3
#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(58, 64, 100)
VGM_ASSERT(stream->skip_samples > 0, "FFMPEG: skip_samples %i\n", (int)stream->skip_samples); //delay: MP4
VGM_ASSERT(stream->start_skip_samples > 0, "FFMPEG: start_skip_samples %i\n", (int)stream->start_skip_samples); //delay: MP3
#endif

View File

@ -349,7 +349,7 @@ static STREAMFILE* setup_opus_streamfile(STREAMFILE* sf, opus_config* cfg, off_t
/* setup subfile */
new_sf = open_wrap_streamfile(sf);
new_sf = open_io_streamfile_ex_f(new_sf, &io_data, sizeof(opus_io_data), opus_io_read, opus_io_size, opus_io_init, opus_io_close);
new_sf = open_buffer_streamfile_f(new_sf, 0);
//new_sf = open_buffer_streamfile_f(new_sf, 0); /* seems slightly slower on typical files */
return new_sf;
fail:
return NULL;

View File

@ -266,6 +266,7 @@ static const char* extension_list[] = {
"l",
"l00", //txth/reserved [Disney's Dinosaur (PS2)]
"laac", //fake extension for .aac (tri-Ace)
"ladpcm", //not fake
"laif", //fake extension for .aif (various)
"laiff", //fake extension for .aiff
"laifc", //fake extension for .aifc
@ -289,7 +290,7 @@ static const char* extension_list[] = {
"lmp4", //fake extension for .mp4
"lmpc", //fake extension for .mpc, FFmpeg/not parsed
"logg", //fake extension for .ogg
"lopus", //fake extension for .opus
"lopus", //fake extension for .opus, used by LOPU too
"lp",
"lpcm",
"lpk",
@ -1364,6 +1365,8 @@ static const meta_info meta_info_list[] = {
{meta_BNK_RELIC, "Relic BNK header"},
{meta_XSH_XSD_XSS, "Treyarch XSH+XSD/XSS header"},
{meta_PSB, "M2 PSB header"},
{meta_LOPU_FB, "French-Bread LOPU header"},
{meta_LPCM_FB, "French-Bread LPCM header"},
};
void get_vgmstream_coding_description(VGMSTREAM* vgmstream, char* out, size_t out_size) {

View File

@ -3,7 +3,7 @@
#include "../coding/coding.h"
#include "../coding/hca_decoder_clhca.h"
//#define HCA_BRUTEFORCE
#define HCA_BRUTEFORCE
#ifdef HCA_BRUTEFORCE
static void bruteforce_hca_key(STREAMFILE* sf, hca_codec_data* hca_data, unsigned long long* p_keycode, uint16_t subkey);
#endif
@ -22,12 +22,12 @@ VGMSTREAM* init_vgmstream_hca_subkey(STREAMFILE* sf, uint16_t subkey) {
/* checks */
if ((read_u32be(0x00,sf) & 0x7F7F7F7F) != get_id32be("HCA\0")) /* masked in encrypted files */
goto fail;
if (!check_extensions(sf, "hca"))
return NULL;
if ((read_u32be(0x00,sf) & 0x7F7F7F7F) != 0x48434100) /* "HCA\0", possibly masked */
goto fail;
/* init vgmstream and library's context, will validate the HCA */
hca_data = init_hca(sf);
if (!hca_data) {
@ -212,15 +212,15 @@ static void bruteforce_hca_key_bin_type(STREAMFILE* sf, hca_codec_data* hca_data
off_t keys_size, bytes;
int pos;
uint64_t old_key = 0;
uint64_t key = 0;
VGM_LOG("HCA: test keys.bin (type %i)\n", type);
*p_keycode = 0;
/* load whole file in memory for performance (exes with keys shouldn't be too big) */
sf_keys = open_streamfile_by_filename(sf, "keys.bin");
if (!sf_keys) goto done;
if (!sf_keys) return;
VGM_LOG("HCA: test keys.bin (type %i)\n", type);
*p_keycode = 0;
keys_size = get_streamfile_size(sf_keys);
@ -234,7 +234,6 @@ static void bruteforce_hca_key_bin_type(STREAMFILE* sf, hca_codec_data* hca_data
pos = 0;
while (pos < keys_size - 4) {
uint64_t key;
VGM_ASSERT(pos % 0x1000000 == 0, "HCA: pos %x...\n", pos);
/* keys are usually u64le but other orders may exist */
@ -256,8 +255,10 @@ static void bruteforce_hca_key_bin_type(STREAMFILE* sf, hca_codec_data* hca_data
cur_score = 0;
test_key(hca_data, key, subkey, &cur_score, p_keycode);
if (cur_score == 1)
if (cur_score == 1) {
best_score = cur_score;
goto done;
}
if (cur_score > 0 && cur_score <= 500) {
VGM_LOG("HCA: possible key=%08x%08x (score=%i) at %x\n",
@ -268,25 +269,32 @@ static void bruteforce_hca_key_bin_type(STREAMFILE* sf, hca_codec_data* hca_data
}
done:
VGM_ASSERT(best_score > 0, "HCA: best key=%08x%08x (score=%i)\n",
(uint32_t)((*p_keycode >> 32) & 0xFFFFFFFF), (uint32_t)(*p_keycode & 0xFFFFFFFF), best_score);
VGM_ASSERT(best_score < 0, "HCA: key not found\n");
if (best_score < 0 || best_score > 10000)
if (best_score < 0 || best_score > 10000) {
*p_keycode = 0;
VGM_LOG("HCA: good key not found\n");
}
else {
/* print key as p_keycode includes subkey */
VGM_LOG("HCA: best key=%08x%08x (score=%i)\n",
(uint32_t)((key >> 32) & 0xFFFFFFFF), (uint32_t)(key & 0xFFFFFFFF), best_score);
}
close_streamfile(sf_keys);
free(buf);
}
static void bruteforce_hca_key_bin(STREAMFILE* sf, hca_codec_data* hca_data, unsigned long long* p_keycode, uint16_t subkey) {
bruteforce_hca_key_bin_type(sf, hca_data, p_keycode, subkey, HBF_TYPE_64LE_4);
bruteforce_hca_key_bin_type(sf, hca_data, p_keycode, subkey, HBF_TYPE_64BE_4);
bruteforce_hca_key_bin_type(sf, hca_data, p_keycode, subkey, HBF_TYPE_32LE_4);
bruteforce_hca_key_bin_type(sf, hca_data, p_keycode, subkey, HBF_TYPE_32BE_4);
bruteforce_hca_key_bin_type(sf, hca_data, p_keycode, subkey, HBF_TYPE_64LE_1);
bruteforce_hca_key_bin_type(sf, hca_data, p_keycode, subkey, HBF_TYPE_64BE_1);
/*
bruteforce_hca_key_bin_type(sf, hca_data, p_keycode, subkey, HBF_TYPE_32LE_1);
bruteforce_hca_key_bin_type(sf, hca_data, p_keycode, subkey, HBF_TYPE_64BE_1);
bruteforce_hca_key_bin_type(sf, hca_data, p_keycode, subkey, HBF_TYPE_32BE_1);
bruteforce_hca_key_bin_type(sf, hca_data, p_keycode, subkey, HBF_TYPE_64LE_4);
bruteforce_hca_key_bin_type(sf, hca_data, p_keycode, subkey, HBF_TYPE_32LE_4);
bruteforce_hca_key_bin_type(sf, hca_data, p_keycode, subkey, HBF_TYPE_64BE_4);
bruteforce_hca_key_bin_type(sf, hca_data, p_keycode, subkey, HBF_TYPE_32BE_4);
*/
}
@ -303,13 +311,13 @@ static void bruteforce_hca_key_txt(STREAMFILE* sf, hca_codec_data* hca_data, uns
char line[1024];
VGM_LOG("HCA: test keys.txt\n");
*p_keycode = 0;
/* load whole file in memory for performance (exes with keys shouldn't be too big) */
sf_keys = open_streamfile_by_filename(sf, "keys.txt");
if (!sf_keys) goto done;
if (!sf_keys) return;
VGM_LOG("HCA: test keys.txt\n");
*p_keycode = 0;
keys_size = get_streamfile_size(sf_keys);

View File

@ -758,9 +758,24 @@ static const hcakey_info hcakey_list[] = {
// m HOLD'EM (Android)
{369211553984367}, // 00014FCBC385AF6F
// Sonic Colors Ultimate (multi)
{1991062320101111}, // 000712DC5250B6F7
// ALTDEUS: Beyond Chronos (PC) [base-string 14238637353525924 + mods)]
{14238637353525934}, // 003295F7198AE2AE - bgm
{14238637353525954}, // 003295F7198AE2C2 - se
{14238637353525944}, // 003295F7198AE2B8 - voice
// SHOW BY ROCK!! Fes A Live (Android)
{54605542411982574}, // 00C1FF73963BD6EE
// Touhou Danmaku Kagura (Android)
{5654863921795627}, // 001417119B4FD22B
// Nogizaka 46 Fractal (Android)
{984635491346198130}, // 0DAA20C336EEAE72
};
#endif/*_HCA_KEYS_H_*/

View File

@ -0,0 +1,61 @@
#include "meta.h"
#include "../coding/coding.h"
/* LOPU - French-Bread's Opus [Melty Blood: Type Lumina (Switch)] */
VGMSTREAM* init_vgmstream_lopu_fb(STREAMFILE* sf) {
VGMSTREAM* vgmstream = NULL;
uint32_t start_offset, data_size;
int loop_flag, channels, sample_rate;
int32_t num_samples, loop_start, loop_end, skip;
/* checks */
if (!is_id32be(0x00, sf, "LOPU"))
goto fail;
/* .lopus: real extension (honest) */
if (!check_extensions(sf, "lopus"))
goto fail;
start_offset = read_u32le(0x04, sf);
sample_rate = read_s32le(0x08, sf);
channels = read_s16le(0x0c, sf);
/* 0x10: ? (1984) */
num_samples = read_s32le(0x14, sf);
loop_start = read_s32le(0x18, sf);
loop_end = read_s32le(0x1c, sf) + 1;
/* 0x20: frame size */
skip = read_s16le(0x24, sf);
data_size = read_u32le(0x28, sf);
/* rest: null */
loop_flag = (loop_end > 0); /* -1 if no loop */
num_samples -= skip;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channels, loop_flag);
if (!vgmstream) goto fail;
vgmstream->meta_type = meta_LOPU_FB;
vgmstream->sample_rate = sample_rate;
vgmstream->num_samples = num_samples;
vgmstream->loop_start_sample = loop_start;
vgmstream->loop_end_sample = loop_end;
#ifdef VGM_USE_FFMPEG
vgmstream->codec_data = init_ffmpeg_switch_opus(sf, start_offset, data_size, vgmstream->channels, skip, vgmstream->sample_rate);
if (!vgmstream->codec_data) goto fail;
vgmstream->coding_type = coding_FFmpeg;
vgmstream->layout_type = layout_none;
#else
goto fail;
#endif
if (!vgmstream_open_stream(vgmstream, sf, start_offset))
goto fail;
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;
}

View File

@ -0,0 +1,61 @@
#include "meta.h"
#include "../coding/coding.h"
/* LPCM - French-Bread's DSP [Melty Blood: Type Lumina (Switch)] */
VGMSTREAM* init_vgmstream_lpcm_fb(STREAMFILE* sf) {
VGMSTREAM* vgmstream = NULL;
uint32_t start_offset;
int loop_flag, channels, sample_rate;
int32_t num_samples;
/* checks */
if (!is_id32be(0x00, sf, "LPCM"))
goto fail;
/* .ladpcm: real extension (honest) */
if (!check_extensions(sf, "ladpcm"))
goto fail;
/* 0x04: dsp offset (0x20) */
if (read_u32le(0x04, sf) != 0x20)
goto fail;
num_samples = read_s32le(0x20, sf);
/* 0x24: nibbles? */
sample_rate = read_s32le(0x28, sf);
/* 0x2c: 0? */
/* 0x30: 2? */
/* 0x34: nibbles? */
/* 0x38: 2? */
if (read_u32le(0x38, sf) != 2)
goto fail;
channels = 1;
loop_flag = 0;
start_offset = 0x78; /* could be 0x80 but this is closer to num_samples */
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channels, loop_flag);
if (!vgmstream) goto fail;
vgmstream->meta_type = meta_LPCM_FB;
vgmstream->sample_rate = sample_rate;
vgmstream->num_samples = num_samples;
vgmstream->coding_type = coding_NGC_DSP;
vgmstream->layout_type = layout_none;
dsp_read_coefs_le(vgmstream, sf, 0x3c, 0);
/* 0x5c: hist? */
if (!vgmstream_open_stream(vgmstream, sf, start_offset))
goto fail;
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;
}

View File

@ -964,4 +964,8 @@ VGMSTREAM* init_vgmstream_xsh_xsd_xss(STREAMFILE* sf);
VGMSTREAM* init_vgmstream_psb(STREAMFILE* sf);
VGMSTREAM* init_vgmstream_lopu_fb(STREAMFILE* sf);
VGMSTREAM* init_vgmstream_lpcm_fb(STREAMFILE* sf);
#endif /*_META_H*/

View File

@ -25,16 +25,16 @@ VGMSTREAM* init_vgmstream_mp4_aac_ffmpeg(STREAMFILE* sf) {
/* checks */
/* .bin: Final Fantasy Dimensions (iOS), Final Fantasy V (iOS)
* .msd: UNO (iOS) */
if (!check_extensions(sf,"mp4,m4a,m4v,lmp4,bin,lbin,msd"))
goto fail;
if ((read_u32be(0x00,sf) & 0xFFFFFF00) != 0) /* first atom BE size (usually ~0x18) */
goto fail;
if (!is_id32be(0x04,sf, "ftyp"))
goto fail;
/* .bin: Final Fantasy Dimensions (iOS), Final Fantasy V (iOS)
* .msd: UNO (iOS) */
if (!check_extensions(sf,"mp4,m4a,m4v,lmp4,bin,lbin,msd"))
goto fail;
file_size = get_streamfile_size(sf);
ffmpeg_data = init_ffmpeg_offset(sf, start_offset, file_size);
@ -86,7 +86,7 @@ fail:
/* read useful MP4 chunks */
static void parse_mp4(STREAMFILE* sf, mp4_header* mp4) {
off_t offset, suboffset, max_offset, max_suboffset;
uint32_t offset, suboffset, max_offset, max_suboffset;
/* MOV format chunks, called "atoms", size goes first because Apple */
@ -117,12 +117,25 @@ static void parse_mp4(STREAMFILE* sf, mp4_header* mp4) {
mp4->loop_start = read_u32be(offset + 0x08 + 0x2c,sf);
mp4->loop_end = read_u32be(offset + 0x08 + 0x30,sf);
}
/* could stop reading since FFmpeg will too */
max_offset = 0;
}
/* M2 emu's .m4a (codename zoom) */
else if (is_id32be(offset + 0x08 + 0x00,sf, "ZOOM")) {
/* 0x00: id */
mp4->encoder_delay = read_s32be(offset + 0x08 + 0x04,sf); /* Apple's 2112, also in iTunes tag */
/* 0x08: end padding */
mp4->num_samples = read_s32be(offset + 0x08 + 0x0c,sf);
mp4->loop_start = read_s32be(offset + 0x08 + 0x10,sf);
mp4->loop_end = read_s32be(offset + 0x08 + 0x14,sf);
mp4->loop_flag = (mp4->loop_end != 0);
if (mp4->loop_flag)
mp4->loop_end++; /* assumed, matches num_samples this way */
max_offset = 0;
}
break;
case 0x6D6F6F76: { /* "moov" (header) */
suboffset = offset += 0x08;
suboffset = offset + 0x08;
max_suboffset = offset + size;
while (suboffset < max_suboffset) {
uint32_t subsize = read_u32be(suboffset + 0x00,sf);
@ -145,6 +158,7 @@ static void parse_mp4(STREAMFILE* sf, mp4_header* mp4) {
mp4->num_samples = read_s32be(criw_offset + 0x0c,sf);
mp4->loop_flag = (mp4->loop_end > 0);
/* next 2 fields are null */
max_offset = 0;
}
break;

View File

@ -200,9 +200,19 @@ static int get_loops_gameexe_ini(STREAMFILE* sf, int* p_loop_flag, int32_t* p_lo
length = ext-1-namebase;
file_size = get_streamfile_size(sf_loop);
/* format of line is:
* #DSTRACK = 00000000 - eeeeeeee - ssssssss = "name" = "name2?"
* ^22 ^33 ^45 ^57
/* According to the official documentation of RealLiveMax (the public version of RealLive), format of line is:
* #DSTRACK = 00000000 - eeeeeeee - ssssssss = "filename" = "alias for game script"
* ^22 ^33 ^45 ^57?
*
* Original text from the documentation (written in Japanese) is:
* ;
* ;
* ; 99999999
* ;
* ;=========================================================================================================
* ; - - = =
* #DSTRACK = 00000000 - 01896330 - 00088270 = "b_manuke" = "b_manuke"
* #DSTRACK = 00000000 - 01918487 - 00132385 = "c_happy" = "c_happy"
*/
for (found = 0, offset = 0; !found && offset<file_size; offset++) {

View File

@ -51,6 +51,7 @@ typedef struct {
int loop_flag;
int32_t loop_start;
int32_t loop_end;
int duration_test;
} psb_header_t;
@ -153,6 +154,9 @@ VGMSTREAM* init_vgmstream_psb(STREAMFILE* sf) {
case 24: vgmstream->coding_type = coding_PCM24LE; break; /* Legend of Mana (PC) */
default: goto fail;
}
if (psb.duration_test && psb.loop_start + psb.loop_end < vgmstream->num_samples)
vgmstream->loop_end_sample += psb.loop_start;
break;
case MSADPCM: /* [Senxin Aleste (AC)] */
@ -218,6 +222,8 @@ VGMSTREAM* init_vgmstream_psb(STREAMFILE* sf) {
}
vgmstream->num_samples = read_u32le(psb.stream_offset[0] + 0x00, sf);
if (psb.duration_test && psb.loop_start + psb.loop_end < vgmstream->num_samples)
vgmstream->loop_end_sample += psb.loop_start;
break;
default:
@ -592,7 +598,9 @@ static int parse_psb_channels(psb_header_t* psb, psb_node_t* nchans) {
psb->loop_start = psb_node_get_result(&nsub).num;
psb_node_by_index(&node, 1, &nsub);
psb->loop_end = psb_node_get_result(&nsub).num + psb->loop_start; /* duration */
psb->loop_end = psb_node_get_result(&nsub).num + 1; /* assumed, matches num_samples */
/* duration [LoM (PC), Namco Museum V1 (PC)] or standard [G-Darius (Sw)] (no apparent flags) */
psb->duration_test = 1;
}
}

View File

@ -527,6 +527,8 @@ VGMSTREAM* (*init_vgmstream_functions[])(STREAMFILE* sf) = {
init_vgmstream_bnk_relic,
init_vgmstream_xsh_xsd_xss,
init_vgmstream_psb,
init_vgmstream_lopu_fb,
init_vgmstream_lpcm_fb,
/* 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 */

View File

@ -756,6 +756,8 @@ typedef enum {
meta_BNK_RELIC,
meta_XSH_XSD_XSS,
meta_PSB,
meta_LOPU_FB,
meta_LPCM_FB,
} meta_t;