From 54f6702fb240d75aa065716fa6de6e754691d8e9 Mon Sep 17 00:00:00 2001 From: Christopher Snowhill Date: Tue, 19 Apr 2022 23:29:21 -0700 Subject: [PATCH] Updated VGMStream to r1721-48-g9d47eded Signed-off-by: Christopher Snowhill --- .../libvgmstream.xcodeproj/project.pbxproj | 12 ++ .../vgmstream/vgmstream/src/coding/coding.h | 5 +- .../vgmstream/src/coding/mta2_decoder.c | 153 ++++++++------ .../vgmstream/src/coding/ubi_adpcm_decoder.c | 93 +++++---- Frameworks/vgmstream/vgmstream/src/decode.c | 2 +- Frameworks/vgmstream/vgmstream/src/formats.c | 5 + .../vgmstream/vgmstream/src/meta/aifc.c | 12 +- .../vgmstream/vgmstream/src/meta/bkhd.c | 27 ++- .../vgmstream/vgmstream/src/meta/ea_1snh.c | 6 +- .../vgmstream/vgmstream/src/meta/ea_schl.c | 2 +- Frameworks/vgmstream/vgmstream/src/meta/hca.c | 2 +- .../vgmstream/vgmstream/src/meta/hca_keys.h | 64 ++++++ .../vgmstream/vgmstream/src/meta/ktac.c | 3 +- .../vgmstream/vgmstream/src/meta/meta.h | 4 + .../vgmstream/vgmstream/src/meta/opus.c | 29 +++ Frameworks/vgmstream/vgmstream/src/meta/psb.c | 25 ++- .../vgmstream/vgmstream/src/meta/silence.c | 20 +- .../vgmstream/vgmstream/src/meta/sqex_sead.c | 9 +- .../vgmstream/vgmstream/src/meta/sspf.c | 159 +++++++++++++++ .../vgmstream/vgmstream/src/meta/txth.c | 37 ++-- .../vgmstream/vgmstream/src/meta/txtp.c | 62 +++--- .../vgmstream/vgmstream/src/meta/ubi_hx.c | 162 ++++++++++----- Frameworks/vgmstream/vgmstream/src/meta/xa.c | 2 +- Frameworks/vgmstream/vgmstream/src/mixing.c | 58 +++++- .../vgmstream/src/util/text_reader.c | 187 ++++++++++++++++++ .../vgmstream/src/util/text_reader.h | 43 ++++ .../vgmstream/vgmstream/src/vgmstream.c | 2 + .../vgmstream/vgmstream/src/vgmstream.h | 1 + 28 files changed, 954 insertions(+), 232 deletions(-) create mode 100644 Frameworks/vgmstream/vgmstream/src/meta/sspf.c create mode 100644 Frameworks/vgmstream/vgmstream/src/util/text_reader.c create mode 100644 Frameworks/vgmstream/vgmstream/src/util/text_reader.h diff --git a/Frameworks/vgmstream/libvgmstream.xcodeproj/project.pbxproj b/Frameworks/vgmstream/libvgmstream.xcodeproj/project.pbxproj index 8e7e58136..7722bf53d 100644 --- a/Frameworks/vgmstream/libvgmstream.xcodeproj/project.pbxproj +++ b/Frameworks/vgmstream/libvgmstream.xcodeproj/project.pbxproj @@ -136,6 +136,9 @@ 832FC36F278FAE3E0056A860 /* encrypted_mc161_streamfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 832FC36E278FAE3E0056A860 /* encrypted_mc161_streamfile.h */; }; 83345A521F8AEB2800B2EAA4 /* xvag.c in Sources */ = {isa = PBXBuildFile; fileRef = 83345A4E1F8AEB2800B2EAA4 /* xvag.c */; }; 83349719275DD2AC00302E21 /* wbk.c in Sources */ = {isa = PBXBuildFile; fileRef = 83349715275DD2AC00302E21 /* wbk.c */; }; + 8339B323280FDF250076F74B /* sspf.c in Sources */ = {isa = PBXBuildFile; fileRef = 8339B322280FDF250076F74B /* sspf.c */; }; + 8339B326280FDF4B0076F74B /* text_reader.c in Sources */ = {isa = PBXBuildFile; fileRef = 8339B324280FDF4B0076F74B /* text_reader.c */; }; + 8339B327280FDF4B0076F74B /* text_reader.h in Headers */ = {isa = PBXBuildFile; fileRef = 8339B325280FDF4B0076F74B /* text_reader.h */; }; 833A7A2E1ED11961003EC53E /* xau.c in Sources */ = {isa = PBXBuildFile; fileRef = 833A7A2D1ED11961003EC53E /* xau.c */; }; 8342469420C4D23000926E48 /* h4m.c in Sources */ = {isa = PBXBuildFile; fileRef = 8342469020C4D22F00926E48 /* h4m.c */; }; 8342469620C4D23D00926E48 /* blocked_h4m.c in Sources */ = {isa = PBXBuildFile; fileRef = 8342469520C4D23D00926E48 /* blocked_h4m.c */; }; @@ -924,6 +927,9 @@ 832FC36E278FAE3E0056A860 /* encrypted_mc161_streamfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = encrypted_mc161_streamfile.h; sourceTree = ""; }; 83345A4E1F8AEB2800B2EAA4 /* xvag.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xvag.c; sourceTree = ""; }; 83349715275DD2AC00302E21 /* wbk.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = wbk.c; sourceTree = ""; }; + 8339B322280FDF250076F74B /* sspf.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = sspf.c; sourceTree = ""; }; + 8339B324280FDF4B0076F74B /* text_reader.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = text_reader.c; sourceTree = ""; }; + 8339B325280FDF4B0076F74B /* text_reader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = text_reader.h; sourceTree = ""; }; 833A7A2D1ED11961003EC53E /* xau.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xau.c; sourceTree = ""; }; 8342469020C4D22F00926E48 /* h4m.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = h4m.c; sourceTree = ""; }; 8342469520C4D23D00926E48 /* blocked_h4m.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = blocked_h4m.c; sourceTree = ""; }; @@ -2142,6 +2148,7 @@ 836F6EF418BDC2190095E648 /* sqex_scd.c */, 837CEAF023487F2C00E62A4A /* sqex_sead_streamfile.h */, 83A21F84201D8981000F04B9 /* sqex_sead.c */, + 8339B322280FDF250076F74B /* sspf.c */, 8317C24826982CC1007DD0B8 /* sspr.c */, 836F6EBA18BDC2180095E648 /* ster.c */, 8306B0C12098458C000302D4 /* sthd.c */, @@ -2269,6 +2276,8 @@ 83D26A7F26E66DC2001A9475 /* log.h */, 8315868826F586F900803A3A /* m2_psb.c */, 8315868926F586F900803A3A /* m2_psb.h */, + 8339B324280FDF4B0076F74B /* text_reader.c */, + 8339B325280FDF4B0076F74B /* text_reader.h */, ); path = util; sourceTree = ""; @@ -2370,6 +2379,7 @@ 834FE103215C79ED000A5D3D /* ea_schl_streamfile.h in Headers */, 83F0AA6021E2028C004BBC04 /* vsv_streamfile.h in Headers */, 83FBD506235D31F800D35BCD /* riff_ogg_streamfile.h in Headers */, + 8339B327280FDF4B0076F74B /* text_reader.h in Headers */, 836F6F9A18BDC2190095E648 /* meta.h in Headers */, 83A8BAE625667AA8000F5F3F /* ps2_enth_streamfile.h in Headers */, 8306B0D820984590000302D4 /* ea_eaac_streamfile.h in Headers */, @@ -2582,6 +2592,7 @@ 836F6F7D18BDC2190095E648 /* dc_str.c in Sources */, 83A5F75F198DF021009AF94C /* bfwav.c in Sources */, 8373341723F60C7B00DE14DC /* g7221_decoder_aes.c in Sources */, + 8339B326280FDF4B0076F74B /* text_reader.c in Sources */, 836F702018BDC2190095E648 /* rkv.c in Sources */, 834FE0F4215C79ED000A5D3D /* wsi.c in Sources */, 83D26A7A26E66D98001A9475 /* adp_bos.c in Sources */, @@ -3013,6 +3024,7 @@ 835B9B8F2730BF2D00F87EE3 /* ast_mv.c in Sources */, 83AA5D1D1F6E2F800020821C /* blocked_vgs.c in Sources */, 836F702E18BDC2190095E648 /* sli.c in Sources */, + 8339B323280FDF250076F74B /* sspf.c in Sources */, 83AA7F822519C042004C5298 /* ktsc.c in Sources */, 836F6FDE18BDC2190095E648 /* ps2_ild.c in Sources */, 836F703E18BDC2190095E648 /* wii_ras.c in Sources */, diff --git a/Frameworks/vgmstream/vgmstream/src/coding/coding.h b/Frameworks/vgmstream/vgmstream/src/coding/coding.h index d9c237e86..1b3d52b15 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/coding.h +++ b/Frameworks/vgmstream/vgmstream/src/coding/coding.h @@ -204,7 +204,7 @@ void decode_mtaf(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, /* mta2_decoder */ -void decode_mta2(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); +void decode_mta2(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, int config); /* mc3_decoder */ @@ -270,7 +270,8 @@ void decode_ubi_adpcm(VGMSTREAM* vgmstream, sample_t* outbuf, int32_t samples_to void reset_ubi_adpcm(ubi_adpcm_codec_data* data); void seek_ubi_adpcm(ubi_adpcm_codec_data* data, int32_t num_sample); void free_ubi_adpcm(ubi_adpcm_codec_data* data); -int ubi_adpcm_get_samples(ubi_adpcm_codec_data* data); +int32_t ubi_adpcm_get_samples(ubi_adpcm_codec_data* data); +int32_t ubi_adpcm_bytes_to_samples(ubi_adpcm_codec_data* data, uint32_t size); /* imuse_decoder */ diff --git a/Frameworks/vgmstream/vgmstream/src/coding/mta2_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/mta2_decoder.c index 63eb08622..661e4efab 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/mta2_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/mta2_decoder.c @@ -4,30 +4,37 @@ /* MTA2 decoder based on: * - MGS Developer Wiki: https://www.mgsdevwiki.com/wiki/index.php/MTA2_(Codec) [codec by daemon1] * - Solid4 tools: https://github.com/GHzGangster/Drebin - * (PS3 probably uses floats, so this may not be 100% accurate) + * - Partially reverse engineered to fix tables + * - Internal codec name may be "vax2", with Mta2 being the file format. * * MTA2 layout: * - data is divided into N tracks of 0x10 header + 0x90 frame per track channel, forming N streams * ex: 8ch: track0 4ch + track1 4ch + track0 4ch + track1 4ch ...; or 2ch = 1ch track0 + 1ch track1 * * up to 16 possible tracks, but max seen is 3 (ex. track0=sneaking, track1=action, track2=ambience) * - each ch frame is divided into 4 headers + 4 vertical groups with nibbles (0x4*4 + 0x20*4) - * ex. group1 is 0x04(4) + 0x14(4) + 0x24(4) + 0x34(4) ... (vertically maybe for paralelism?) + * ex. group1 is 0x04(4) + 0x14(4) + 0x24(4) + 0x34(4) ... (seemingly for vector paralelism) * * Due to this vertical layout and multiple hist/indexes, it decodes everything in a block between calls * but discards unwanted data, instead of trying to skip to the target nibble. Meaning no need to save hist, and * expects samples_to_do to be block_samples at most (could be simplified, I guess). */ -/* tweaked XA/PSX coefs << 8 */ +/* tblSsw2Vax2K0 / K1 (extended from classic XA's K0/K1 */ +static const float VAX2_K0[8] = { 0.0, 0.9375, 1.796875, 1.53125, 1.90625, 1.796875, 1.796875, 0.9375 }; +static const float VAX2_K1[8] = { -0.0, -0.0, -0.8125, -0.859375, -0.9375, -0.9375, -0.859375, -0.40625 }; +/* tblSsw2Vax2Rng */ +static const float VAX2_RANGES[32] = { + 1.0, 1.3125, 1.6875, 2.25, 2.9375, 3.8125, 5.0, 6.5625, + 8.5625, 11.1875, 14.625, 19.125, 25.0, 32.75, 42.8125, 55.9375, + 73.1875, 95.6875, 125.1875, 163.6875, 214.0625, 279.9375, 366.125, 478.8125, + 626.125, 818.8125, 1070.8125, 1400.375, 1831.375, 2395.0, 3132.0625, 4096.0 +}; + +/* somewhat equivalent tables K*2^8 (as found elsewhere): */ +# if 0 static const int16_t mta2_coefs[8][2] = { - { 0, 0 }, - { 240, 0 }, - { 460, -208 }, - { 392, -220 }, - { 488, -240 }, - { 460, -240 }, - { 460, -220 }, - { 240, -104 } + { 0, 0 }, { 240, 0 }, { 460, -208 }, { 392, -220 }, + { 488, -240 }, { 460, -240 }, { 460, -220 }, { 240, -104 } }; static const int mta2_scales[32] = { @@ -36,72 +43,93 @@ static const int mta2_scales[32] = { 18736, 24503, 32043, 41905, 54802, 71668, 93724, 122568, 160290, 209620, 274133, 358500, 468831, 613119, 801811, 1048576 }; +#endif /* decodes a block for a channel */ -void decode_mta2(VGMSTREAMCHANNEL *stream, sample_t *outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { +void decode_mta2(VGMSTREAMCHANNEL *stream, sample_t *outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, int config) { uint8_t frame[0x10 + 0x90*8] = {0}; int samples_done = 0, sample_count = 0, channel_block_samples, channel_first_sample, frame_size = 0; int i, group, row, col; - int track_channels = 0, track_channel; + int track_channel; + uint32_t frame_offset = stream->offset; + uint32_t head_size; - /* track skip */ - do { - int num_track = 0, channel_layout; - - /* parse track header (0x10) and skip tracks that our current channel doesn't belong to */ - read_streamfile(frame, stream->offset, 0x10, stream->streamfile); /* ignore EOF errors */ - num_track = get_u8 (frame + 0x00); /* 0=first */ - /* 0x01(3): num_frame (0=first) */ - /* 0x04(1): 0? */ - channel_layout = get_u8 (frame + 0x05); /* bitmask, see mta2.c */ - frame_size = get_u16be(frame + 0x06); /* not including this header */ - /* 0x08(8): null */ - - VGM_ASSERT(frame_size == 0, "MTA2: empty frame at %x\n", (uint32_t)stream->offset); - /* frame_size 0 means silent/empty frame (rarely found near EOF for one track but not others) - * negative track only happens for truncated files (EOF) */ - if (frame_size == 0 || num_track < 0) { - for (i = 0; i < samples_to_do; i++) - outbuf[i * channelspacing] = 0; - stream->offset += 0x10; - return; - } - - track_channels = 0; - for (i = 0; i < 8; i++) { /* max 8ch */ - if ((channel_layout >> i) & 0x01) - track_channels++; - } - - if (track_channels == 0) { /* bad data, avoid div by 0 */ - VGM_LOG("MTA2: track_channels 0 at %x\n", (uint32_t)stream->offset); - return; - } - - /* assumes tracks channels are divided evenly in all tracks (ex. not 2ch + 1ch + 1ch) */ - if (channel / track_channels == num_track) - break; /* channel belongs to this track */ - - /* keep looping for our track */ - stream->offset += 0x10 + frame_size; + if (config == 1) { + /* regular frames (sfx) */ + frame_size = 0x90 * channelspacing; + track_channel = channel; + head_size = 0x00; } - while (1); + else { + /* track info (bgm): parse header and skip tracks that our current channel doesn't belong to */ + int track_channels; + + head_size = 0x10; + do { + int num_track = 0, channel_layout; + + read_streamfile(frame, frame_offset, head_size, stream->streamfile); /* ignore EOF errors */ + num_track = get_u8 (frame + 0x00); /* 0=first */ + /* 0x01(3): num_frame (0=first) */ + /* 0x04(1): 0? */ + channel_layout = get_u8 (frame + 0x05); /* bitmask, see mta2.c */ + frame_size = get_u16be(frame + 0x06); /* not including this header */ + /* 0x08(8): null */ + + VGM_ASSERT(frame_size == 0, "MTA2: empty frame at %x\n", frame_offset); + /* frame_size 0 means silent/empty frame (rarely found near EOF for one track but not others) + * negative track only happens for truncated files (EOF) */ + if (frame_size == 0 || num_track < 0) { + for (i = 0; i < samples_to_do; i++) + outbuf[i * channelspacing] = 0; + stream->offset += 0x10; + return; + } + + + track_channels = 0; + for (i = 0; i < 8; i++) { /* max 8ch */ + if ((channel_layout >> i) & 0x01) + track_channels++; + } + + if (track_channels == 0) { /* bad data, avoid div by 0 */ + VGM_LOG("MTA2: track_channels 0 at %x\n", frame_offset); + return; + } + + /* assumes tracks channels are divided evenly in all tracks (ex. not 2ch + 1ch + 1ch) */ + if (channel / track_channels == num_track) + break; /* channel belongs to this track */ + + /* keep looping for our track */ + stream->offset += head_size + frame_size; + frame_offset = stream->offset; + } + while (1); + + frame_offset += head_size; /* point to actual data */ + track_channel = channel % track_channels; + if (frame_size > sizeof(frame)) + return; + } + /* parse stuff */ - read_streamfile(frame + 0x10, stream->offset + 0x10, frame_size, stream->streamfile); /* ignore EOF errors */ - track_channel = channel % track_channels; + read_streamfile(frame, frame_offset, frame_size, stream->streamfile); /* ignore EOF errors */ channel_block_samples = (0x80*2); channel_first_sample = first_sample % (0x80*2); + /* parse channel frame (header 0x04*4 + data 0x20*4) */ for (group = 0; group < 4; group++) { short hist2, hist1, coefs, scale; - uint32_t group_header = get_u32be(frame + 0x10 + track_channel*0x90 + group*0x4); + uint32_t group_header = get_u32be(frame + track_channel*0x90 + group*0x4); hist2 = (short) ((group_header >> 16) & 0xfff0); /* upper 16b discarding 4b */ hist1 = (short) ((group_header >> 4) & 0xfff0); /* lower 16b discarding 4b */ - coefs = (group_header >> 5) & 0x7; /* mid 3b */ - scale = group_header & 0x1f; /* lower 5b */ + coefs = (group_header >> 5) & 0x07; /* mid 3b */ + scale = (group_header >> 0) & 0x1f; /* lower 5b */ /* write header samples (skips the last 2 group nibbles), like Drebin's decoder * last 2 nibbles and next 2 header hist should match though */ @@ -118,7 +146,7 @@ void decode_mta2(VGMSTREAMCHANNEL *stream, sample_t *outbuf, int channelspacing, /* decode nibbles */ for (row = 0; row < 8; row++) { - int pos = 0x10 + track_channel*0x90 + 0x10 + group*0x4 + row*0x10; + int pos = track_channel*0x90 + 0x10 + group*0x4 + row*0x10; for (col = 0; col < 4*2; col++) { uint8_t nibbles = frame[pos + col/2]; int32_t sample; @@ -126,8 +154,11 @@ void decode_mta2(VGMSTREAMCHANNEL *stream, sample_t *outbuf, int channelspacing, sample = col&1 ? /* high nibble first */ get_low_nibble_signed(nibbles) : get_high_nibble_signed(nibbles); +#if 0 sample = sample * mta2_scales[scale]; sample = (sample + hist1 * mta2_coefs[coefs][0] + hist2 * mta2_coefs[coefs][1] + 128) >> 8; +#endif + sample = (sample * VAX2_RANGES[scale] + hist1 * VAX2_K0[coefs] + hist2 * VAX2_K1[coefs]); /* f32 sample to int */ sample = clamp16(sample); /* ignore last 2 nibbles (uses first 2 header samples) */ @@ -148,6 +179,6 @@ void decode_mta2(VGMSTREAMCHANNEL *stream, sample_t *outbuf, int channelspacing, /* block fully done */ if (channel_first_sample + samples_done == channel_block_samples) { - stream->offset += 0x10 + frame_size; + stream->offset += head_size + frame_size; } } diff --git a/Frameworks/vgmstream/vgmstream/src/coding/ubi_adpcm_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/ubi_adpcm_decoder.c index 0d7be9ef8..948f039d2 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/ubi_adpcm_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/ubi_adpcm_decoder.c @@ -29,7 +29,7 @@ typedef struct { uint32_t codes_per_subframe_last; uint32_t codes_per_subframe; uint32_t subframes_per_frame; - uint32_t sample_rate; + uint32_t unknown18; uint32_t unknown1c; uint32_t unknown20; uint32_t bits_per_sample; @@ -179,18 +179,18 @@ void free_ubi_adpcm(ubi_adpcm_codec_data *data) { /* ************************************************************************ */ static void read_header_state(uint8_t* data, ubi_adpcm_header_data* header) { - header->signature = get_32bitLE(data + 0x00); - header->sample_count = get_32bitLE(data + 0x04); - header->subframe_count = get_32bitLE(data + 0x08); - header->codes_per_subframe_last= get_32bitLE(data + 0x0c); - header->codes_per_subframe = get_32bitLE(data + 0x10); - header->subframes_per_frame = get_32bitLE(data + 0x14); - header->sample_rate = get_32bitLE(data + 0x18); /* optional? */ - header->unknown1c = get_32bitLE(data + 0x1c); /* variable */ - header->unknown20 = get_32bitLE(data + 0x20); /* null? */ - header->bits_per_sample = get_32bitLE(data + 0x24); - header->unknown28 = get_32bitLE(data + 0x28); /* 1~3? */ - header->channels = get_32bitLE(data + 0x2c); + header->signature = get_u32le(data + 0x00); + header->sample_count = get_u32le(data + 0x04); + header->subframe_count = get_u32le(data + 0x08); + header->codes_per_subframe_last= get_u32le(data + 0x0c); + header->codes_per_subframe = get_u32le(data + 0x10); + header->subframes_per_frame = get_u32le(data + 0x14); + header->unknown18 = get_u32le(data + 0x18); /* sometimes sample rate but algo other values (garbage?) */ + header->unknown1c = get_u32le(data + 0x1c); /* variable */ + header->unknown20 = get_u32le(data + 0x20); /* null? */ + header->bits_per_sample = get_u32le(data + 0x24); + header->unknown28 = get_u32le(data + 0x28); /* 1~3? */ + header->channels = get_u32le(data + 0x2c); } static int parse_header(STREAMFILE* sf, ubi_adpcm_codec_data* data, off_t offset) { @@ -463,7 +463,7 @@ static void unpack_codes(uint8_t* data, uint8_t* codes, int code_count, int bps) for (i = 0; i < code_count; i++) { if (bits < bps) { - uint32_t source32le = (uint32_t)get_32bitLE(data + pos); + uint32_t source32le = get_u32le(data + pos); pos += 0x04; input = (input << 32) | (uint64_t)source32le; @@ -480,31 +480,31 @@ static void read_channel_state(uint8_t* data, ubi_adpcm_channel_data* ch) { * probably exist for padding (original code uses MMX to operate in multiple 16b at the same time) * or reserved for other bit modes */ - ch->signature = get_32bitLE(data + 0x00); - ch->step1 = get_32bitLE(data + 0x04); - ch->next1 = get_32bitLE(data + 0x08); - ch->next2 = get_32bitLE(data + 0x0c); + ch->signature = get_u32le(data + 0x00); + ch->step1 = get_s32le(data + 0x04); + ch->next1 = get_s32le(data + 0x08); + ch->next2 = get_s32le(data + 0x0c); - ch->coef1 = get_16bitLE(data + 0x10); - ch->coef2 = get_16bitLE(data + 0x12); - ch->unused1 = get_16bitLE(data + 0x14); - ch->unused2 = get_16bitLE(data + 0x16); - ch->mod1 = get_16bitLE(data + 0x18); - ch->mod2 = get_16bitLE(data + 0x1a); - ch->mod3 = get_16bitLE(data + 0x1c); - ch->mod4 = get_16bitLE(data + 0x1e); + ch->coef1 = get_s16le(data + 0x10); + ch->coef2 = get_s16le(data + 0x12); + ch->unused1 = get_s16le(data + 0x14); + ch->unused2 = get_s16le(data + 0x16); + ch->mod1 = get_s16le(data + 0x18); + ch->mod2 = get_s16le(data + 0x1a); + ch->mod3 = get_s16le(data + 0x1c); + ch->mod4 = get_s16le(data + 0x1e); - ch->hist1 = get_16bitLE(data + 0x20); - ch->hist2 = get_16bitLE(data + 0x22); - ch->unused3 = get_16bitLE(data + 0x24); - ch->unused4 = get_16bitLE(data + 0x26); - ch->delta1 = get_16bitLE(data + 0x28); - ch->delta2 = get_16bitLE(data + 0x2a); - ch->delta3 = get_16bitLE(data + 0x2c); - ch->delta4 = get_16bitLE(data + 0x2e); + ch->hist1 = get_s16le(data + 0x20); + ch->hist2 = get_s16le(data + 0x22); + ch->unused3 = get_s16le(data + 0x24); + ch->unused4 = get_s16le(data + 0x26); + ch->delta1 = get_s16le(data + 0x28); + ch->delta2 = get_s16le(data + 0x2a); + ch->delta3 = get_s16le(data + 0x2c); + ch->delta4 = get_s16le(data + 0x2e); - ch->delta5 = get_16bitLE(data + 0x30); - ch->unused5 = get_16bitLE(data + 0x32); + ch->delta5 = get_s16le(data + 0x30); + ch->unused5 = get_s16le(data + 0x32); VGM_ASSERT(ch->signature != 0x02, "UBI ADPCM: incorrect channel header\n"); VGM_ASSERT(ch->unused3 != 0x00, "UBI ADPCM: found unused3 used\n"); @@ -580,9 +580,28 @@ static void decode_frame(STREAMFILE* sf, ubi_adpcm_codec_data* data) { } -int ubi_adpcm_get_samples(ubi_adpcm_codec_data* data) { +int32_t ubi_adpcm_get_samples(ubi_adpcm_codec_data* data) { if (!data) return 0; return data->header.sample_count / data->header.channels; } + +int32_t ubi_adpcm_bytes_to_samples(ubi_adpcm_codec_data* data, uint32_t size) { + uint32_t frame_size; + + if (!data || !data->header.channels || !data->header.subframes_per_frame) + return 0; + + /* don't trust subframe count */ + + size -= 0x30; /* header */ + + frame_size = 0x34 * data->header.channels; /* setup per channel */ + frame_size += (data->header.codes_per_subframe * data->header.bits_per_sample /*+ 8*/) * data->header.subframes_per_frame / 8; + frame_size += data->header.subframes_per_frame * 0x01; /* padding byte */ + + return ((size - 0x01) / frame_size) * /* force smaller size so last frame isn't used */ + data->header.codes_per_subframe * data->header.subframes_per_frame + + data->header.codes_per_subframe_last * data->header.subframes_per_frame; +} diff --git a/Frameworks/vgmstream/vgmstream/src/decode.c b/Frameworks/vgmstream/vgmstream/src/decode.c index 0ee85100e..1549e78ea 100644 --- a/Frameworks/vgmstream/vgmstream/src/decode.c +++ b/Frameworks/vgmstream/vgmstream/src/decode.c @@ -1392,7 +1392,7 @@ void decode_vgmstream(VGMSTREAM* vgmstream, int samples_written, int samples_to_ case coding_MTA2: for (ch = 0; ch < vgmstream->channels; ch++) { decode_mta2(&vgmstream->ch[ch], buffer+ch, - vgmstream->channels, vgmstream->samples_into_block, samples_to_do, ch); + vgmstream->channels, vgmstream->samples_into_block, samples_to_do, ch, vgmstream->codec_config); } break; case coding_MC3: diff --git a/Frameworks/vgmstream/vgmstream/src/formats.c b/Frameworks/vgmstream/vgmstream/src/formats.c index 649464b3f..f7a90af4e 100644 --- a/Frameworks/vgmstream/vgmstream/src/formats.c +++ b/Frameworks/vgmstream/vgmstream/src/formats.c @@ -229,6 +229,7 @@ static const char* extension_list[] = { "ifs", "ikm", "ild", + "ilf", //txth/reserved [Madden NFL 98 (PS1)] "ilv", //txth/reserved [Star Wars Episode III (PS2)] "ima", "imc", @@ -417,6 +418,7 @@ static const char* extension_list[] = { "rsd", "rsf", "rsm", + "rsnd", //txth/reserved [Birushana: Ichijuu no Kaze (Switch)] "rsp", "rstm", //fake extension/header id for .rstm (in bigfiles) "rvws", @@ -500,6 +502,7 @@ static const char* extension_list[] = { "ssd", //txth/reserved [Zack & Wiki (Wii)] "ssm", "sspr", + "ssp", "sss", "ster", "sth", @@ -563,6 +566,7 @@ static const char* extension_list[] = { "vid", "vig", "vis", + "vm4", //txth/reserved [Elder Gate (PS1)] "vms", "vmu", //txth/reserved [Red Faction (PS2)] "voi", @@ -1383,6 +1387,7 @@ static const meta_info meta_info_list[] = { {meta_WBK_NSLB, "Treyarch NSLB header"}, {meta_DSP_APEX, "Koei Tecmo APEX header"}, {meta_MPEG, "MPEG header"}, + {meta_SSPF, "Konami SSPF header"}, }; void get_vgmstream_coding_description(VGMSTREAM* vgmstream, char* out, size_t out_size) { diff --git a/Frameworks/vgmstream/vgmstream/src/meta/aifc.c b/Frameworks/vgmstream/vgmstream/src/meta/aifc.c index 678895c0a..ad6822b03 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/aifc.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/aifc.c @@ -83,6 +83,7 @@ VGMSTREAM* init_vgmstream_aifc(STREAMFILE* sf) { /* checks */ if (!is_id32be(0x00,sf, "FORM")) goto fail; +VGM_LOG("1\n"); /* .aif: common (AIFF or AIFC), .aiff: common AIFF, .aifc: common AIFC * .laif/laiff/laifc: for plugins @@ -95,12 +96,15 @@ VGMSTREAM* init_vgmstream_aifc(STREAMFILE* sf) { * .fda: Homeworld 2 (PC) * .n64: Turok (N64) src * .pcm: Road Rash (SAT) + * .wav: SimCity 3000 (Mac) (both AIFC and AIFF) + * .lwav: for media players that may confuse this format with the usual RIFF WAVE file. + * .xa: SimCity 3000 (Mac) */ - if (check_extensions(sf, "aif,laif,")) { + if (check_extensions(sf, "aif,laif,wav,lwav,")) { is_aifc_ext = 1; is_aiff_ext = 1; } - else if (check_extensions(sf, "aifc,laifc,afc,cbd2,bgm,fda,n64")) { + else if (check_extensions(sf, "aifc,laifc,afc,cbd2,bgm,fda,n64,xa")) { is_aifc_ext = 1; } else if (check_extensions(sf, "aiff,laiff,acm,adp,ai,pcm")) { @@ -133,8 +137,10 @@ VGMSTREAM* init_vgmstream_aifc(STREAMFILE* sf) { aifx_size += 0x08; /* [Psychic Force Puzzle Taisen CD2 (PS1)] */ } - if (aifx_size + 0x08 != file_size) + if (aifx_size + 0x08 != file_size) { + vgm_logi("AIFF: wrong reported size %x + 0x8 vs file size %x\n", aifx_size, file_size); goto fail; + } /* read through chunks to verify format and find metadata */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/bkhd.c b/Frameworks/vgmstream/vgmstream/src/meta/bkhd.c index 37f7dde74..2b255bfd9 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/bkhd.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/bkhd.c @@ -126,25 +126,14 @@ VGMSTREAM* init_vgmstream_bkhd(STREAMFILE* sf) { /* detect format */ if (subfile_offset <= 0 || subfile_size <= 0) { - /* some indexes don't have data */ is_dummy = 1; - } - else if (read_f32(subfile_offset + 0x02, sf) >= 30.0 && - read_f32(subfile_offset + 0x02, sf) <= 250.0) { - /* ignore Wwise's custom .wmid (similar to a regular midi but with simplified - * chunks and custom fields: 0x00=MThd's division, 0x02: bpm (new), etc) */ - is_wmid = 1; - } - /* default is riff/sfx */ - - - if (is_dummy || is_wmid) { - /* for now leave a dummy song for easier .bnk index-to-subsong mapping */ + /* rarely some indexes don't have data (early bnk) + * for now leave a dummy song for easier .bnk index-to-subsong mapping */ vgmstream = init_vgmstream_silence(0, 0, 0); if (!vgmstream) goto fail; } else { - /* could pass .wem but few files need memory .wem detection */ + /* could pass .wem extension but few files need memory .wem detection */ temp_sf = setup_subfile_streamfile(sf, subfile_offset, subfile_size, NULL); if (!temp_sf) goto fail; @@ -153,13 +142,21 @@ VGMSTREAM* init_vgmstream_bkhd(STREAMFILE* sf) { vgmstream = init_vgmstream_wwise_bnk(temp_sf, &prefetch); if (!vgmstream) goto fail; } + else if (read_f32(subfile_offset + 0x02, temp_sf) >= 30.0 && + read_f32(subfile_offset + 0x02, temp_sf) <= 250.0) { + is_wmid = 1; + /* ignore Wwise's custom .wmid (similar to a regular midi but with simplified + * chunks and custom fields: 0x00=MThd's division, 0x02: bpm (new), etc) */ + vgmstream = init_vgmstream_silence(0, 0, 0); + if (!vgmstream) goto fail; + } else { + /* may fail if not an actual wfx */ vgmstream = init_vgmstream_bkhd_fx(temp_sf); if (!vgmstream) goto fail; } } - vgmstream->num_streams = total_subsongs; { diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ea_1snh.c b/Frameworks/vgmstream/vgmstream/src/meta/ea_1snh.c index 206c90099..d3714f9c2 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ea_1snh.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ea_1snh.c @@ -235,11 +235,15 @@ static int parse_header(STREAMFILE* sf, eacs_header* ea, uint32_t offset) { ea->num_samples = read_s32(offset+0x0c, sf); ea->loop_start = read_s32(offset+0x10, sf); ea->loop_end = read_s32(offset+0x14, sf) + ea->loop_start; /* loop length */ - ea->data_offset = read_s32(offset+0x18, sf); /* 0 when blocked */ + ea->data_offset = read_s32(offset+0x18, sf); /* 0 when blocked, usually */ /* 0x1c: pan/volume/etc? (0x7F) * rest may be padding/garbage */ //VGM_ASSERT(ea->type != 0, "EA EACS: unknown type %i\n", ea->type); + /* blocked should set 0 but in rare cases points to data start [NBA Live 95 (MS-DOS)] */ + if (!ea->is_bank) + ea->data_offset = 0; + if (ea->codec == EA_CODEC_IMA) ea->codec_config = get_ea_1snh_ima_version(sf, 0x00, ea); /* EACS banks with empty values exist but will be rejected later */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ea_schl.c b/Frameworks/vgmstream/vgmstream/src/meta/ea_schl.c index d4f271fe3..5b0bbac17 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ea_schl.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ea_schl.c @@ -1086,7 +1086,7 @@ VGMSTREAM* init_vgmstream_ea_mpf_mus(STREAMFILE* sf) { if (!vgmstream) goto fail; } else { - if (version == 5 && read_u32be(0x00, sf_mus) != track_checksum) + if (version == 5 && track_checksum && read_u32be(0x00, sf_mus) != track_checksum) goto fail; sound_offset *= off_mult;; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/hca.c b/Frameworks/vgmstream/vgmstream/src/meta/hca.c index 57a2060fe..61105d5a9 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/hca.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/hca.c @@ -46,7 +46,7 @@ VGMSTREAM* init_vgmstream_hca_subkey(STREAMFILE* sf, uint16_t subkey) { uint8_t keybuf[0x08+0x02]; size_t keysize; - keysize = read_key_file(keybuf, 0x08+0x04, sf); + keysize = read_key_file(keybuf, sizeof(keybuf), sf); if (keysize == 0x08) { /* standard */ keycode = get_u64be(keybuf+0x00); } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/hca_keys.h b/Frameworks/vgmstream/vgmstream/src/meta/hca_keys.h index fb971f29f..b456a86f4 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/hca_keys.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/hca_keys.h @@ -439,6 +439,7 @@ static const hcakey_info hcakey_list[] = { {0x9f7a0810034669fe}, //music_0110020 {0xe8333d53d2779e38}, //music_0110021 {0x2cdcac4f44f67075}, //music_0110022 + {0x670c738c8171210b}, //music_0110023 {0xfb647d074e53fab6}, //music_0120001 {0xc24049b9f7ed3105}, //music_0120002 {0xdc128f2fd48bf4b}, //music_0120003 @@ -458,6 +459,7 @@ static const hcakey_info hcakey_list[] = { {0xadfecfaf25cfe2ce}, //music_0120017 {0x3674aba8da7bc84b}, //music_0120018 {0xfd61f2c3b89f3888}, //music_0120019 + {0x514fa9879fd07278}, //music_0120021 {0x4fffee4065d22bec}, //music_0210001 {0x7678588b0adf59df}, //music_0210002 {0xa0316b536c8b7540}, //music_0210003 @@ -470,6 +472,7 @@ static const hcakey_info hcakey_list[] = { {0x5ef795cdbcdcba91}, //music_0210010 {0x868acc0102c59a38}, //music_0210011 {0x6dc5ff77263450a5}, //music_0210012 + {0x1dca436afdd18d9}, //music_0210013 {0x15bb78c31db0a0b6}, //music_0220001 {0x59b1257242c40109}, //music_0220002 {0xdb402bd08d522f34}, //music_0220003 @@ -488,6 +491,7 @@ static const hcakey_info hcakey_list[] = { {0xe6d1fd6effa46736}, //music_0220017 {0xd23bdacd616fc4c9}, //music_0220018 {0xfceaa73248868ec5}, //music_0220019 + {0x3b6b0023a2dd8c3d}, //music_0220020 {0x6a15a9610d10d210}, //music_0310001 {0x57111c24801b44a1}, //music_0310002 {0x40443974a0a86b8b}, //music_0310003 @@ -502,6 +506,8 @@ static const hcakey_info hcakey_list[] = { {0xcc5610c09f472ce9}, //music_0310012 {0xd447a497c5547a1c}, //music_0310013 {0x227b85948bb3d899}, //music_0310014 + {0xfff671f0ddb660b1}, //music_0310015 + {0x22e33db9b5625a96}, //music_0310016 {0xb921c3992807dadd}, //music_0320001 {0x38ad99a045dc971f}, //music_0320002 {0xf616642579ba5850}, //music_0320003 @@ -518,6 +524,7 @@ static const hcakey_info hcakey_list[] = { {0x26ee13598091b548}, //music_0320014 {0xf06a6bfdd00c8286}, //music_0320015 {0x2df608ef06aca41c}, //music_0320016 + {0x641af19c287d4a2e}, //music_0320017 {0x776c4aded0bca5d1}, //music_0410001 {0xb7bff4fbf66be43f}, //music_0410002 {0x904f50c5ce8ec6e4}, //music_0410003 @@ -530,6 +537,7 @@ static const hcakey_info hcakey_list[] = { {0x75859a7a2b1ed37d}, //music_0410010 {0x2e5f57a6c6e9c97f}, //music_0410011 {0xa144f6d7de02e000}, //music_0410012 + {0x1da4370c9c20319c}, //music_0410013 {0x5d1f3fdbbb036f8d}, //music_0420001 {0xc04264e8f34ad5c0}, //music_0420002 {0x8f0e96b4f71f724f}, //music_0420003 @@ -561,6 +569,7 @@ static const hcakey_info hcakey_list[] = { {0x75c5bd4e3a01a8a4}, //music_0510013 {0xec5f5fbe92bbb771}, //music_0510014 {0xb8c3233338ad8e0}, //music_0510015 + {0xda4ce04dbda1bd7e}, //music_0510016 {0x15f82c1617013c36}, //music_0520001 {0xc7da8e6f0e2fe399}, //music_0520002 {0xe350bffcdc9cb686}, //music_0520003 @@ -576,6 +585,7 @@ static const hcakey_info hcakey_list[] = { {0x2e8d1134ce415f8c}, //music_0520013 {0x1bf43def1e4b103a}, //music_0520014 {0x6721ad5109e4840d}, //music_0520015 + {0xc488dd62fc89090}, //music_0520016 {0xd2ce91dbfc209b10}, //music_0610001 {0xa662be1601e49476}, //music_0610002 {0xe5e83d31e64273f8}, //music_0610003 @@ -589,6 +599,7 @@ static const hcakey_info hcakey_list[] = { {0x6492e7708204838}, //music_0610011 {0x957e4d3948427952}, //music_0610012 {0x7081f083ac3d6f0a}, //music_0610013 + {0xfcfa4dbd1ec6cfcb}, //music_0610014 {0x8258ddd6a1d0849b}, //music_0620001 {0x1dd21a1244ca12f1}, //music_0620002 {0xfdec74b23d8b494b}, //music_0620003 @@ -603,6 +614,7 @@ static const hcakey_info hcakey_list[] = { {0xfab3596f11cc4d7a}, //music_0620012 {0xe35d52b6d2c094fb}, //music_0620013 {0xcdb9bc2ad7024ca2}, //music_0620014 + {0xcf27380a5a949dc1}, //music_0620015 {0x5de2b0a34eee1c89}, //music_0620016 {0x2a47feac8dc3ca9c}, //music_3010001 {0x9ebbaf63ffe9d9ef}, //music_3010002 @@ -617,6 +629,7 @@ static const hcakey_info hcakey_list[] = { {0x7555feeaa2a8fac4}, //music_3010011 {0xa42de67a89fb3175}, //music_3010012 {0xbdd0c58062c675d4}, //music_3010014 + {0xef257f41a265a0af}, //music_3010015 {0xfd3ea450350d666f}, //music_3020001 {0x5e91a3790c32e2b3}, //music_3020002 {0x358adfd1bbd3a95e}, //music_3020003 @@ -626,6 +639,7 @@ static const hcakey_info hcakey_list[] = { {0x402b13df5481d4e6}, //music_3020007 {0x729efd67aede1a40}, //music_3020008 {0xb7b9a143742fa51e}, //music_3020009 + {0xc7750328bcd329f}, //music_3020010 {0xdfad847a86a126bb}, //music_5030001 {0x711ef85045b8c26e}, //music_5030002 {0xff7640b46d72b337}, //music_5030003 @@ -676,11 +690,21 @@ static const hcakey_info hcakey_list[] = { {0x458b73844ed5219e}, //music_5030048 {0x7d83b8da9023ef26}, //music_5030049 {0x32cb728ddab4d956}, //music_5030050 + {0xbd128c4a4d1f565b}, //music_5030051 + {0x363aa084f3bf7af1}, //music_5030052 {0x52c5dfb61fe4c87a}, //music_5030054 {0x3ebbccab07c9a9ba}, //music_5030055 + {0x400b11e930008a58}, //music_5030059 + {0x15aaecc4725a5f70}, //music_5030060 {0x7a5e0865ba8cafa7}, //music_5030061 {0x7679587f7292b057}, //music_5030062 {0xc9c804e6fed3387c}, //music_5030063 + {0xc72eb23bcdc43f42}, //music_5030064 + {0xf7cedd212a06307}, //music_5030065 + {0x850ad05f415d6018}, //music_5030066 + {0x36f62d41aa4203c9}, //music_5030067 + {0x2174d57bfeafc637}, //music_5030068 + {0x143a7405ef56e4df}, //music_5030069 {0x444dda6d55d76095}, //music_5040001 {0xcbf4f1324081e0a6}, //music_5040002 {0xf1db3c1d9542063a}, //music_5040003 @@ -802,11 +826,44 @@ static const hcakey_info hcakey_list[] = { {0x79c1f27fa0f8c937}, //music_5050103 {0xe1e4f9125646aa8a}, //music_5050104 {0xd5cf3ce581c59e40}, //music_5050105 + {0x5509e33bc008bf09}, //music_5050106 {0x5ecb21ac94aa4b8f}, //music_5050107 {0x3786b3940e98628a}, //music_5050108 + {0x54de39434bfe4f07}, //music_5050109 + {0x919d24cc51244387}, //music_5050110 + {0x9d020e395e34dc9d}, //music_5050111 + {0x35cd0df418d118d7}, //music_5050112 + {0xf8c3ee3e6a9034f1}, //music_5050113 + {0x77878d37ce7af3de}, //music_5050114 + {0xc017329b350ece41}, //music_5050115 + {0xf593a4edff474f67}, //music_5050116 + {0xe1972fbb1f007ddb}, //music_5050117 + {0xfed8de6081bb79a4}, //music_5050118 + {0x4877977d05b312f3}, //music_5050119 + {0x7eaa69de94e0eff1}, //music_5050120 + {0xee1270b0ecf19fa9}, //music_5050121 + {0x194e2cf79077c167}, //music_5050122 + {0x9dbfafd78a550632}, //music_5050123 + {0xf95b5bbf7e04f6f7}, //music_5050124 + {0x72eab426f4800cdf}, //music_5050125 + {0xfbaaa063a92e3fa}, //music_5050126 + {0x1b837c9a98b7d123}, //music_5050127 + {0x94f28e181640e219}, //music_5050128 + {0x5d931d29d1432b4c}, //music_5050129 + {0xc50b5e9a5adcaae4}, //music_5050130 + {0xeb7796684409adfa}, //music_5050131 + {0x13ef11289cf31dad}, //music_5050132 + {0xd446d0ea96dfdf76}, //music_5050133 + {0x4c56ec4e341a717}, //music_5050134 + {0x82523f6386d6a38a}, //music_5050135 + {0x520868bafa84e471}, //music_5050136 + {0x58b735f1a68c9a81}, //music_5050148 + {0x5b3cb281d89019db}, //music_5050149 {0x52c250eade92393b}, //music_9010001 {0xf66e6bb5b0599b07}, //music_9010002 + {0x8582b5a60dbbf948}, //music_9010003 {0xfea0d6adff136868}, //music_9050001 + {0x19480b946279507a}, //music_9050002 // Mini 4WD Hyper Dash Grand Prix (Android) {7957824642808300098}, // 6E6FDF59AB704242 @@ -858,6 +915,7 @@ static const hcakey_info hcakey_list[] = { {0x0a5d0fc8cc5c4502}, // Sng018 {0x198ea1a17416050b}, // Sng019 {0x2aa3b8abad207a1e}, // Sng020 + {0x4ee10a3e3bb19e57}, // Sng021 {0x08ad2fe12c79bca9}, // Sng022 {0x18488992b1632ef5}, // Sng023 {0x1175edbbacc1fc18}, // Sng024 @@ -887,6 +945,12 @@ static const hcakey_info hcakey_list[] = { // Digimon ReArise (Android) {34810080072368384}, // 007BAB9559510100 + + // ANNO: Mutationem (PC) + {351185040111633400}, // 04DFA8EEEE4903F8 + + // Priconne! Grand Masters (iOS/Android) + {185705658241}, // 0000002B3CEB7781 }; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ktac.c b/Frameworks/vgmstream/vgmstream/src/meta/ktac.c index 14143a8e9..568a67a92 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ktac.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ktac.c @@ -6,8 +6,9 @@ typedef struct { int32_t loop_start; int32_t loop_end; uint32_t file_size; - +#ifdef VGM_USE_FFMPEG mp4_custom_t mp4; +#endif int type; } ktac_header_t; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/meta.h b/Frameworks/vgmstream/vgmstream/src/meta/meta.h index 4ce84eb70..3a70308e4 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/meta.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/meta.h @@ -7,6 +7,7 @@ typedef VGMSTREAM* (*init_vgmstream_t)(STREAMFILE* sf); VGMSTREAM* init_vgmstream_silence(int channels, int sample_rate, int32_t num_samples); VGMSTREAM* init_vgmstream_silence_container(int total_subsongs); +VGMSTREAM* init_vgmstream_silence_base(VGMSTREAM* vgmstream); VGMSTREAM* init_vgmstream_adx(STREAMFILE* sf); @@ -654,6 +655,7 @@ VGMSTREAM* init_vgmstream_opus_prototype(STREAMFILE* sf); VGMSTREAM* init_vgmstream_opus_opusnx(STREAMFILE* sf); VGMSTREAM* init_vgmstream_opus_nsopus(STREAMFILE* sf); VGMSTREAM* init_vgmstream_opus_sqex(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_opus_rsnd(STREAMFILE* sf); VGMSTREAM * init_vgmstream_raw_al(STREAMFILE * streamFile); @@ -976,4 +978,6 @@ VGMSTREAM* init_vgmstream_ubi_ckd_cwav(STREAMFILE* sf); VGMSTREAM* init_vgmstream_mpeg(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_sspf(STREAMFILE* sf); + #endif /*_META_H*/ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/opus.c b/Frameworks/vgmstream/vgmstream/src/meta/opus.c index d05af43ed..95e14c376 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/opus.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/opus.c @@ -511,3 +511,32 @@ VGMSTREAM* init_vgmstream_opus_sqex(STREAMFILE* sf) { fail: return NULL; } + + +/* Idea Factory(?) variation [Birushana: Ichijuu no Kaze (Switch)] */ +VGMSTREAM* init_vgmstream_opus_rsnd(STREAMFILE* sf) { + off_t offset = 0; + int num_samples = 0, loop_start = 0, loop_end = 0, loop_flag; + + /* checks */ + if (!is_id32be(0x00, sf,"RSND")) + goto fail; + if (!check_extensions(sf, "rsnd")) + goto fail; + /* 0x04: 00? (16b)*/ + /* 0x06: 00? (8b)*/ + loop_flag = read_u8(0x07, sf); + if (loop_flag) { /* not really needed as both will be 0 */ + loop_start = read_s32le(0x08, sf); + loop_end = read_s32le(0x0c, sf); + } + offset = read_u32le(0x10, sf); /* always 0x40 */ + /* 0x14: offset again? */ + /* 0x18+: null? (unknown numbers in bgm050) */ + num_samples = 0; /* not loop_end as it isn't set when looping is disabled */ + + + return init_vgmstream_opus(sf, meta_OPUS, offset, num_samples, loop_start, loop_end); +fail: + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/psb.c b/Frameworks/vgmstream/vgmstream/src/meta/psb.c index db5e2230b..5468f74ef 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/psb.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/psb.c @@ -51,7 +51,7 @@ typedef struct { int loop_flag; int32_t loop_start; int32_t loop_end; - int duration_test; + int loop_test; } psb_header_t; @@ -155,8 +155,6 @@ VGMSTREAM* init_vgmstream_psb(STREAMFILE* sf) { 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)] */ @@ -222,14 +220,25 @@ 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: goto fail; } + /* loop meaning varies, no apparent flags, seen in PCM/DSP/MSADPCM/WMAv2: + * - loop_start + loop_length [LoM (PC), Namco Museum V1 (PC), Senxin Aleste (PC)] + * - loop_start + loop_end [G-Darius (Sw)] + * (only in some cases of "loop" field so shouldn't happen to often) */ + if (psb.loop_test) { + if (psb.loop_start + psb.loop_end <= vgmstream->num_samples) { + vgmstream->loop_end_sample += psb.loop_start; + /* assumed, matches num_samples in LoM and Namco but not in Senjin Aleste (unknown in G-Darius) */ + if (vgmstream->loop_end_sample < vgmstream->num_samples) + vgmstream->loop_end_sample += 1; + } + } + strncpy(vgmstream->stream_name, psb.readable_name, STREAM_NAME_SIZE); if (!vgmstream_open_stream(vgmstream, sf, psb.stream_offset[0])) @@ -598,9 +607,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 + 1; /* assumed, matches num_samples */ - /* duration [LoM (PC), Namco Museum V1 (PC)] or standard [G-Darius (Sw)] (no apparent flags) */ - psb->duration_test = 1; + psb->loop_end = psb_node_get_result(&nsub).num; + + psb->loop_test = 1; /* loop end meaning varies*/ } } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/silence.c b/Frameworks/vgmstream/vgmstream/src/meta/silence.c index f2ca65bf5..0e1a0e548 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/silence.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/silence.c @@ -1,7 +1,7 @@ #include "meta.h" /* silent stream - mainly for engines that need them or dummy subsongs */ -VGMSTREAM* init_vgmstream_silence(int channels, int sample_rate, int32_t num_samples) { +static VGMSTREAM* init_vgmstream_silence_internal(int channels, int sample_rate, int32_t num_samples, uint32_t channel_layout) { VGMSTREAM* vgmstream = NULL; if (channels <= 0) @@ -18,6 +18,7 @@ VGMSTREAM* init_vgmstream_silence(int channels, int sample_rate, int32_t num_sam vgmstream->meta_type = meta_SILENCE; vgmstream->sample_rate = sample_rate; vgmstream->num_samples = num_samples; + vgmstream->channel_layout = channel_layout; vgmstream->coding_type = coding_SILENCE; vgmstream->layout_type = layout_none; @@ -28,11 +29,15 @@ fail: return NULL; } +VGMSTREAM* init_vgmstream_silence(int channels, int sample_rate, int32_t num_samples) { + return init_vgmstream_silence_internal(channels, sample_rate, num_samples, 0); +} + /* silent stream - for containers that have dummy streams but it's a hassle to detect/filter out */ VGMSTREAM* init_vgmstream_silence_container(int total_subsongs) { VGMSTREAM* vgmstream = NULL; - vgmstream = init_vgmstream_silence(0, 0, 0); + vgmstream = init_vgmstream_silence_internal(0, 0, 0, 0); if (!vgmstream) goto fail; vgmstream->num_streams = total_subsongs; @@ -43,3 +48,14 @@ fail: close_vgmstream(vgmstream); return NULL; } + +VGMSTREAM* init_vgmstream_silence_base(VGMSTREAM* v) { + if (!v) + return init_vgmstream_silence_internal(0, 0, 0, 0); + + return init_vgmstream_silence_internal( + v->channels, + v->sample_rate, + 0, //v->num_samples ? + v->channel_layout); +} \ No newline at end of file diff --git a/Frameworks/vgmstream/vgmstream/src/meta/sqex_sead.c b/Frameworks/vgmstream/vgmstream/src/meta/sqex_sead.c index 651b7cf35..f1e30be72 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/sqex_sead.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/sqex_sead.c @@ -79,9 +79,14 @@ VGMSTREAM* init_vgmstream_sqex_sead(STREAMFILE* sf) { goto fail; } - /* SEAD handles both sab/mab in the same lib, and other similar files (config, engine, etc). + /* SEAD handles both sab/mab in the same lib (libsead), and other similar files (config, engine, etc). * Has some chunks pointing to sections, and each section entry (usually starting with section - * version/reserved/size) is always padded to 0x10. Most values are unsigned. */ + * version/reserved/size) is always padded to 0x10. Most values are unsigned. + * + * "SEAD Engine" (Square Enix Application on Demand Engine) is/was SQEX's internal middleware (~2006), + * so it's possible SEAD refers to the whole thing rather than audio, but since .sab/mab audio lib typically goes + * with other engines it's hard to say if "libsead" is the whole engine but trimmed with only audio functions, + * or is a separate audio lib derived from this "SEAD Engine". */ sead.big_endian = guess_endianness16bit(0x06, sf); /* no flag, use size */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/sspf.c b/Frameworks/vgmstream/vgmstream/src/meta/sspf.c new file mode 100644 index 000000000..2929fa4ee --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/sspf.c @@ -0,0 +1,159 @@ +#include "meta.h" + + +static int freq_to_rate(int freq); + +/* SSPF - Konami/KCET banks [Metal Gear Solid 4 (PS3)] */ +VGMSTREAM* init_vgmstream_sspf(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + off_t start_offset; + int loop_flag, channels, sample_rate; + int32_t num_samples, loop_start; + int total_subsongs, target_subsong = sf->stream_index; + uint32_t file_size, pad_size, offset, bwav_offset, iwav_offset, wave_offset, stream_size; + uint32_t codec; + + + /* checks */ + if (!is_id32be(0x00,sf, "SSPF")) + goto fail; + if (!check_extensions(sf, "ssp")) + goto fail; + + /* extra check to ignore .spc, that are a RAM pack of .ssp with a ~0x800 table at the end */ + file_size = read_u32be(0x08, sf) + 0x08; /* without padding */ + pad_size = 0; + if (file_size % 0x800) /* add padding */ + pad_size = 0x800 - (file_size % 0x800); + if (file_size != get_streamfile_size(sf) && file_size + pad_size != get_streamfile_size(sf)) + goto fail; + /* 0x0c: "loadBank"? (always 2? MTA2 is always 1) */ + + /* read chunks (fixed order) */ + bwav_offset = read_u32be(0x04, sf) + 0x08; + if (!is_id32be(bwav_offset,sf, "BWAV")) + goto fail; + + iwav_offset = read_u32be(bwav_offset + 0x04, sf) + 0x08 + bwav_offset; + + if (!is_id32be(iwav_offset,sf, "IWAV")) + goto fail; + /* past IWAV are some more chunks then padding (variable? some are defined in debug structs only, not seen) */ + + total_subsongs = read_u32be(iwav_offset + 0x08,sf); + if (target_subsong == 0) target_subsong = 1; + if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail; + + offset = iwav_offset + 0x10 + (target_subsong - 1) * 0x20; + + /* IWAV entry supposedly contains more info but seems only offset and some ID at 0x14, rest is 0 */ + wave_offset = read_u32be(offset + 0x00,sf) + bwav_offset; + if (is_id32be(wave_offset,sf, "SSWF")) { + codec = read_u8(wave_offset + 0x04,sf); /* kType (always 0x01) */ + if (read_u8(wave_offset + 0x05,sf) != 0x01) /* nChannels? */ + goto fail; + sample_rate = read_u16be(wave_offset + 0x06,sf); /* not freq (ex. 48000 is used) */ + loop_start = read_s32be(wave_offset + 0x08,sf); + num_samples = read_s32be(wave_offset + 0x0c,sf); + + channels = 1; + loop_flag = loop_start != 0x7FFFFFFF; + start_offset = wave_offset + 0x10; + + stream_size = 0x10 + (num_samples * channels * 0x02); /* implicit */ + } + else if (is_id32be(wave_offset,sf, "SSW2")) { + stream_size = read_u32be(wave_offset + 0x04,sf); + /* 08 version? (always 0) */ + num_samples = read_s32be(wave_offset + 0x0c,sf); + codec = read_u32be(wave_offset + 0x10,sf); /* kType (always 0x21) */ + if (read_u32be(wave_offset + 0x10,sf) != 0x21) + goto fail; + if (read_u8(wave_offset + 0x14,sf) != 0x08) /* nBlocks? */ + goto fail; + if (read_u8(wave_offset + 0x15,sf) != 0x01) /* nChannels? */ + goto fail; + + channels = 1; + sample_rate = freq_to_rate(read_u16be(wave_offset + 0x16,sf)); /* freq value */ + loop_start = read_s32be(wave_offset + 0x18,sf); + /* 0x1c: lpStartAddr (0xFFFFFFFF is none) */ + + loop_flag = loop_start != 0x7FFFFFFF; + start_offset = wave_offset + 0x20; + } + else { + vgm_logi("SSPF: unknown variant at %x\n", wave_offset); + goto fail; + } + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channels, loop_flag); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_SSPF; + vgmstream->sample_rate = sample_rate; + vgmstream->num_samples = num_samples; + vgmstream->loop_start_sample = loop_start; + vgmstream->loop_end_sample = num_samples; + + vgmstream->num_streams = total_subsongs; + vgmstream->stream_size = stream_size; + + switch (codec) { + case 0x01: + vgmstream->coding_type = coding_PCM16BE; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x02; + break; + + case 0x21: + vgmstream->coding_type = coding_MTA2; + vgmstream->codec_config = 1; + vgmstream->layout_type = layout_none; + break; + + default: + vgm_logi("SSPF: unknown codec %x\n", codec); + goto fail; + } + + if (!vgmstream_open_stream(vgmstream, sf, start_offset)) + goto fail; + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} + +/* transforms internal freq to sample rate */ +static int freq_to_rate(int freq) { + /* from PowerPC code seems like it's trying something like this, but not quite (PPC is complex): + if ((freq & 0xFF) != 0) + return powf(10.0, 0.0117647 * (freq & 0xFF))) * 20.0; + return powf(10.0, 0.0117647 * 2048)) * 20.0; //??? + */ + + //TODO improve, for now fake it + switch(freq) { + case 0x9000: return 24000; /* most voices, sounds right */ + case 0xA200: return 48000; /* most sfx */ + /* rest is rarely used for some sfx, so it's hard to guess actual frequency and this is just approximate */ + case 0x9fcd: return 44100; + case 0x9c9c: return 39000; + case 0x9b79: return 38000; + case 0x9b13: return 37000; + case 0x9a88: return 36000; + case 0x9778: return 32000; + case 0x9401: return 28000; + case 0x8578: return 16000; + case 0x7e00: return 11050; + default: + VGM_LOG("SSPF: unknown freq %x\n", freq); + break; + } + + return freq; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/txth.c b/Frameworks/vgmstream/vgmstream/src/meta/txth.c index 8f746f00a..e23548413 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/txth.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/txth.c @@ -2,8 +2,11 @@ #include "../coding/coding.h" #include "../layout/layout.h" #include "txth_streamfile.h" +#include "../util/text_reader.h" -#define TXT_LINE_MAX 0x2000 +#define TXT_LINE_MAX 2048 /* probably ~1000 would be ok */ +#define TXT_LINE_KEY_MAX 128 +#define TXT_LINE_VAL_MAX (TXT_LINE_MAX - TXT_LINE_KEY_MAX) /* known TXTH types */ typedef enum { @@ -862,7 +865,7 @@ static int get_padding_size(txth_header* txth, int discard_empty); /* Simple text parser of "key = value" lines. * The code is meh and error handling not exactly the best. */ static int parse_txth(txth_header* txth) { - off_t txt_offset, file_size; + uint32_t txt_offset; /* setup txth defaults */ if (txth->sf_body) @@ -872,23 +875,28 @@ static int parse_txth(txth_header* txth) { txt_offset = read_bom(txth->sf_text); - file_size = get_streamfile_size(txth->sf_text); /* read lines */ { - char line[TXT_LINE_MAX]; - char key[TXT_LINE_MAX]; - char val[TXT_LINE_MAX]; - /* at least as big as a line to avoid overflows (I hope) */ + text_reader_t tr; + uint8_t buf[TXT_LINE_MAX + 1]; + char key[TXT_LINE_KEY_MAX]; + char val[TXT_LINE_VAL_MAX]; + int ok, line_len; + char* line; - while (txt_offset < file_size) { - int ok, bytes_read, line_ok; + if (!text_reader_init(&tr, buf, sizeof(buf), txth->sf_text, txt_offset, 0)) + goto fail; - bytes_read = read_line(line, sizeof(line), txt_offset, txth->sf_text, &line_ok); - if (!line_ok) goto fail; - //;VGM_LOG("TXTH: line=%s\n",line); + do { + line_len = text_reader_get_line(&tr, &line); + if (line_len < 0) goto fail; /* too big for buf (maybe not text)) */ - txt_offset += bytes_read; + if (line == NULL) /* EOF */ + break; + + if (line_len == 0) /* empty */ + continue; /* get key/val (ignores lead spaces, stops at space/comment/separator) */ ok = sscanf(line, " %[^ \t#=] = %[^\t#\r\n] ", key,val); @@ -897,7 +905,8 @@ static int parse_txth(txth_header* txth) { if (!parse_keyval(txth->sf, txth, key, val)) /* read key/val */ goto fail; - } + + } while (line_len >= 0); } if (!txth->loop_flag_set) diff --git a/Frameworks/vgmstream/vgmstream/src/meta/txtp.c b/Frameworks/vgmstream/vgmstream/src/meta/txtp.c index 450d8d076..72df65d74 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/txtp.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/txtp.c @@ -3,11 +3,14 @@ #include "../layout/layout.h" #include "../mixing.h" #include "../plugins.h" +#include "../util/text_reader.h" #include -#define TXTP_LINE_MAX 1024 +#define TXT_LINE_MAX 2048 /* some wwise .txtp get wordy */ +#define TXT_LINE_KEY_MAX 128 +#define TXT_LINE_VAL_MAX (TXT_LINE_MAX - TXT_LINE_KEY_MAX) #define TXTP_MIXING_MAX 512 #define TXTP_GROUP_MODE_SEGMENTED 'S' #define TXTP_GROUP_MODE_LAYERED 'L' @@ -68,7 +71,7 @@ typedef struct { typedef struct { /* main entry */ - char filename[TXTP_LINE_MAX]; + char filename[TXT_LINE_MAX]; int silent; /* TXTP settings (applied at the end) */ @@ -218,15 +221,12 @@ static void clean_txtp(txtp_header* txtp, int fail) { static int parse_silents(txtp_header* txtp) { int i; - int channels = 0; - int sample_rate = 0; - int32_t num_samples = 0; + VGMSTREAM* v_base = NULL; /* silents use same channels as close files */ for (i = 0; i < txtp->vgmstream_count; i++) { if (!txtp->entry[i].silent) { - channels = txtp->vgmstream[i]->channels; - sample_rate = txtp->vgmstream[i]->sample_rate; + v_base = txtp->vgmstream[i]; break; } } @@ -236,7 +236,7 @@ static int parse_silents(txtp_header* txtp) { if (!txtp->entry[i].silent) continue; - txtp->vgmstream[i] = init_vgmstream_silence(channels, sample_rate, num_samples); + txtp->vgmstream[i] = init_vgmstream_silence_base(v_base); if (!txtp->vgmstream[i]) goto fail; apply_settings(txtp->vgmstream[i], &txtp->entry[i]); @@ -1274,7 +1274,7 @@ static inline int is_match(const char* str1, const char* str2) { static void parse_params(txtp_entry* entry, char* params) { /* parse params: #(commands) */ int n, nc, nm, mc; - char command[TXTP_LINE_MAX]; + char command[TXT_LINE_MAX]; play_config_t* tcfg = &entry->config; entry->range_start = 0; @@ -1805,7 +1805,7 @@ fail: static int is_substring(const char* val, const char* cmp) { int n; - char subval[TXTP_LINE_MAX]; + char subval[TXT_LINE_MAX]; /* read string without trailing spaces or comments/commands */ if (sscanf(val, " %s%n[^ #\t\r\n]%n", subval, &n, &n) != 1) @@ -1865,12 +1865,12 @@ static int parse_keyval(txtp_header* txtp, const char* key, const char* val) { } } else if (0==strcmp(key,"commands")) { - char val2[TXTP_LINE_MAX]; + char val2[TXT_LINE_MAX]; strcpy(val2, val); /* copy since val is modified here but probably not important */ if (!add_entry(txtp, val2, 1)) goto fail; } else if (0==strcmp(key,"group")) { - char val2[TXTP_LINE_MAX]; + char val2[TXT_LINE_MAX]; strcpy(val2, val); /* copy since val is modified here but probably not important */ if (!add_group(txtp, val2)) goto fail; @@ -1887,7 +1887,7 @@ fail: static txtp_header* parse_txtp(STREAMFILE* sf) { txtp_header* txtp = NULL; - off_t txt_offset, file_size; + uint32_t txt_offset; txtp = calloc(1,sizeof(txtp_header)); @@ -1897,23 +1897,28 @@ static txtp_header* parse_txtp(STREAMFILE* sf) { txtp->is_segmented = 1; txt_offset = read_bom(sf); - file_size = get_streamfile_size(sf); /* read and parse lines */ { - char line[TXTP_LINE_MAX]; - char key[TXTP_LINE_MAX]; - char val[TXTP_LINE_MAX]; - char filename[TXTP_LINE_MAX]; - /* at least as big as a line to avoid overflows (I hope) */ + text_reader_t tr; + uint8_t buf[TXT_LINE_MAX + 1]; + char key[TXT_LINE_KEY_MAX]; + char val[TXT_LINE_VAL_MAX]; + int ok, line_len; + char* line; - while (txt_offset < file_size) { - int ok, bytes_read, line_ok; + if (!text_reader_init(&tr, buf, sizeof(buf), sf, txt_offset, 0)) + goto fail; - bytes_read = read_line(line, sizeof(line), txt_offset, sf, &line_ok); - if (!line_ok) goto fail; + do { + line_len = text_reader_get_line(&tr, &line); + if (line_len < 0) goto fail; /* too big for buf (maybe not text)) */ - txt_offset += bytes_read; + if (line == NULL) /* EOF */ + break; + + if (line_len == 0) /* empty */ + continue; /* try key/val (ignores lead/trail spaces, # may be commands or comments) */ ok = sscanf(line, " %[^ \t#=] = %[^\t\r\n] ", key,val); @@ -1924,16 +1929,17 @@ static txtp_header* parse_txtp(STREAMFILE* sf) { } /* must be a filename (only remove spaces from start/end, as filenames con contain mid spaces/#/etc) */ - ok = sscanf(line, " %[^\t\r\n] ", filename); + ok = sscanf(line, " %[^\t\r\n] ", val); if (ok != 1) /* not a filename either */ continue; - if (filename[0] == '#') + if (val[0] == '#') continue; /* simple comment */ /* filename with settings */ - if (!add_entry(txtp, filename, 0)) + if (!add_entry(txtp, val, 0)) goto fail; - } + + } while (line_len >= 0); } /* mini-txth: if no entries are set try with filename, ex. from "song.ext#3.txtp" use "song.ext#3" diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ubi_hx.c b/Frameworks/vgmstream/vgmstream/src/meta/ubi_hx.c index e43379a33..3b80d4b8b 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ubi_hx.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ubi_hx.c @@ -4,11 +4,12 @@ #include "../util/endianness.h" -typedef enum { PCM, UBI, PSX, DSP, XIMA, ATRAC3, XMA2, MP3 } ubi_hx_codec; +typedef enum { PCM, UBI, PSX, DSP, XIMA, ATRAC3, XMA2, MP3, SILENCE } ubi_hx_codec; typedef struct { int big_endian; int total_subsongs; + int is_riff; int codec_id; ubi_hx_codec codec; /* unified codec */ @@ -68,7 +69,7 @@ VGMSTREAM* init_vgmstream_ubi_hx(STREAMFILE* sf) { * Game seems to play files by calling linked ids: EventResData (play/stop/etc) > Random/Program/Wav ResData (1..N refs) > FileIdObj */ /* HX HEADER */ - hx.big_endian = guess_endianness32bit(0x00, sf); + hx.big_endian = guess_endian32(0x00, sf); if (!parse_hx(&hx, sf, target_subsong)) goto fail; @@ -140,13 +141,19 @@ fail: static int parse_name(ubi_hx_header* hx, STREAMFILE* sf) { read_u32_t read_u32 = hx->big_endian ? read_u32be : read_u32le; read_s32_t read_s32 = hx->big_endian ? read_s32be : read_s32le; - off_t index_offset, offset; + uint32_t index_type, index_offset, offset; int i, index_entries; char class_name[255]; index_offset = read_u32(0x00, sf); + index_type = read_u32(index_offset + 0x04, sf); index_entries = read_s32(index_offset + 0x08, sf); + + /* doesn't seem to have names (no way to link) */ + if (index_type == 0x01) + return 1; + offset = index_offset + 0x0c; for (i = 0; i < index_entries; i++) { off_t header_offset; @@ -169,29 +176,34 @@ static int parse_name(ubi_hx_header* hx, STREAMFILE* sf) { //unknown_count = read_s32(offset + 0x00, sf); offset += 0x04; - link_count = read_s32(offset + 0x00, sf); - offset += 0x04; - for (j = 0; j < link_count; j++) { - uint32_t link_id1 = read_u32(offset + 0x00, sf); - uint32_t link_id2 = read_u32(offset + 0x04, sf); - - if (link_id1 == hx->cuuid1 && link_id2 == hx->cuuid2) { - is_found = 1; - } - offset += 0x08; + if (index_type == 0x01) { + goto fail; } + else { + link_count = read_s32(offset + 0x00, sf); + offset += 0x04; + for (j = 0; j < link_count; j++) { + uint32_t link_id1 = read_u32(offset + 0x00, sf); + uint32_t link_id2 = read_u32(offset + 0x04, sf); - language_count = read_s32(offset + 0x00, sf); - offset += 0x04; - for (j = 0; j < language_count; j++) { - uint32_t link_id1 = read_u32(offset + 0x08, sf); - uint32_t link_id2 = read_u32(offset + 0x0c, sf); - - if (link_id1 == hx->cuuid1 && link_id2 == hx->cuuid2) { - is_found = 1; + if (link_id1 == hx->cuuid1 && link_id2 == hx->cuuid2) { + is_found = 1; + } + offset += 0x08; } - offset += 0x10; + language_count = read_s32(offset + 0x00, sf); + offset += 0x04; + for (j = 0; j < language_count; j++) { + uint32_t link_id1 = read_u32(offset + 0x08, sf); + uint32_t link_id2 = read_u32(offset + 0x0c, sf); + + if (link_id1 == hx->cuuid1 && link_id2 == hx->cuuid2) { + is_found = 1; + } + + offset += 0x10; + } } /* identify all possible names so unknown platforms fail */ @@ -228,6 +240,7 @@ static int parse_name(ubi_hx_header* hx, STREAMFILE* sf) { } fail: + vgm_logi("UBI HX: error parsing name at %x (report)\n", index_offset); return 0; } @@ -265,11 +278,14 @@ static int parse_header(ubi_hx_header* hx, STREAMFILE* sf, uint32_t offset, uint uint32_t flag_type = read_u32(offset + 0x00, sf); if (flag_type == 0x01 || flag_type == 0x02) { /* Rayman Arena */ - uint32_t unk_value = read_u32(offset + 0x04, sf); - if (unk_value != 0x00 && /* common */ - unk_value != 0xbe570a3d && /* Largo Winch: Empire Under Threat (PC)-most */ - unk_value != 0xbf8e147b) /* Largo Winch: Empire Under Threat (PC)-few */ + uint32_t unk_value = read_u32(offset + 0x04, sf); /* float? */ + if (unk_value != 0x00 && /* common */ + unk_value != 0xbe570a3d && /* Largo Winch: Empire Under Threat (PC)-most */ + unk_value != 0xbf8e147b) { /* Largo Winch: Empire Under Threat (PC)-few */ + VGM_LOG("ubi hx: unknown flag\n"); goto fail; + } + hx->stream_mode = read_u32(offset + 0x08, sf); /* flag: 0=internal, 1=external */ /* 0x0c: flag: 0=static, 1=stream */ offset += 0x10; @@ -279,7 +295,8 @@ static int parse_header(ubi_hx_header* hx, STREAMFILE* sf, uint32_t offset, uint offset += 0x08; if (strcmp(hx->class_name, "CGCWaveFileIdObj") == 0) { - if (read_u32(offset + 0x00, sf) != read_u32(offset + 0x04, sf)) goto fail; /* meaning? */ + if (read_u32(offset + 0x00, sf) != read_u32(offset + 0x04, sf)) + goto fail; /* meaning? */ hx->stream_mode = read_u32(offset + 0x04, sf); offset += 0x08; } @@ -302,6 +319,7 @@ static int parse_header(ubi_hx_header* hx, STREAMFILE* sf, uint32_t offset, uint //todo probably a flag: &1=external, &2=stream, &8=has adjust (XIII), &4=??? (XIII PS2, small, mono) switch(hx->stream_mode) { case 0x00: /* memory (internal file) */ + case 0x02: /* same (no diffs in size/channels/etc?) [Rayman 3 demo (PC)] */ riff_offset = offset; riff_size = read_u32(riff_offset + 0x04, sf) + 0x08; break; @@ -321,13 +339,17 @@ static int parse_header(ubi_hx_header* hx, STREAMFILE* sf, uint32_t offset, uint break; default: - VGM_LOG("ubi hx: %x\n", hx->stream_mode); + VGM_LOG("ubi hx: unknown stream mode %x\n", hx->stream_mode); goto fail; } /* parse pseudo-RIFF "fmt" */ - if (read_u32(riff_offset, sf) != 0x46464952) /* "RIFF" in machine endianness */ + if (read_u32(riff_offset, sf) != 0x46464952) { /* "RIFF" in machine endianness */ + VGM_LOG("ubi hx: unknown RIFF\n"); goto fail; + } + + hx->is_riff = 1; hx->codec_id = read_u16(riff_offset + 0x14 , sf); switch(hx->codec_id) { @@ -357,12 +379,15 @@ static int parse_header(ubi_hx_header* hx, STREAMFILE* sf, uint32_t offset, uint hx->stream_offset = read_u32(chunk_offset + 0x00, sf) + stream_adjust; } else { + VGM_LOG("ubi hx: unknown chunk\n"); goto fail; } } else { - if (!find_chunk_riff_ve(sf, 0x61746164,riff_offset + 0x0c,riff_size - 0x0c, &chunk_offset,&chunk_size, hx->big_endian)) + if (!find_chunk_riff_ve(sf, 0x61746164,riff_offset + 0x0c,riff_size - 0x0c, &chunk_offset,&chunk_size, hx->big_endian)) { + VGM_LOG("ubi hx: unknown chunk RIFF\n"); goto fail; + } hx->stream_offset = chunk_offset; if (chunk_size > riff_size - (chunk_offset - riff_offset) || !chunk_size) @@ -384,7 +409,11 @@ static int parse_header(ubi_hx_header* hx, STREAMFILE* sf, uint32_t offset, uint //todo some dummy files have 0 size - if (read_u32(offset + 0x00, sf) != 0x01) goto fail; + if (read_u32(offset + 0x00, sf) != 0x01) { + VGM_LOG("ubi hx: unknown flag non 0x01\n"); + goto fail; + } + /* 0x04: some kind of parent id shared by multiple Waves, or 0 */ offset += 0x08; @@ -400,7 +429,9 @@ static int parse_header(ubi_hx_header* hx, STREAMFILE* sf, uint32_t offset, uint switch(hx->channels) { case 0x48: hx->channels = 1; break; case 0x90: hx->channels = 2; break; - default: goto fail; + default: + VGM_LOG("ubi hx: channel type %x\n", hx->channels); + goto fail; } hx->sample_rate = (read_u16(offset + 0x02, sf) & 0x7FFFu) << 1u; /* ??? */ cue_flag = read_u8(offset + 0x03, sf) & (1 << 7); @@ -461,6 +492,7 @@ static int parse_header(ubi_hx_header* hx, STREAMFILE* sf, uint32_t offset, uint } } else { + VGM_LOG("ubi hx: unknown type\n"); goto fail; } @@ -478,13 +510,21 @@ static int parse_hx(ubi_hx_header* hx, STREAMFILE* sf, int target_subsong) { uint32_t index_offset, offset; int i, index_entries; char class_name[255]; + uint32_t index_type; index_offset = read_u32(0x00, sf); - if (read_u32(index_offset + 0x00, sf) != get_id32be("XDNI")) /* (INDX in given endianness) */ + if (read_u32(index_offset + 0x00, sf) != get_id32be("XDNI")) { /* (INDX in given endianness) */ + VGM_LOG("ubi hx: unknown index\n"); goto fail; - if (read_u32(index_offset + 0x04, sf) != 0x02) /* type? */ + } + + /* usually 0x02, rarely 0x01 [Rayman M demo (PS2)] */ + index_type = read_u32(index_offset + 0x04, sf); + if (index_type != 0x01 && index_type != 0x02) { + VGM_LOG("ubi hx: unknown index type\n"); goto fail; + } if (target_subsong == 0) target_subsong = 1; @@ -517,23 +557,29 @@ static int parse_hx(ubi_hx_header* hx, STREAMFILE* sf, int target_subsong) { } offset += 0x04; - /* ids that this object directly points to (ex. Event > Random) */ - link_count = read_s32(offset + 0x00, sf); - offset += 0x04 + 0x08 * link_count; + if (index_type == 0x01) { + link_count = 0; + language_count = 0; + } + else { + /* ids that this object directly points to (ex. Event > Random) */ + link_count = read_s32(offset + 0x00, sf); + offset += 0x04 + 0x08 * link_count; - /* localized id list of WavRes (can use this list instead of the prev one) */ - language_count = read_s32(offset + 0x00, sf); - offset += 0x04; - for (j = 0; j < language_count; j++) { - /* 0x00: lang code, in reverse endianness: "en ", "fr ", etc */ - /* 0x04: possibly count of ids for this lang */ - /* 0x08: id1+2 */ + /* localized id list of WavRes (can use this list instead of the prev one) */ + language_count = read_s32(offset + 0x00, sf); + offset += 0x04; + for (j = 0; j < language_count; j++) { + /* 0x00: lang code, in reverse endianness: "en ", "fr ", etc */ + /* 0x04: possibly count of ids for this lang */ + /* 0x08: id1+2 */ - if (read_u32(offset + 0x04, sf) != 1) { - VGM_LOG("ubi hx: wrong lang count near %x\n", offset); - goto fail; /* WavRes doesn't have this field */ + if (read_u32(offset + 0x04, sf) != 1) { + VGM_LOG("ubi hx: wrong lang count near %x\n", offset); + goto fail; /* WavRes doesn't have this field */ + } + offset += 0x10; } - offset += 0x10; } //todo figure out CProgramResData sequences @@ -571,6 +617,7 @@ static int parse_hx(ubi_hx_header* hx, STREAMFILE* sf, int target_subsong) { goto fail; } + /* should only exist on non-wave objects (like CProgramResData) */ if (link_count != 0) { vgm_logi("UBI HX: found links in wav object (report)\n"); goto fail; @@ -632,6 +679,12 @@ static VGMSTREAM* init_vgmstream_ubi_hx_header(ubi_hx_header* hx, STREAMFILE* sf sb = sf; } + /* very rarely a game uses Ubi ADPCM, but data is empty and has missing header [Rayman 3 demo 3 (PC) fixe.hxc#84] */ + if (hx->is_riff && hx->codec == UBI) { //todo improve + if (read_u32le(hx->stream_offset, sb) == 0x02) { + hx->codec = SILENCE; + } + } /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(hx->channels, hx->loop_flag); @@ -658,6 +711,12 @@ static VGMSTREAM* init_vgmstream_ubi_hx_header(ubi_hx_header* hx, STREAMFILE* sf vgmstream->layout_type = layout_none; vgmstream->num_samples = ubi_adpcm_get_samples(vgmstream->codec_data); + + /* some kind of internal bug I guess, seen in a few subsongs in Rayman 3 PC demo, other values are also buggy */ + if (vgmstream->num_samples == 0x77E7A374) { + vgmstream->num_samples = ubi_adpcm_bytes_to_samples(vgmstream->codec_data, hx->stream_size); + } + /* XIII has 6-bit stereo music, Rayman 3 4-bit music, both use 6-bit mono) */ break; @@ -745,6 +804,13 @@ static VGMSTREAM* init_vgmstream_ubi_hx_header(ubi_hx_header* hx, STREAMFILE* sf break; } #endif + + case SILENCE: /* special hack */ + vgmstream->coding_type = coding_SILENCE; + vgmstream->layout_type = layout_none; + + vgmstream->num_samples = ps_bytes_to_samples(hx->stream_size, hx->channels); + break; default: goto fail; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/xa.c b/Frameworks/vgmstream/vgmstream/src/meta/xa.c index e4645bb95..3bd7745e4 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/xa.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/xa.c @@ -244,7 +244,7 @@ static int xa_read_subsongs(STREAMFILE* sf, int target_subsong, off_t start, uin is_audio = !(xa_submode & 0x08) && (xa_submode & 0x04) && !(xa_submode & 0x02); is_eof = (xa_submode & 0x80); - VGM_ASSERT((xa_submode & 0x01), "XA: end of audio at %lx\n", offset); /* used? */ + VGM_ASSERT((xa_submode & 0x01), "XA: end of audio at %lx\n", offset); /* rare, signals last sector [Tetris (CD-i)] */ //;VGM_ASSERT(is_eof, "XA: eof at %lx\n", offset); //;VGM_ASSERT(!is_audio, "XA: not audio at %lx\n", offset); diff --git a/Frameworks/vgmstream/vgmstream/src/mixing.c b/Frameworks/vgmstream/vgmstream/src/mixing.c index e357a703c..73717bffe 100644 --- a/Frameworks/vgmstream/vgmstream/src/mixing.c +++ b/Frameworks/vgmstream/vgmstream/src/mixing.c @@ -1283,7 +1283,57 @@ void mixing_macro_downmix(VGMSTREAM* vgmstream, int max /*, mapping_t output_map /* ******************************************************************* */ -void mixing_setup(VGMSTREAM * vgmstream, int32_t max_sample_count) { +static int fix_layered_channel_layout(VGMSTREAM* vgmstream) { + int i; + mixing_data* data = vgmstream->mixing_data; + layered_layout_data* layout_data; + uint32_t prev_cl; + + if (vgmstream->channel_layout || vgmstream->layout_type != layout_layered) + return 0; + + layout_data = vgmstream->layout_data; + + /* mainly layer-v (in cases of layers-within-layers should cascade) */ + if (data->output_channels != layout_data->layers[0]->channels) + return 0; + + /* check all layers share layout (implicitly works as a channel check, if not 0) */ + prev_cl = layout_data->layers[0]->channel_layout; + if (prev_cl == 0) + return 0; + + for (i = 1; i < layout_data->layer_count; i++) { + uint32_t layer_cl = layout_data->layers[i]->channel_layout; + if (prev_cl != layer_cl) + return 0; + + prev_cl = layer_cl; + } + + vgmstream->channel_layout = prev_cl; + return 1; +} + +/* channel layout + down/upmixing = ?, salvage what we can */ +static void fix_channel_layout(VGMSTREAM* vgmstream) { + mixing_data* data = vgmstream->mixing_data; + + if (fix_layered_channel_layout(vgmstream)) + goto done; + + /* segments should share channel layout automatically */ + + /* a bit wonky but eh... */ + if (vgmstream->channel_layout && vgmstream->channels != data->output_channels) { + vgmstream->channel_layout = 0; + } + +done: + ((VGMSTREAM*)vgmstream->start_vgmstream)->channel_layout = vgmstream->channel_layout; +} + +void mixing_setup(VGMSTREAM* vgmstream, int32_t max_sample_count) { mixing_data *data = vgmstream->mixing_data; float *mixbuf_re = NULL; @@ -1300,11 +1350,7 @@ void mixing_setup(VGMSTREAM * vgmstream, int32_t max_sample_count) { data->mixbuf = mixbuf_re; data->mixing_on = 1; - /* a bit wonky but eh... */ - if (vgmstream->channel_layout && vgmstream->channels != data->output_channels) { - vgmstream->channel_layout = 0; - ((VGMSTREAM*)vgmstream->start_vgmstream)->channel_layout = 0; - } + fix_channel_layout(vgmstream); /* since data exists on its own memory and pointer is already set * there is no need to propagate to start_vgmstream */ diff --git a/Frameworks/vgmstream/vgmstream/src/util/text_reader.c b/Frameworks/vgmstream/vgmstream/src/util/text_reader.c new file mode 100644 index 000000000..4d8db192e --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/util/text_reader.c @@ -0,0 +1,187 @@ +#include +#include "text_reader.h" +#include "log.h" + + +/* convenience function to init the above struct */ +int text_reader_init(text_reader_t* tr, uint8_t* buf, int buf_size, STREAMFILE* sf, uint32_t offset, uint32_t max) { + memset(tr, 0, sizeof(text_reader_t)); + + if (buf_size <= 1 || !buf || !sf) + return 0; + + tr->buf = buf; + tr->buf_size = buf_size; + tr->sf = sf; + tr->offset = offset; + + if (!max) + max = get_streamfile_size(sf) - offset; + tr->max_offset = max; + + return 1; +} + + +/* reads more data into buf and adjust values */ +static void prepare_buf(text_reader_t* tr) { + + /* since we may read N lines in the same buffer, move starting pos each call */ + tr->pos = tr->next_pos; + + /* not more data (but may still read lines so not an error) */ + if (tr->offset >= tr->max_offset) { + return; + } + + /* request more data */ + if (tr->pos >= tr->filled) { + tr->pos = 0; + tr->filled = 0; + } + + /* partially filled, move buffer */ + if (tr->pos > 0) { + int move_size = tr->filled - tr->pos; + + memmove(tr->buf, &tr->buf[tr->pos], move_size); /* memmove = may overlap */ + tr->filled -= tr->pos; /* now less filled */ + tr->pos = 0; + } + + /* has enough data */ + if (tr->filled >= tr->buf_size) { + return; + } + + /* read buf up to max */ + { + int bytes; + int read_size = tr->buf_size - tr->filled; + if (read_size + tr->offset > tr->max_offset) + read_size = tr->max_offset - tr->offset; + + if (read_size <= 0) { /* ??? */ + bytes = 0; + } + else { + if (tr->filled + read_size >= tr->buf_size) + read_size -= 1; /* always leave an extra byte for c-string null */ + + bytes = read_streamfile(tr->buf + tr->filled, tr->offset, read_size, tr->sf); + tr->offset += bytes; + tr->filled += bytes; + } + + /* maybe some internal issue, force EOF */ + if (bytes == 0) { + tr->offset = tr->max_offset; + } + + /* ensure no old data is used as valid (simplifies some checks during parse) */ + tr->buf[tr->filled] = '\0'; + } +} + +static void parse_buf(text_reader_t* tr) { + int i; + + tr->line = (char*)&tr->buf[tr->pos]; + tr->line_len = 0; + tr->line_ok = 0; + + /* detect EOF (this should only happen if no more data was loaded) */ + if (tr->pos == tr->filled) { + tr->line = NULL; + tr->line_ok = 1; + tr->line_len = 0; + return; + } + + /* assumes filled doesn't reach buf_size (to allow trailing \0 after filled) */ + for (i = tr->pos; i < tr->filled; i++) { + char c = (char)tr->buf[i]; + + if (c == '\0') { + i++; + break; /* not a valid file? (line_ok=0) */ + } + + if (c == '\r' && tr->buf[i+1] == '\n') { /* CRLF (0x0d0a) */ + /* i+1 may read past filled but it's pre-set to \0 */ + i += 2; //todo check that i < buf_size-1 + tr->line_ok = 1; + break; + } + else if (c == '\n') { /* LF (0x0a) */ + i++; + tr->line_ok = 1; + break; + } + else if (c == '\r') { /* CR (0x0d) */ + i++; + tr->line_ok = (i < tr->buf_size - 1); + /* if buf ends with a CR, next buf may start be a LF (single CRLF), so line is not ok near buf end + * (old Macs use single \r as lines, but using only that and reaching buf end should happen rarely) */ + break; + } + + tr->line_len++; + } + + /* when lines are small may read up to filled smaller than buf, with no more data */ + if (!tr->line_ok && i == tr->filled) + tr->line_ok = (tr->filled < tr->buf_size - 1); + + /* added after proper line (a \n) or after buf end, so we aren't changing valid data */ + tr->buf[tr->pos + tr->line_len] = '\0'; + tr->next_pos = i; +} + +int text_reader_get_line(text_reader_t* tr, char** p_line) { + + if (!tr->buf) /* no init */ + return 0; + + /* how it works: + * - fills buffer up to max or buf_len, from pos 0 + * - counts from 0 to next '\n' or EOF + * - nulls \n or after EOF to make a proper c-string + * - returns from string from pos 0 to len + * - on next call rather than re-reading continues from pos N (after \n) + * - a buf will likely contain multiple lines + * - if read chars reach buf_end (no proper line found): + * - pos = 0: buf isn't big enough, error + * - pos > 0: move data to pos=0, fill rest of buf, fill rest of buf + * + * ex. + * - parse buf: read chunk full [aaaaa\nbbbb] (pos = 0) + * - get line: returns "aaaaa\0" (next_pos points to first 'b') + * - get line: from 'b', but reaches buf end before \n or EOF: must readjust + * - parse buf: move chunk part [bbbb*******] ('b' to beginning, * is garbage) + * - parse buf: read chunk part [bbbbbb\ncc_] (reaches EOF) + * - get line: returns "bbbbbb\0" (pos points to first c) + * - get line: returns "cc\0" + * - get line: returns NULL (reached EOF, no more bytes) + * - (there is an implicit \0 reserved in buf) + * + * ex. + * - start: read chunk [aaaaaaaaaaa] + * - get line: reaches buf end, but didn't reach EOF nor \n: error, can't store line + */ + + prepare_buf(tr); /* may not do anything */ + parse_buf(tr); /* next line */ + + /* if we are reading a partial line there may be more data */ + if (!tr->line_ok && tr->pos > 0) { + prepare_buf(tr); + parse_buf(tr); /* could continue from prev parse but makes logic more complex for little gain */ + } + + /* always output line even if truncated */ + if (p_line) *p_line = tr->line; + return !tr->line_ok ? + -(tr->line_len + 1) : /* -0 also is possible, force -1 */ + tr->line_len; +} diff --git a/Frameworks/vgmstream/vgmstream/src/util/text_reader.h b/Frameworks/vgmstream/vgmstream/src/util/text_reader.h new file mode 100644 index 000000000..93dc51ab2 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/util/text_reader.h @@ -0,0 +1,43 @@ +#ifndef _TEXT_READER_H_ +#define _TEXT_READER_H_ + + +/* Reader tuned for whole text files, reading chunks to minimize I/O with a single buffer. + * For short lines read_line may be more appropriate (reads up to line end, while this reads bigger chunks), + * which also allow \0 (this reader returns an error). + * NOTE: modifies passed buffer (lines are forced to end with \0 rather than \n). + * + * Usage: set text_reader_t and defaults with text_reader_init, call text_reader_get_line(...) to get lines. + * buf may be size+1 to allow 2^N chunk reads + trailing \0 (better performance?). + */ + +#include "../streamfile.h" + +typedef struct { + /* init */ + uint8_t* buf; /* where data will be read */ + int buf_size; /* size of the struct (also max line size) */ + STREAMFILE* sf; /* used to read data */ + uint32_t offset; /* sf pos */ + uint32_t max_offset; /* sf max */ + + /* internal */ + int filled; /* current buf bytes */ + int pos; /* current buf pos (last line) */ + int next_pos; /* buf pos on next call, after line end */ + int line_ok; /* current line is fully correct */ + + char* line; + int line_len; +} text_reader_t; + + +/* convenience function to init the above struct */ +int text_reader_init(text_reader_t* tr, uint8_t* buf, int buf_size, STREAMFILE* sf, uint32_t offset, uint32_t max); + +/* Reads and sets next line, or NULL if no lines are found (EOF). + * returns line length (0 for empty lines), or <0 if line was too long to store in buf. + * Will always return a valid (null terminated) string. */ +int text_reader_get_line(text_reader_t* tr, char** p_line); + +#endif diff --git a/Frameworks/vgmstream/vgmstream/src/vgmstream.c b/Frameworks/vgmstream/vgmstream/src/vgmstream.c index faa3b1dbe..3e0b9b955 100644 --- a/Frameworks/vgmstream/vgmstream/src/vgmstream.c +++ b/Frameworks/vgmstream/vgmstream/src/vgmstream.c @@ -519,6 +519,8 @@ VGMSTREAM* (*init_vgmstream_functions[])(STREAMFILE* sf) = { init_vgmstream_wbk_nslb, init_vgmstream_dsp_apex, init_vgmstream_ubi_ckd_cwav, + init_vgmstream_sspf, + init_vgmstream_opus_rsnd, /* lower priority metas (no clean header identity, somewhat ambiguous, or need extension/companion file to identify) */ init_vgmstream_mpeg, diff --git a/Frameworks/vgmstream/vgmstream/src/vgmstream.h b/Frameworks/vgmstream/vgmstream/src/vgmstream.h index 6b16ca2f6..61086fab3 100644 --- a/Frameworks/vgmstream/vgmstream/src/vgmstream.h +++ b/Frameworks/vgmstream/vgmstream/src/vgmstream.h @@ -763,6 +763,7 @@ typedef enum { meta_WBK_NSLB, meta_DSP_APEX, meta_MPEG, + meta_SSPF, } meta_t;