From 0bac16c976f2c122ffce6bb0805b1d3d2779f4f2 Mon Sep 17 00:00:00 2001 From: Christopher Snowhill Date: Wed, 7 Jul 2021 14:44:53 -0700 Subject: [PATCH] Updated VGMStream to r1050-3791-g4bd91df3 --- .../vgmstream/vgmstream/src/meta/str_wav.c | 663 ++++++++++-------- .../vgmstream/vgmstream/src/meta/txth.c | 149 +++- .../vgmstream/vgmstream/src/streamfile.h | 20 +- 3 files changed, 522 insertions(+), 310 deletions(-) diff --git a/Frameworks/vgmstream/vgmstream/src/meta/str_wav.c b/Frameworks/vgmstream/vgmstream/src/meta/str_wav.c index 9f9ee767a..ea2f026a2 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/str_wav.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/str_wav.c @@ -4,12 +4,13 @@ typedef enum { PSX, DSP, XBOX, WMA, IMA, XMA2 } strwav_codec; typedef struct { - int32_t channels; - int32_t sample_rate; + int tracks; + int channels; + int sample_rate; int32_t num_samples; int32_t loop_start; int32_t loop_end; - int32_t loop_flag; + int loop_flag; size_t interleave; off_t coefs_offset; @@ -20,7 +21,7 @@ typedef struct { strwav_codec codec; } strwav_header; -static int parse_header(STREAMFILE* sf_h, strwav_header* strwav); +static int parse_header(STREAMFILE* sf_h, STREAMFILE* sf_b, strwav_header* strwav); /* STR+WAV - Blitz Games streams+header */ @@ -61,20 +62,23 @@ VGMSTREAM* init_vgmstream_str_wav(STREAMFILE* sf) { } /* detect version */ - if (!parse_header(sf_h, &strwav)) + if (!parse_header(sf_h, sf, &strwav)) goto fail; if (strwav.flags == 0) goto fail; - if (strwav.channels > 8) - goto fail; - - /* &0x01: loop?, &0x02: non-mono?, &0x04: stream???, &0x08: unused? */ - if (strwav.flags > 0x07) { - VGM_LOG("STR+WAV: unknown flags\n"); + /* &0x01: loop, &0x02: stereo tracks, &0x04: stream?, &0x200: has named cues? */ + if (strwav.flags & 0xFFFFFDF8) { + VGM_LOG("STR+WAV: unknown flags %x\n", strwav.flags); goto fail; } + strwav.loop_flag = strwav.flags & 0x01; + + if (!strwav.channels) + strwav.channels = strwav.tracks * (strwav.flags & 0x02 ? 2 : 1); + if (strwav.channels > 8) + goto fail; start_offset = 0x00; @@ -195,170 +199,318 @@ fail: /* Parse header versions. Almost every game uses its own variation (struct serialization?), * so detection could be improved once enough versions are known. */ -static int parse_header(STREAMFILE* sf_h, strwav_header* strwav) { +static int parse_header(STREAMFILE* sf_h, STREAMFILE* sf_b, strwav_header* strwav) { size_t header_size; - if (read_32bitBE(0x00,sf_h) != 0x00000000) + if (read_u32be(0x00,sf_h) != 0x00000000) goto fail; header_size = get_streamfile_size(sf_h); + + /* most variations have extra tables (at least 1 entry): + * - table1: samples + * - table2: f32 ms + cue hash (ex. 2D7FC4C5 = __EndMarker0, not unique) + optional 0x38 cue name + * table entries don't need to match (table2 may be slightly bigger) + */ + +//breaking ta rules full test again, fuse with Pac-Man World 3 +//same on xbox and pc +//same with zapper + pw3 gc + //todo loop start/end values may be off for some headers /* Fuzion Frenzy (Xbox)[2001] wma */ - if ( read_32bitBE(0x04,sf_h) == 0x00000900 && - read_32bitLE(0x20,sf_h) == 0 && /* no num_samples */ - read_32bitLE(0x24,sf_h) == read_32bitLE(0x80,sf_h) && /* sample rate repeat */ - read_32bitLE(0x28,sf_h) == 0x10 && + if ( read_u32be(0x04,sf_h) == 0x00000900 && + read_u32le(0x0c,sf_h) != header_size && + read_u32le(0x24,sf_h) != 0 && + read_u32le(0x24,sf_h) == read_u32le(0x80,sf_h) && /* sample rate repeat */ header_size == 0x110 /* no value in header */ ) { - strwav->num_samples = read_32bitLE(0x20,sf_h); /* 0 but will be rectified later */ - strwav->sample_rate = read_32bitLE(0x24,sf_h); - strwav->flags = read_32bitLE(0x2c,sf_h); + /* 0x08: null */ + /* 0x0c: hashname */ + strwav->num_samples = read_s32le(0x20,sf_h); /* 0 but will be rectified later */ + strwav->sample_rate = read_s32le(0x24,sf_h); + /* 0x28: 16 bps */ + strwav->flags = read_u32le(0x2c,sf_h); + /* 0x38: related to samples? */ + /* 0x48: number of chunks */ + strwav->tracks = read_s32le(0x60,sf_h); + /* 0x80: sample rate 2 */ + strwav->loop_start = 0; strwav->loop_end = 0; - strwav->channels = read_32bitLE(0x60,sf_h) * (strwav->flags & 0x02 ? 2 : 1); /* tracks of 2/1ch */ - strwav->loop_flag = strwav->flags & 0x01; - strwav->interleave = 0; - strwav->codec = WMA; - //;VGM_LOG("STR+WAV: header Fuzion Frenzy (Xbox)\n"); + strwav->interleave = 0; + ;VGM_LOG("STR+WAV: header FF (Xbox)\n"); return 1; } /* Taz: Wanted (GC)[2002] */ /* Cubix Robots for Everyone: Showdown (GC)[2003] */ - if ( read_32bitBE(0x04,sf_h) == 0x00000900 && - read_32bitBE(0x24,sf_h) == read_32bitBE(0x90,sf_h) && /* sample rate repeat */ - read_32bitBE(0x28,sf_h) == 0x10 && - read_32bitBE(0xa0,sf_h) == header_size /* ~0x3C0 */ + if ( read_u32be(0x04,sf_h) == 0x00000900 && + read_u32be(0x0c,sf_h) != header_size && + read_u32le(0x24,sf_h) != 0 && + read_u32be(0x24,sf_h) == read_u32be(0x90,sf_h) && /* sample rate repeat */ + read_u32be(0xa0,sf_h) == header_size /* ~0x3C0 */ ) { - strwav->num_samples = read_32bitBE(0x20,sf_h); - strwav->sample_rate = read_32bitBE(0x24,sf_h); - strwav->flags = read_32bitBE(0x2c,sf_h); - strwav->loop_start = read_32bitBE(0xb8,sf_h); - strwav->loop_end = read_32bitBE(0xbc,sf_h); + /* 0x08: null */ + /* 0x0c: hashname */ + strwav->num_samples = read_s32be(0x20,sf_h); + strwav->sample_rate = read_s32be(0x24,sf_h); + /* 0x28: 16 bps */ + strwav->flags = read_u32be(0x2c,sf_h); + /* 0x38: related to samples? */ + strwav->tracks = read_s32be(0x50,sf_h); + /* 0x58: number of chunks */ + /* 0x90: sample rate 2 */ + /* 0xa0: header size */ + strwav->loop_start = read_s32be(0xb8,sf_h); + strwav->loop_end = read_s32be(0xbc,sf_h); + /* 0xc0: standard DSP header */ - strwav->channels = read_32bitBE(0x50,sf_h) * (strwav->flags & 0x02 ? 2 : 1); /* tracks of 2/1ch */ - strwav->loop_flag = strwav->flags & 0x01; - strwav->interleave = strwav->channels > 2 ? 0x8000 : 0x10000; - - strwav->coefs_offset = 0xdc; strwav->codec = DSP; - //;VGM_LOG("STR+WAV: header Taz: Wanted (GC)\n"); + strwav->interleave = strwav->tracks > 1 ? 0x8000 : 0x10000; + strwav->coefs_offset = 0xdc; + ;VGM_LOG("STR+WAV: header TZW/CBX (GC)\n"); return 1; } /* The Fairly OddParents - Breakin' da Rules (Xbox)[2003] */ - if ( read_32bitBE(0x04,sf_h) == 0x00000900 && - read_32bitLE(0x24,sf_h) == read_32bitLE(0xb0,sf_h) && /* sample rate repeat */ - read_32bitLE(0x28,sf_h) == 0x10 && - read_32bitLE(0xc0,sf_h)*0x04 + read_32bitLE(0xc4,sf_h) == header_size /* ~0xe0 + variable */ + if ( read_u32be(0x04,sf_h) == 0x00000900 && + read_u32le(0x24,sf_h) == read_u32le(0xb0,sf_h) && /* sample rate repeat */ + read_u32le(0xc0,sf_h)*0x04 + read_u32le(0xc4,sf_h) == header_size /* ~0xe0 + variable */ ) { - strwav->num_samples = read_32bitLE(0x20,sf_h); - strwav->sample_rate = read_32bitLE(0x24,sf_h); - strwav->flags = read_32bitLE(0x2c,sf_h); - strwav->loop_start = read_32bitLE(0x38,sf_h); + /* 0x08: null */ + /* 0x0c: hashname */ + strwav->num_samples = read_s32le(0x20,sf_h); + strwav->sample_rate = read_s32le(0x24,sf_h); + /* 0x28: 16 bps */ + strwav->flags = read_u32le(0x2c,sf_h); + strwav->loop_start = read_s32le(0x38,sf_h); + strwav->tracks = read_s32le(0x70,sf_h); + /* 0x78: number of chunks */ + /* 0xb0: sample rate 2 */ + /* 0xc0: table1 entries */ + /* 0xc4: table1 offset */ + /* 0xdc: total frames */ + strwav->loop_end = strwav->num_samples; - strwav->channels = read_32bitLE(0x70,sf_h) * (strwav->flags & 0x02 ? 2 : 1); /* tracks of 2/1ch */ - strwav->loop_flag = strwav->flags & 0x01; - strwav->interleave = 0xD800/2; - strwav->codec = XBOX; - //;VGM_LOG("STR+WAV: header The Fairly OddParents (Xbox)\n"); - return 1; - } - - /* Bad Boys II (GC)[2004] */ - if ( read_32bitBE(0x04,sf_h) == 0x00000800 && - read_32bitBE(0x24,sf_h) == read_32bitBE(0xb0,sf_h) && /* sample rate repeat */ - read_32bitBE(0x24,sf_h) == read_32bitBE(read_32bitBE(0xe0,sf_h)+0x08,sf_h) && /* sample rate vs 1st DSP header */ - read_32bitBE(0x28,sf_h) == 0x10 && - read_32bitBE(0xc0,sf_h)*0x04 + read_32bitBE(0xc4,sf_h) == header_size /* variable + variable */ - ) { - strwav->num_samples = read_32bitBE(0x20,sf_h); - strwav->sample_rate = read_32bitBE(0x24,sf_h); - strwav->flags = read_32bitBE(0x2c,sf_h); - strwav->loop_start = read_32bitBE(0xd8,sf_h); - strwav->loop_end = read_32bitBE(0xdc,sf_h); - - strwav->channels = read_32bitBE(0x70,sf_h) * read_32bitBE(0x88,sf_h); /* tracks of Nch */ - strwav->loop_flag = strwav->flags & 0x01; - strwav->interleave = strwav->channels > 2 ? 0x8000 : 0x10000; - - strwav->dsps_table = 0xe0; - strwav->codec = DSP; - //;VGM_LOG("STR+WAV: header Bad Boys II (GC)\n"); - return 1; - } - - /* Bad Boys II (PS2)[2004] */ - /* Pac-Man World 3 (PS2)[2005] */ - if ((read_32bitBE(0x04,sf_h) == 0x00000800 || - read_32bitBE(0x04,sf_h) == 0x01000800) && /* rare (PW3 mu_spectral1_explore_2) */ - read_32bitLE(0x24,sf_h) == read_32bitLE(0x70,sf_h) && /* sample rate repeat */ - read_32bitLE(0x28,sf_h) == 0x10 && - read_32bitLE(0x78,sf_h)*0x04 + read_32bitLE(0x7c,sf_h) == header_size /* ~0xe0 + variable */ - ) { - strwav->num_samples = read_32bitLE(0x20,sf_h); - strwav->sample_rate = read_32bitLE(0x24,sf_h); - strwav->flags = read_32bitLE(0x2c,sf_h); - strwav->loop_start = read_32bitLE(0x38,sf_h); - strwav->interleave = read_32bitLE(0x40,sf_h) == 1 ? 0x8000 : 0x4000; - strwav->loop_end = read_32bitLE(0x54,sf_h); - - strwav->channels = read_32bitLE(0x40,sf_h) * (strwav->flags & 0x02 ? 2 : 1); /* tracks of 2/1ch */ - strwav->loop_flag = strwav->flags & 0x01; - strwav->interleave = strwav->channels > 2 ? 0x4000 : 0x8000; - - strwav->codec = PSX; - //;VGM_LOG("STR+WAV: header Bad Boys II (PS2)\n"); + strwav->interleave = strwav->tracks > 1 ? 0xD800/2 : 0xD800; + ;VGM_LOG("STR+WAV: header FOP (Xbox)\n"); return 1; } /* Pac-Man World 3 (Xbox)[2005] */ if ((read_u32be(0x04,sf_h) == 0x00000800 || - read_u32be(0x04,sf_h) == 0x01000800) && /* rare (PW3 mu_spectral1_explore_2) */ + read_u32be(0x04,sf_h) == 0x01000800) && /* rare, mu_spectral1_explore_2 */ read_u32le(0x24,sf_h) == read_u32le(0xB0,sf_h) && /* sample rate repeat */ read_u32le(0x28,sf_h) == 0x10 && read_u32le(0xE0,sf_h) + read_u32le(0xE4,sf_h) * 0x40 == header_size /* ~0x100 + cues */ ) { - strwav->num_samples = read_u32le(0x20,sf_h); - strwav->sample_rate = read_u32le(0x24,sf_h); + /* 0x08: null */ + /* 0x0c: hashname */ + strwav->num_samples = read_s32le(0x20,sf_h); + strwav->sample_rate = read_s32le(0x24,sf_h); + /* 0x28: 16 bps */ strwav->flags = read_u32le(0x2c,sf_h); - strwav->loop_start = read_u32le(0x38,sf_h); + strwav->loop_start = read_s32le(0x38,sf_h); + strwav->tracks = read_s32le(0x70,sf_h); + /* 0x78: number of chunks */ + /* 0xb0: sample rate 2 */ + /* 0xdc: total frames */ + /* 0xe0: table2 offset */ + /* 0xe4: table2 entries */ + /* 0xf0: default hashname? */ + strwav->loop_end = strwav->num_samples; - strwav->channels = read_u32le(0x70,sf_h) * (strwav->flags & 0x02 ? 2 : 1); /* tracks of 2/1ch */ - strwav->loop_flag = strwav->flags & 0x01; - strwav->interleave = strwav->channels > 2 ? 0xD800/2 : 0xD800; - strwav->codec = XBOX; - //;VGM_LOG("STR+WAV: Pac-Man World 3 (Xbox)\n"); + strwav->interleave = strwav->tracks > 1 ? 0xD800/2 : 0xD800; + ;VGM_LOG("STR+WAV: PW3 (Xbox)\n"); + return 1; + } + + /* The Fairly OddParents! - Shadow Showdown (GC)[2004] */ + /* Bad Boys II (GC)[2004] */ + if ( read_u32be(0x04,sf_h) == 0x00000800 && + read_u32be(0x24,sf_h) == read_u32be(0xb0,sf_h) && /* sample rate repeat */ + read_u32be(0x24,sf_h) == read_u32be(read_u32be(0xe0,sf_h)+0x08,sf_h) && /* sample rate vs 1st DSP header */ + read_u32be(0xc0,sf_h)*0x04 + read_u32be(0xc4,sf_h) == header_size /* variable + variable */ + ) { + /* 0x08: null */ + /* 0x0c: hashname */ + strwav->num_samples = read_s32be(0x20,sf_h); + strwav->sample_rate = read_s32be(0x24,sf_h); + /* 0x28: 16 bps */ + strwav->flags = read_u32be(0x2c,sf_h); + /* 0x38: related to samples? */ + strwav->tracks = read_s32be(0x70,sf_h); + /* 0x78: number of chunks */ + /* 0x88: track channels */ + /* 0xb0: sample rate 2 */ + /* 0xc0: table1 entries */ + /* 0xc4: table1 offset */ + + strwav->loop_start = read_s32be(0xd8,sf_h); + strwav->loop_end = read_s32be(0xdc,sf_h); + /* 0xe0: DSP offset per channel */ + /* 0x100: standard DSP header */ + + strwav->codec = DSP; + strwav->dsps_table = 0xe0; + strwav->interleave = strwav->tracks > 1 ? 0x8000 : 0x10000; + ;VGM_LOG("STR+WAV: header FOP/BB2 (GC)\n"); + return 1; + } + + /* Zapper: One Wicked Cricket! (GC)[2005] */ + if ( read_u32be(0x04,sf_h) == 0x00000900 && + read_u32be(0x24,sf_h) == read_u32be(0xB0,sf_h) && /* sample rate repeat */ + read_u32le(0xc0,sf_h) == header_size /* LE! */ + ) { + /* 0x08: null */ + /* 0x0c: hashname */ + strwav->num_samples = read_s32be(0x20,sf_h); + strwav->sample_rate = read_s32be(0x24,sf_h); + /* 0x28: 16 bps */ + strwav->flags = read_u32be(0x2c,sf_h); + /* 0x38: related to samples? */ + strwav->tracks = read_s32be(0x70,sf_h); + /* 0x78: number of chunks */ + /* 0x88: track channels */ + /* 0xC0: size */ + /* 0xb0: sample rate 2 */ + /* 0xc0: table1 offset LE */ + + strwav->loop_start = read_s32be(0xd8,sf_h); + strwav->loop_end = read_s32be(0xdc,sf_h); + /* 0xe0: DSP offset per channel */ + /* 0x100: standard DSP header */ + + strwav->codec = DSP; + strwav->dsps_table = 0xe0; + strwav->interleave = strwav->tracks > 1 ? 0x8000 : 0x10000; + + ;VGM_LOG("STR+WAV: header ZP (GC)\n"); + return 1; + } + + /* The Fairly OddParents - Breakin' da Rules (PS2)[2003] */ + /* The Fairly OddParents! - Shadow Showdown (PS2)[2004] */ + /* Bad Boys II (PS2)[2004] */ + /* Zapper: One Wicked Cricket! (PS2)[2005] */ + if ((read_u32be(0x04,sf_h) == 0x00000800 || /* BB2 */ + read_u32be(0x04,sf_h) == 0x00000900) && /* FOP, ZP */ + read_u32le(0x24,sf_h) == read_u32le(0x70,sf_h) && /* sample rate repeat */ + read_u32le(0x78,sf_h)*0x04 + read_u32le(0x7c,sf_h) == header_size /* ~0xe0 + variable */ + ) { + /* 0x08: null */ + /* 0x0c: hashname */ + strwav->num_samples = read_s32le(0x20,sf_h); + strwav->sample_rate = read_s32le(0x24,sf_h); + /* 0x28: 16 bps */ + strwav->flags = read_u32le(0x2c,sf_h); + strwav->loop_start = read_s32le(0x38,sf_h); + strwav->tracks = read_s32le(0x40,sf_h); + strwav->loop_end = read_s32le(0x54,sf_h); + /* 0x70: sample rate 2 */ + /* 0x78: table1 entries */ + /* 0x7c: table1 offset */ + /* 0xb4: number of 0x800 sectors */ + + strwav->codec = PSX; + strwav->interleave = strwav->tracks > 1 ? 0x4000 : 0x8000; + ;VGM_LOG("STR+WAV: header FOP/BB2/ZP/PW3 (PS2)\n"); + return 1; + } + + /* Pac-Man World 3 (PS2)[2005] */ + if ((read_u32be(0x04,sf_h) == 0x00000800 || + read_u32be(0x04,sf_h) == 0x01000800) && /* rare, mu_spectral1_explore_2 */ + read_u32le(0x24,sf_h) == read_u32le(0x70,sf_h) && /* sample rate repeat */ + read_u32le(0x78,sf_h)*0x04 + read_u32le(0x7c,sf_h) == header_size /* ~0xe0 + variable */ + ) { + /* 0x08: null */ + /* 0x0c: hashname */ + strwav->num_samples = read_s32le(0x20,sf_h); + strwav->sample_rate = read_s32le(0x24,sf_h); + /* 0x28: 16 bps */ + strwav->flags = read_u32le(0x2c,sf_h); + strwav->loop_start = read_s32le(0x38,sf_h); + strwav->tracks = read_s32le(0x40,sf_h); + strwav->loop_end = read_s32le(0x54,sf_h); + /* 0x70: sample rate 2 */ + /* 0x78: table1 entries */ + /* 0x7c: table1 offset */ + /* 0xb4: number of 0x800 sectors */ + /* 0xe0: table2 offset */ + /* 0xe4: table2 entries */ + + strwav->codec = PSX; + strwav->interleave = strwav->tracks > 1 ? 0x4000 : 0x8000; + ;VGM_LOG("STR+WAV: header FOP/BB2/ZP/PW3 (PS2)\n"); + return 1; + } + + /* Zapper: One Wicked Cricket! (PC)[2005] */ + if ( read_u32be(0x04,sf_h) == 0x00000900 && + read_u32le(0x24,sf_h) == read_u32le(0x114,sf_h) && /* sample rate repeat */ + read_u32le(0x12c,sf_h) == header_size /* ~0x130 */ + ) { + /* 0x08: null */ + /* 0x0c: hashname */ + strwav->num_samples = read_s32le(0x20,sf_h); + strwav->sample_rate = read_s32le(0x24,sf_h); + /* 0x28: 16 bps */ + strwav->flags = read_u32le(0x2c,sf_h); + strwav->loop_end = read_s32le(0x30,sf_h); + /* 0x38: related to samples? */ + /* 0x54: number of chunks */ + strwav->loop_start = read_s32le(0x54,sf_h); + strwav->tracks = read_s32le(0xF8,sf_h); + /* 0x114: sample rate 2 */ + /* 0x120: number of blocks */ + /* 0x12c: table1 offset */ + + strwav->loop_start = 0; /* ??? */ + + strwav->codec = IMA; + strwav->interleave = strwav->tracks > 1 ? 0x8000 : 0x10000; + ;VGM_LOG("STR+WAV: header ZP (PC)\n"); return 1; } /* Pac-Man World 3 (PC)[2005] */ if ((read_u32be(0x04,sf_h) == 0x00000800 || - read_u32be(0x04,sf_h) == 0x01000800) && /* rare (PW3 mu_spectral1_explore_2) */ + read_u32be(0x04,sf_h) == 0x01000800) && /* rare, mu_spectral1_explore_2 */ read_u32le(0x24,sf_h) == read_u32le(0x114,sf_h) && /* sample rate repeat */ - read_u32le(0x28,sf_h) == 0x10 && read_u32le(0x130,sf_h) + read_u32le(0x134,sf_h) * 0x40 == header_size /* ~0x140 + cues */ ) { - strwav->num_samples = read_u32le(0x20,sf_h); - strwav->sample_rate = read_u32le(0x24,sf_h); + /* 0x08: null */ + /* 0x0c: hashname */ + strwav->num_samples = read_s32le(0x20,sf_h); + strwav->sample_rate = read_s32le(0x24,sf_h); + /* 0x28: 16 bps */ strwav->flags = read_u32le(0x2c,sf_h); - strwav->loop_start = read_u32le(0x38,sf_h); - strwav->loop_end = read_u32le(0x30,sf_h); - - strwav->channels = read_u32le(0xF8,sf_h) * (strwav->flags & 0x02 ? 2 : 1); /* tracks of 2/1ch */ - strwav->loop_flag = strwav->flags & 0x01; - strwav->interleave = strwav->channels > 2 ? 0x8000 : 0x10000; + strwav->loop_end = read_s32le(0x30,sf_h); + strwav->loop_start = read_s32le(0x38,sf_h); + /* 0x54: number of chunks */ + strwav->tracks = read_s32le(0xF8,sf_h); + /* 0x114: sample rate 2 */ + /* 0x120: default hashname? */ + /* 0x124: number of blocks? */ + /* 0x128: table1 entries */ + /* 0x12c: table1 offset */ + /* 0x130: table2 offset */ + /* 0x134: table2 entries */ strwav->codec = IMA; - //;VGM_LOG("STR+WAV: Pac-Man World 3 (PC)\n"); + strwav->interleave = strwav->tracks > 1 ? 0x8000 : 0x10000; + ;VGM_LOG("STR+WAV: PW3 (PC)\n"); return 1; } @@ -368,224 +520,179 @@ static int parse_header(STREAMFILE* sf_h, strwav_header* strwav) { read_u32le(0x70,sf_h) == 0 && /* sample rate repeat? */ header_size == 0x78 ) { - strwav->num_samples = read_u32le(0x5c,sf_h); - strwav->sample_rate = read_u32le(0x2c,sf_h); + /* 0x08: null */ + /* 0x0c: hashname */ + /* 0x28: loop start? */ + strwav->sample_rate = read_s32le(0x2c,sf_h); + /* 0x30: number of 0x800 sectors */ strwav->flags = read_u32le(0x34,sf_h); + strwav->num_samples = read_s32le(0x5c,sf_h); + strwav->tracks = read_s32le(0x60,sf_h); + strwav->loop_start = 0; strwav->loop_end = 0; - strwav->channels = read_u32le(0x60,sf_h) * (strwav->flags & 0x02 ? 2 : 1); /* tracks of 2/1ch */ - strwav->loop_flag = strwav->flags & 0x01; - strwav->interleave = strwav->channels > 2 ? 0x8000 : 0x8000; + strwav->codec = PSX; + strwav->interleave = strwav->tracks > 1 ? 0x8000 : 0x8000; //todo: tracks are stereo blocks of size 0x20000*tracks, containing 4 interleaves of 0x8000: // | 1 2 1 2 | 3 4 3 4 | 5 6 5 6 | 1 2 1 2 | 3 4 3 4 | 5 6 5 6 | ... - - strwav->codec = PSX; - ;VGM_LOG("STR+WAV: header Zapper Beta (PS2)\n"); - return 1; - } - - /* Zapper: One Wicked Cricket! (PS2)[2005] */ - if ( read_32bitBE(0x04,sf_h) == 0x00000900 && - read_32bitLE(0x24,sf_h) == read_32bitLE(0x70,sf_h) && /* sample rate repeat */ - read_32bitLE(0x28,sf_h) == 0x10 && - read_32bitLE(0x7c,sf_h) == header_size /* ~0xD0 */ - ) { - strwav->num_samples = read_32bitLE(0x20,sf_h); - strwav->sample_rate = read_32bitLE(0x24,sf_h); - strwav->flags = read_32bitLE(0x2c,sf_h); - strwav->loop_start = read_32bitLE(0x38,sf_h); - strwav->loop_end = read_32bitLE(0x54,sf_h); - - strwav->channels = read_32bitLE(0x40,sf_h) * (strwav->flags & 0x02 ? 2 : 1); /* tracks of 2/1ch */ - strwav->loop_flag = strwav->flags & 0x01; - strwav->interleave = strwav->channels > 2 ? 0x4000 : 0x8000; - - strwav->codec = PSX; - //;VGM_LOG("STR+WAV: header Zapper (PS2)\n"); - return 1; - } - - /* Zapper: One Wicked Cricket! (GC)[2005] */ - if ( read_32bitBE(0x04,sf_h) == 0x00000900 && - read_32bitBE(0x24,sf_h) == read_32bitBE(0xB0,sf_h) && /* sample rate repeat */ - read_32bitBE(0x28,sf_h) == 0x10 && - read_32bitLE(0xc0,sf_h) == header_size /* variable LE size */ - ) { - strwav->num_samples = read_32bitBE(0x20,sf_h); - strwav->sample_rate = read_32bitBE(0x24,sf_h); - strwav->flags = read_32bitBE(0x2c,sf_h); - strwav->loop_start = read_32bitBE(0xd8,sf_h); - strwav->loop_end = read_32bitBE(0xdc,sf_h); - - strwav->channels = read_32bitBE(0x70,sf_h) * read_32bitBE(0x88,sf_h); /* tracks of Nch */ - strwav->loop_flag = strwav->flags & 0x01; - strwav->interleave = strwav->channels > 2 ? 0x8000 : 0x10000; - - strwav->dsps_table = 0xe0; - strwav->codec = DSP; - //;VGM_LOG("STR+WAV: header Zapper (GC)\n"); - return 1; - } - - /* Zapper: One Wicked Cricket! (PC)[2005] */ - if ( read_32bitBE(0x04,sf_h) == 0x00000900 && - read_32bitLE(0x24,sf_h) == read_32bitLE(0x114,sf_h) && /* sample rate repeat */ - read_32bitLE(0x28,sf_h) == 0x10 && - read_32bitLE(0x12c,sf_h) == header_size /* ~0x130 */ - ) { - strwav->num_samples = read_32bitLE(0x20,sf_h); - strwav->sample_rate = read_32bitLE(0x24,sf_h); - strwav->flags = read_32bitLE(0x2c,sf_h); - strwav->loop_start = read_32bitLE(0x54,sf_h); - strwav->loop_end = read_32bitLE(0x30,sf_h); - - strwav->channels = read_32bitLE(0xF8,sf_h) * (strwav->flags & 0x02 ? 2 : 1); /* tracks of 2/1ch */ - strwav->loop_flag = strwav->flags & 0x01; - strwav->interleave = strwav->channels > 2 ? 0x8000 : 0x10000; - - strwav->codec = IMA; - //;VGM_LOG("STR+WAV: header Zapper (PC)\n"); + ;VGM_LOG("STR+WAV: header ZPb (PS2)\n"); return 1; } /* Pac-Man World 3 (GC)[2005] */ /* SpongeBob SquarePants: Creature from the Krusty Krab (GC)[2006] */ /* SpongeBob SquarePants: Creature from the Krusty Krab (Wii)[2006] */ - if ( read_32bitBE(0x04,sf_h) == 0x00000800 && - read_32bitBE(0x24,sf_h) == read_32bitBE(0xb0,sf_h) && /* sample rate repeat */ - read_32bitBE(0x24,sf_h) == read_32bitBE(read_32bitBE(0xf0,sf_h)+0x08,sf_h) && /* sample rate vs 1st DSP header */ - read_32bitBE(0x28,sf_h) == 0x10 && - read_32bitBE(0xc0,sf_h)*0x04 + read_32bitBE(0xc4,sf_h) == read_32bitBE(0xe0,sf_h) && /* main size */ - (read_32bitBE(0xe0,sf_h) + read_32bitBE(0xe4,sf_h)*0x40 == header_size || /* main size + extradata 1 (config? PMW3 cs2.wav) */ - read_32bitBE(0xe0,sf_h) + read_32bitBE(0xe4,sf_h)*0x08 == header_size) /* main size + extradata 2 (ids? SBSP 0_0_mu_hr.wav) */ + if ( read_u32be(0x04,sf_h) == 0x00000800 && + read_u32be(0x24,sf_h) == read_u32be(0xb0,sf_h) && /* sample rate repeat */ + read_u32be(0x24,sf_h) == read_u32be(read_u32be(0xf0,sf_h)+0x08,sf_h) && /* sample rate vs 1st DSP header */ + read_u32be(0xc0,sf_h)*0x04 + read_u32be(0xc4,sf_h) == read_u32be(0xe0,sf_h) && /* main size */ + (read_u32be(0xe0,sf_h) + read_u32be(0xe4,sf_h)*0x40 == header_size || /* main size + extradata 1 (config? PMW3 cs2.wav) */ + read_u32be(0xe0,sf_h) + read_u32be(0xe4,sf_h)*0x08 == header_size) /* main size + extradata 2 (ids? SBSP 0_0_mu_hr.wav) */ ) { - strwav->num_samples = read_32bitBE(0x20,sf_h); - strwav->sample_rate = read_32bitBE(0x24,sf_h); - strwav->flags = read_32bitBE(0x2c,sf_h); - strwav->loop_start = read_32bitBE(0xd8,sf_h); - strwav->loop_end = read_32bitBE(0xdc,sf_h); + strwav->num_samples = read_s32be(0x20,sf_h); + strwav->sample_rate = read_s32be(0x24,sf_h); + strwav->flags = read_u32be(0x2c,sf_h); + strwav->loop_start = read_s32be(0xd8,sf_h); + strwav->loop_end = read_s32be(0xdc,sf_h); - strwav->channels = read_32bitBE(0x70,sf_h) * read_32bitBE(0x88,sf_h); /* tracks of Nch */ - strwav->loop_flag = strwav->flags & 0x01; - strwav->interleave = strwav->channels > 2 ? 0x8000 : 0x10000; + strwav->tracks = read_s32be(0x70,sf_h); - strwav->dsps_table = 0xf0; strwav->codec = DSP; - //;VGM_LOG("STR+WAV: header SpongeBob SquarePants (GC)\n"); + strwav->dsps_table = 0xf0; + strwav->interleave = strwav->tracks > 2 ? 0x8000 : 0x10000; + ;VGM_LOG("STR+WAV: header SBCKK (GC)\n"); return 1; } /* SpongeBob SquarePants: Creature from the Krusty Krab (PS2)[2006] */ - if ( read_32bitBE(0x04,sf_h) == 0x00000800 && - read_32bitLE(0x08,sf_h) == 0x00000000 && - read_32bitLE(0x0c,sf_h) != header_size && /* some ID */ - (header_size == 0x74 + read_32bitLE(0x64,sf_h)*0x04 || - header_size == 0x78 + read_32bitLE(0x64,sf_h)*0x04) + /* Sneak King (Xbox)[2006] */ + if ( read_u32be(0x04,sf_h) == 0x00000800 && + read_u32le(0x08,sf_h) == 0x00000000 && + read_u32le(0x0c,sf_h) != header_size && + header_size == + read_u32le(0x40,sf_h) + read_u16le(0x48,sf_h) * 0x04 + + read_u16le(0x4a,sf_h) * ((read_u32le(0x3c,sf_h) & 0x200) ? 0x08+0x38 : 0x08) ) { - strwav->loop_start = read_32bitLE(0x24,sf_h); //not ok? - strwav->num_samples = read_32bitLE(0x30,sf_h); - strwav->loop_end = read_32bitLE(0x34,sf_h); - strwav->sample_rate = read_32bitLE(0x38,sf_h); - strwav->flags = read_32bitLE(0x3c,sf_h); + /* 0x08: null */ + /* 0x0c: hashname */ + strwav->loop_start = read_s32le(0x24,sf_h); //ok? + /* 0x28: f32 time in ms */ + strwav->num_samples = read_s32le(0x30,sf_h); + strwav->loop_end = read_s32le(0x34,sf_h); + strwav->sample_rate = read_s32le(0x38,sf_h); + strwav->flags = read_u32le(0x3c,sf_h); + /* 0x40: table1 offset */ + /* 0x44: table2 offset */ + /* 0x48: table1 entries */ + /* 0x4a: table2 entries */ + /* 0x4c: ? (some low number) */ + strwav->tracks = read_u8 (0x4e,sf_h); + /* 0x4f: 16 bps */ + /* 0x54: channels per each track? (ex. 2 stereo track: 0x02,0x02) */ + /* 0x64: channels */ + /* 0x70+: tables */ - strwav->channels = read_32bitLE(0x64,sf_h); /* tracks of 1ch */ - strwav->loop_flag = strwav->flags & 0x01; - strwav->interleave = strwav->channels > 4 ? 0x4000 : 0x8000; - - strwav->codec = PSX; - //;VGM_LOG("STR+WAV: header SpongeBob SquarePants (PS2)\n"); + /* no codec flags */ + if (ps_check_format(sf_b, 0x00, 0x100)) { + strwav->codec = PSX; + strwav->interleave = strwav->tracks > 2 ? 0x4000 : 0x8000; + } + else { + strwav->codec = XBOX; + strwav->interleave = strwav->tracks > 1 ? 0xD800/2 : 0xD800; /* assumed for multitrack */ + } + ;VGM_LOG("STR+WAV: header SBCKK/SK (PS2)\n"); return 1; } /* Tak and the Guardians of Gross (PS2)[2008] */ - if ( read_32bitBE(0x04,sf_h) == 0x00000800 && - read_32bitLE(0x08,sf_h) != 0x00000000 && - read_32bitLE(0x0c,sf_h) == header_size && /* ~0x80+0x04*ch */ - read_32bitLE(0x0c,sf_h) == 0x80 + read_32bitLE(0x70,sf_h)*0x04 + /* SpongeBob's Atlantis SquarePantis (PS2)[2007] */ + if ( read_u32be(0x04,sf_h) == 0x00000800 && + read_u32le(0x08,sf_h) != 0x00000000 && /* some ID */ + read_u32le(0x0c,sf_h) == header_size && /* ~0x7c+variable */ + header_size == + read_u32le(0x40,sf_h) + read_u16le(0x48,sf_h) * 0x04 + + read_u16le(0x4a,sf_h) * ((read_u32le(0x3c,sf_h) & 0x200) ? 0x08+0x38 : 0x08) ) { - strwav->loop_start = read_32bitLE(0x24,sf_h); //not ok? - strwav->num_samples = read_32bitLE(0x30,sf_h); - strwav->loop_end = read_32bitLE(0x34,sf_h); - strwav->sample_rate = read_32bitLE(0x38,sf_h); - strwav->flags = read_32bitLE(0x3c,sf_h); + strwav->loop_start = read_s32le(0x24,sf_h); //not ok? + strwav->num_samples = read_s32le(0x30,sf_h); + strwav->loop_end = read_s32le(0x34,sf_h); + strwav->sample_rate = read_s32le(0x38,sf_h); + strwav->flags = read_u32le(0x3c,sf_h); - strwav->channels = read_32bitLE(0x70,sf_h); /* tracks of 1ch */ - strwav->loop_flag = strwav->flags & 0x01; - strwav->interleave = strwav->channels > 4 ? 0x4000 : 0x8000; + strwav->channels = read_s32le(0x70,sf_h); /* tracks of 1ch */ strwav->codec = PSX; - //;VGM_LOG("STR+WAV: header Tak (PS2)\n"); + strwav->interleave = strwav->channels > 4 ? 0x4000 : 0x8000; + ;VGM_LOG("STR+WAV: header TKGG/SBASP (PS2)\n"); return 1; } /* Tak and the Guardians of Gross (Wii)[2008] */ /* The House of the Dead: Overkill (Wii)[2009] (not Blitz but still the same format) */ /* All Star Karate (Wii)[2010] */ - if ((read_32bitBE(0x04,sf_h) == 0x00000800 || - read_32bitBE(0x04,sf_h) == 0x00000700) && /* rare? */ - read_32bitLE(0x08,sf_h) != 0x00000000 && - read_32bitBE(0x0c,sf_h) == header_size && /* variable per header */ - read_32bitBE(0x7c,sf_h) != 0 && /* has DSP header */ - read_32bitBE(0x38,sf_h) == read_32bitBE(read_32bitBE(0x7c,sf_h)+0x38,sf_h) /* sample rate vs 1st DSP header */ + if ((read_u32be(0x04,sf_h) == 0x00000800 || + read_u32be(0x04,sf_h) == 0x00000700) && /* rare? */ + read_u32be(0x08,sf_h) != 0x00000000 && + read_u32be(0x0c,sf_h) == header_size && /* variable per header */ + read_u32be(0x7c,sf_h) != 0 && /* has DSP header */ + read_u32be(0x38,sf_h) == read_u32be(read_u32be(0x7c,sf_h)+0x38,sf_h) /* sample rate vs 1st DSP header */ ) { - strwav->loop_start = 0; //read_32bitLE(0x24,sf_h); //not ok? - strwav->num_samples = read_32bitBE(0x30,sf_h); - strwav->loop_end = read_32bitBE(0x34,sf_h); - strwav->sample_rate = read_32bitBE(0x38,sf_h); - strwav->flags = read_32bitBE(0x3c,sf_h); + strwav->loop_start = 0; //read_s32be(0x24,sf_h); //not ok? + strwav->num_samples = read_s32be(0x30,sf_h); + strwav->loop_end = read_s32be(0x34,sf_h); + strwav->sample_rate = read_s32be(0x38,sf_h); + strwav->flags = read_u32be(0x3c,sf_h); - strwav->channels = read_32bitBE(0x70,sf_h); /* tracks of 1ch */ - strwav->loop_flag = strwav->flags & 0x01; - strwav->interleave = strwav->channels > 4 ? 0x4000 : 0x8000; + strwav->channels = read_s32be(0x70,sf_h); /* tracks of 1ch */ - strwav->coefs_table = 0x7c; strwav->codec = DSP; - //;VGM_LOG("STR+WAV: header Tak/HOTD:O (Wii)\n"); + strwav->coefs_table = 0x7c; + strwav->interleave = strwav->channels > 4 ? 0x4000 : 0x8000; + ;VGM_LOG("STR+WAV: header TKGG/HOTDO/ASK (Wii)\n"); return 1; } /* The House of the Dead: Overkill (PS3)[2009] (not Blitz but still the same format) */ - if ((read_32bitBE(0x04,sf_h) == 0x00000800 || - read_32bitBE(0x04,sf_h) == 0x00000700) && /* rare? */ - read_32bitLE(0x08,sf_h) != 0x00000000 && - read_32bitBE(0x0c,sf_h) == header_size && /* variable per header */ - read_32bitBE(0x7c,sf_h) == 0 /* not DSP header */ + if ((read_u32be(0x04,sf_h) == 0x00000800 || + read_u32be(0x04,sf_h) == 0x00000700) && /* rare? */ + read_u32be(0x08,sf_h) != 0x00000000 && + read_u32be(0x0c,sf_h) == header_size && /* variable per header */ + read_u32be(0x7c,sf_h) == 0 /* not DSP header */ ) { strwav->loop_start = 0; //read_32bitLE(0x24,sf_h); //not ok? - strwav->num_samples = read_32bitBE(0x30,sf_h); - strwav->loop_end = read_32bitBE(0x34,sf_h); - strwav->sample_rate = read_32bitBE(0x38,sf_h); - strwav->flags = read_32bitBE(0x3c,sf_h); + strwav->num_samples = read_s32be(0x30,sf_h); + strwav->loop_end = read_s32be(0x34,sf_h); + strwav->sample_rate = read_s32be(0x38,sf_h); + strwav->flags = read_u32be(0x3c,sf_h); - strwav->channels = read_32bitBE(0x70,sf_h); /* tracks of 1ch */ - strwav->loop_flag = strwav->flags & 0x01; + strwav->channels = read_s32be(0x70,sf_h); /* tracks of 1ch */ strwav->interleave = strwav->channels > 4 ? 0x4000 : 0x8000; strwav->codec = PSX; - //;VGM_LOG("STR+WAV: header HOTD:O (PS3)\n"); + ;VGM_LOG("STR+WAV: header HOTDO (PS3)\n"); return 1; } /* SpongeBob's Surf & Skate Roadtrip (X360)[2011] */ - if ((read_32bitBE(0x04,sf_h) == 0x00000800 || /* used? */ - read_32bitBE(0x04,sf_h) == 0x00000700) && - read_32bitLE(0x08,sf_h) != 0x00000000 && - read_32bitBE(0x0c,sf_h) == 0x124 && /* variable, not sure about final calc */ - read_32bitBE(0x8c,sf_h) == 0x180 /* encoder delay actually */ + if ((read_u32be(0x04,sf_h) == 0x00000800 || /* used? */ + read_u32be(0x04,sf_h) == 0x00000700) && + read_u32be(0x08,sf_h) != 0x00000000 && + read_u32be(0x0c,sf_h) == 0x124 && /* variable, not sure about final calc */ + read_u32be(0x8c,sf_h) == 0x180 /* encoder delay actually */ //0x4c is data_size + 0x210 ) { strwav->loop_start = 0; //read_32bitLE(0x24,sf_h); //not ok? - strwav->num_samples = read_32bitBE(0x30,sf_h);//todo sometimes wrong? - strwav->loop_end = read_32bitBE(0x34,sf_h); - strwav->sample_rate = read_32bitBE(0x38,sf_h); - strwav->flags = read_32bitBE(0x3c,sf_h); + strwav->num_samples = read_s32be(0x30,sf_h);//todo sometimes wrong? + strwav->loop_end = read_s32be(0x34,sf_h); + strwav->sample_rate = read_s32be(0x38,sf_h); + strwav->flags = read_u32be(0x3c,sf_h); - strwav->channels = read_32bitBE(0x70,sf_h); /* multichannel XMA */ - strwav->loop_flag = strwav->flags & 0x01; + strwav->channels = read_s32be(0x70,sf_h); /* multichannel XMA */ strwav->codec = XMA2; - //;VGM_LOG("STR+WAV: header SBSSR (X360)\n"); + ;VGM_LOG("STR+WAV: header SBSSR (X360)\n"); return 1; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/txth.c b/Frameworks/vgmstream/vgmstream/src/meta/txth.c index 923b4d43d..566c1c4fd 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/txth.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/txth.c @@ -54,7 +54,7 @@ typedef struct { uint32_t value_sub; uint32_t id_value; - uint32_t id_offset; + uint32_t id_check; uint32_t interleave; uint32_t interleave_last; @@ -122,6 +122,8 @@ typedef struct { uint32_t name_values[16]; int name_values_count; + int is_multi_txth; + /* original STREAMFILE and its type (may be an unsupported "base" file or a .txth) */ STREAMFILE* sf; int streamfile_is_txth; @@ -571,7 +573,7 @@ VGMSTREAM* init_vgmstream_txth(STREAMFILE* sf) { vgmstream->allow_dual_stereo = 1; - if ( !vgmstream_open_stream(vgmstream,txth.sf_body,txth.start_offset) ) + if (!vgmstream_open_stream(vgmstream, txth.sf_body, txth.start_offset)) goto fail; clean_txth(&txth); @@ -750,7 +752,7 @@ static void set_body_chunk(txth_header* txth) { return; /* treat chunks as subsongs */ - if (txth->subsong_count > 1) + if (txth->subsong_count > 1 && txth->subsong_count == txth->chunk_count) txth->chunk_number = txth->target_subsong; if (txth->chunk_number == 0) txth->chunk_number = 1; @@ -798,6 +800,7 @@ static int parse_num(STREAMFILE* sf, txth_header* txth, const char* val, uint32_ static int parse_string(STREAMFILE* sf, txth_header* txth, const char* val, char* str); static int parse_coef_table(STREAMFILE* sf, txth_header* txth, const char* val, uint8_t* out_value, size_t out_size); static int parse_name_table(txth_header* txth, char* val); +static int parse_multi_txth(txth_header* txth, char* val); static int is_string(const char* val, const char* cmp); static int get_bytes_to_samples(txth_header* txth, uint32_t bytes); static int get_padding_size(txth_header* txth, int discard_empty); @@ -944,9 +947,9 @@ static int parse_keyval(STREAMFILE* sf_, txth_header* txth, const char* key, cha else if (is_string(key,"id_value")) { if (!parse_num(txth->sf_head,txth,val, &txth->id_value)) goto fail; } - else if (is_string(key,"id_offset")) { - if (!parse_num(txth->sf_head,txth,val, &txth->id_offset)) goto fail; - if (txth->id_value != txth->id_offset) /* evaluate current ID */ + else if (is_string(key,"id_check") || is_string(key,"id_offset")) { + if (!parse_num(txth->sf_head,txth,val, &txth->id_check)) goto fail; + if (txth->id_value != txth->id_check) /* evaluate current ID */ goto fail; } @@ -1327,6 +1330,11 @@ static int parse_keyval(STREAMFILE* sf_, txth_header* txth, const char* key, cha if (!parse_name_table(txth,val)) goto fail; } + /* MULTI TXTH */ + else if (is_string(key,"multi_txth")) { + if (!parse_multi_txth(txth,val)) goto fail; + } + /* DEFAULT */ else { @@ -1511,8 +1519,52 @@ fail: return 0; } +static int read_name_table_keyval(txth_header* txth, const char* line, char* key, char* val) { + int ok; + int subsong; + + /* get key/val (ignores lead spaces, stops at space/comment/separator) */ + //todo names with # and subsongs don't work + + /* ignore comments (that aren't subsongs) */ + if (line[0] == '#' && strchr(line,':') < 0) + return 0; + + /* try "(name): (val))" */ + ok = sscanf(line, " %[^\t#:] : %[^\t#\r\n] ", key, val); + if (ok == 2) { + ;VGM_LOG("TXTH: name %s get\n", key); + return 1; + } + + /* try "(empty): (val))" */ + key[0] = '\0'; + ok = sscanf(line, " : %[^\t#\r\n] ", val); + if (ok == 1) { + ;VGM_LOG("TXTH: default get\n"); + return 1; + } + + /* try "(name)#subsong: (val))" */ + ok = sscanf(line, " %[^\t#:]#%i : %[^\t#\r\n] ", key, &subsong, val); + if (ok == 3 && subsong == txth->target_subsong) { + VGM_LOG("TXTH: name %s + subsong %i get\n", key, subsong); + return 1; + } + + /* try "(empty)#subsong: (val))" */ + key[0] = '\0'; + ok = sscanf(line, " #%i: %[^\t#\r\n] ", &subsong, val); + if (ok == 2 && subsong == txth->target_subsong) { + VGM_LOG("TXTH: default + subsong %i get\n", subsong); + return 1; + } + + return 0; +} + static int parse_name_table(txth_header* txth, char* name_list) { - STREAMFILE* nameFile = NULL; + STREAMFILE* sf_names = NULL; off_t txt_offset, file_size; char fullname[PATH_LIMIT]; char filename[PATH_LIMIT]; @@ -1536,8 +1588,8 @@ static int parse_name_table(txth_header* txth, char* name_list) { //;VGM_LOG("TXTH: name_list='%s'\n", name_list); /* open companion file near .txth */ - nameFile = open_streamfile_by_filename(txth->sf_text, name_list); - if (!nameFile) goto fail; + sf_names = open_streamfile_by_filename(txth->sf_text, name_list); + if (!sf_names) goto fail; get_streamfile_name(txth->sf_body, fullname, sizeof(filename)); get_streamfile_filename(txth->sf_body, filename, sizeof(filename)); @@ -1545,14 +1597,14 @@ static int parse_name_table(txth_header* txth, char* name_list) { //;VGM_LOG("TXTH: names full=%s, file=%s, base=%s\n", fullname, filename, basename); txt_offset = 0x00; - file_size = get_streamfile_size(nameFile); + file_size = get_streamfile_size(sf_names); /* skip BOM if needed */ - if ((uint16_t)read_16bitLE(0x00, nameFile) == 0xFFFE || - (uint16_t)read_16bitLE(0x00, nameFile) == 0xFEFF) { + if ((uint16_t)read_16bitLE(0x00, sf_names) == 0xFFFE || + (uint16_t)read_16bitLE(0x00, sf_names) == 0xFEFF) { txt_offset = 0x02; } - else if (((uint32_t)read_32bitBE(0x00, nameFile) & 0xFFFFFF00) == 0xEFBBBF00) { + else if (((uint32_t)read_32bitBE(0x00, sf_names) & 0xFFFFFF00) == 0xEFBBBF00) { txt_offset = 0x03; } @@ -1569,22 +1621,14 @@ static int parse_name_table(txth_header* txth, char* name_list) { while (txt_offset < file_size) { int ok, bytes_read, line_ok; - bytes_read = read_line(line, sizeof(line), txt_offset, nameFile, &line_ok); + bytes_read = read_line(line, sizeof(line), txt_offset, sf_names, &line_ok); if (!line_ok) goto fail; //;VGM_LOG("TXTH: line=%s\n",line); txt_offset += bytes_read; - /* get key/val (ignores lead spaces, stops at space/comment/separator) */ - ok = sscanf(line, " %[^\t#:] : %[^\t#\r\n] ", key,val); - if (ok != 2) { /* ignore line if no key=val (comment or garbage) */ - /* try again with " (empty): (val)) */ - key[0] = '\0'; - ok = sscanf(line, " : %[^\t#\r\n] ", val); - if (ok != 1) - continue; - } - + if (!read_name_table_keyval(txth, line, key, val)) + continue; //;VGM_LOG("TXTH: compare name '%s'\n", key); /* parse values if key (name) matches default ("") or filename with/without extension */ @@ -1619,14 +1663,66 @@ static int parse_name_table(txth_header* txth, char* name_list) { /* ignore if name is not actually found (values will return 0) */ - close_streamfile(nameFile); + close_streamfile(sf_names); return 1; fail: - close_streamfile(nameFile); + close_streamfile(sf_names); return 0; } +static int parse_multi_txth(txth_header* txth, char* names) { + STREAMFILE* sf_text = NULL; + char name[PATH_LIMIT]; + int n, ok; + + /* temp save */ + sf_text = txth->sf_text; + txth->sf_text = NULL; + + /* to avoid potential infinite recursion plus stack overflows */ + if (txth->is_multi_txth > 3) + goto fail; + txth->is_multi_txth++; + + while (names[0] != '\0') { + STREAMFILE* sf_test = NULL; + int found; + + ok = sscanf(names, " %[^\t#\r\n,]%n ", name, &n); + if (ok != 1) + goto fail; + + //;VGM_LOG("TXTH: multi name %s\n", name); + sf_test = open_streamfile_by_filename(txth->sf, name); + if (!sf_test) + goto fail; + + /* re-parse with current txth and hope */ + txth->sf_text = sf_test; + found = parse_txth(txth); + close_streamfile(sf_test); + //todo may need to close header/body streamfiles? + + if (found) { + //;VGM_LOG("TXTH: found valid multi txth %s\n", name); + break; /* found, otherwise keep trying */ + } + + names += n; + if (names[0] == ',') + names++; + } + + txth->is_multi_txth--; + txth->sf_text = sf_text; + return 1; +fail: + txth->is_multi_txth--; + txth->sf_text = sf_text; + return 0; +} + static int parse_num(STREAMFILE* sf, txth_header* txth, const char* val, uint32_t* out_value) { /* out_value can be these, save before modifying */ uint32_t value_mul = txth->value_mul; @@ -1734,6 +1830,7 @@ static int parse_num(STREAMFILE* sf, txth_header* txth, const char* val, uint32_ else if ((n = is_string_field(val,"loop_start"))) value = txth->loop_start_sample; else if ((n = is_string_field(val,"loop_end_sample"))) value = txth->loop_end_sample; else if ((n = is_string_field(val,"loop_end"))) value = txth->loop_end_sample; + else if ((n = is_string_field(val,"subsong"))) value = txth->target_subsong; else if ((n = is_string_field(val,"subsong_count"))) value = txth->subsong_count; else if ((n = is_string_field(val,"subsong_spacing"))) value = txth->subsong_spacing; else if ((n = is_string_field(val,"subsong_offset"))) value = txth->subsong_spacing; diff --git a/Frameworks/vgmstream/vgmstream/src/streamfile.h b/Frameworks/vgmstream/vgmstream/src/streamfile.h index 6bd04397f..148297af9 100644 --- a/Frameworks/vgmstream/vgmstream/src/streamfile.h +++ b/Frameworks/vgmstream/vgmstream/src/streamfile.h @@ -260,17 +260,24 @@ static inline float read_f32be_m(off_t offset, STREAMFILE* sf) { return sample_float; } #endif + #if 0 + +// on GCC, this reader will be correctly optimized out (as long as it's static/inline), would be same as declaring: +// uintXX_t (*read_uXX)(off_t,uint8_t*) = be ? get_uXXbe : get_uXXle; +// only for the functions actually used in code, and inlined if possible (like big_endian param being a constant). +// on MSVC seems all read_X in sf_reader are compiled and included in the translation unit, plus ignores constants +// so may result on bloatness? +// (from godbolt tests, test more real cases) + /* collection of callbacks for quick access */ typedef struct sf_reader { - int32_t (*read_s32)(off_t,STREAMFILE*); //maybe s32 + int32_t (*read_s32)(off_t,STREAMFILE*); //maybe r.s32 float (*read_f32)(off_t,STREAMFILE*); /* ... */ } sf_reader; -void init_reader(sf_reader *r, int big_endian); -/* ... */ -void sf_reader_init(sf_reader *r, int big_endian) { +static inline void sf_reader_init(sf_reader* r, int big_endian) { memset(r, 0, sizeof(sf_reader)); if (big_endian) { r->read_s32 = read_s32be; @@ -281,6 +288,7 @@ void sf_reader_init(sf_reader *r, int big_endian) { r->read_f32 = read_f32le; } } + /* sf_reader r; * ... * sf_reader_init(&r, big_endian); @@ -326,12 +334,12 @@ static inline /*const*/ int is_id64be(off_t offset, STREAMFILE* sf, const char* static inline int guess_endianness16bit(off_t offset, STREAMFILE* sf) { uint8_t buf[0x02]; if (read_streamfile(buf, offset, 0x02, sf) != 0x02) return -1; /* ? */ - return (uint16_t)get_16bitLE(buf) > (uint16_t)get_16bitBE(buf) ? 1 : 0; + return get_u16le(buf) > get_u16be(buf) ? 1 : 0; } static inline int guess_endianness32bit(off_t offset, STREAMFILE* sf) { uint8_t buf[0x04]; if (read_streamfile(buf, offset, 0x04, sf) != 0x04) return -1; /* ? */ - return (uint32_t)get_32bitLE(buf) > (uint32_t)get_32bitBE(buf) ? 1 : 0; + return get_u32le(buf) > get_u32be(buf) ? 1 : 0; } static inline size_t align_size_to_block(size_t value, size_t block_align) {