Updated VGMStream to r1776-16-g7e4f5dc6

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
main
Christopher Snowhill 2022-08-21 16:17:51 -07:00
parent d03afd6bf8
commit dffd09e6f7
42 changed files with 6506 additions and 9625 deletions

View File

@ -342,7 +342,6 @@
836F6F7418BDC2190095E648 /* bgw.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E3818BDC2180095E648 /* bgw.c */; };
836F6F7518BDC2190095E648 /* bnsf.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E3918BDC2180095E648 /* bnsf.c */; };
836F6F7618BDC2190095E648 /* brstm.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E3A18BDC2180095E648 /* brstm.c */; };
836F6F7718BDC2190095E648 /* capdsp.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E3B18BDC2180095E648 /* capdsp.c */; };
836F6F7818BDC2190095E648 /* Cstr.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E3C18BDC2180095E648 /* Cstr.c */; };
836F6F7918BDC2190095E648 /* dc_asd.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E3D18BDC2180095E648 /* dc_asd.c */; };
836F6F7B18BDC2190095E648 /* dc_idvi.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E3F18BDC2180095E648 /* dc_idvi.c */; };
@ -409,7 +408,7 @@
836F6FD118BDC2190095E648 /* ps2_bg00.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E9518BDC2180095E648 /* ps2_bg00.c */; };
836F6FD218BDC2190095E648 /* ps2_bmdx.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E9618BDC2180095E648 /* ps2_bmdx.c */; };
836F6FD318BDC2190095E648 /* ps2_ccc.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E9718BDC2180095E648 /* ps2_ccc.c */; };
836F6FD418BDC2190095E648 /* ps2_dxh.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E9818BDC2180095E648 /* ps2_dxh.c */; };
836F6FD418BDC2190095E648 /* hxd.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E9818BDC2180095E648 /* hxd.c */; };
836F6FD518BDC2190095E648 /* ps2_enth.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E9918BDC2180095E648 /* ps2_enth.c */; };
836F6FD718BDC2190095E648 /* ps2_filp.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E9B18BDC2180095E648 /* ps2_filp.c */; };
836F6FD818BDC2190095E648 /* ps2_gbts.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E9C18BDC2180095E648 /* ps2_gbts.c */; };
@ -678,6 +677,7 @@
83C7282922BC8C1500678B4A /* mixing.c in Sources */ = {isa = PBXBuildFile; fileRef = 83C7282522BC8C1400678B4A /* mixing.c */; };
83C7282A22BC8C1500678B4A /* plugins.c in Sources */ = {isa = PBXBuildFile; fileRef = 83C7282622BC8C1400678B4A /* plugins.c */; };
83D0381824A4129A004CF90F /* swav.c in Sources */ = {isa = PBXBuildFile; fileRef = 83D0381724A4129A004CF90F /* swav.c */; };
83D1189328B2F33400AF3370 /* vab.c in Sources */ = {isa = PBXBuildFile; fileRef = 83D1189228B2F33400AF3370 /* vab.c */; };
83D2007A248DDB770048BD24 /* fsb_encrypted_streamfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 83D20072248DDB760048BD24 /* fsb_encrypted_streamfile.h */; };
83D2007B248DDB770048BD24 /* mups_streamfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 83D20073248DDB760048BD24 /* mups_streamfile.h */; };
83D2007C248DDB770048BD24 /* ktsr.c in Sources */ = {isa = PBXBuildFile; fileRef = 83D20074248DDB760048BD24 /* ktsr.c */; };
@ -1166,7 +1166,6 @@
836F6E3818BDC2180095E648 /* bgw.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = bgw.c; sourceTree = "<group>"; };
836F6E3918BDC2180095E648 /* bnsf.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = bnsf.c; sourceTree = "<group>"; };
836F6E3A18BDC2180095E648 /* brstm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = brstm.c; sourceTree = "<group>"; };
836F6E3B18BDC2180095E648 /* capdsp.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = capdsp.c; sourceTree = "<group>"; };
836F6E3C18BDC2180095E648 /* Cstr.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = Cstr.c; sourceTree = "<group>"; };
836F6E3D18BDC2180095E648 /* dc_asd.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = dc_asd.c; sourceTree = "<group>"; };
836F6E3F18BDC2180095E648 /* dc_idvi.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = dc_idvi.c; sourceTree = "<group>"; };
@ -1233,7 +1232,7 @@
836F6E9518BDC2180095E648 /* ps2_bg00.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_bg00.c; sourceTree = "<group>"; };
836F6E9618BDC2180095E648 /* ps2_bmdx.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_bmdx.c; sourceTree = "<group>"; };
836F6E9718BDC2180095E648 /* ps2_ccc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_ccc.c; sourceTree = "<group>"; };
836F6E9818BDC2180095E648 /* ps2_dxh.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_dxh.c; sourceTree = "<group>"; };
836F6E9818BDC2180095E648 /* hxd.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = hxd.c; sourceTree = "<group>"; };
836F6E9918BDC2180095E648 /* ps2_enth.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_enth.c; sourceTree = "<group>"; };
836F6E9B18BDC2180095E648 /* ps2_filp.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_filp.c; sourceTree = "<group>"; };
836F6E9C18BDC2180095E648 /* ps2_gbts.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_gbts.c; sourceTree = "<group>"; };
@ -1502,6 +1501,7 @@
83C7282522BC8C1400678B4A /* mixing.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mixing.c; sourceTree = "<group>"; };
83C7282622BC8C1400678B4A /* plugins.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = plugins.c; sourceTree = "<group>"; };
83D0381724A4129A004CF90F /* swav.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = swav.c; sourceTree = "<group>"; };
83D1189228B2F33400AF3370 /* vab.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = vab.c; sourceTree = "<group>"; };
83D20072248DDB760048BD24 /* fsb_encrypted_streamfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = fsb_encrypted_streamfile.h; sourceTree = "<group>"; };
83D20073248DDB760048BD24 /* mups_streamfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mups_streamfile.h; sourceTree = "<group>"; };
83D20074248DDB760048BD24 /* ktsr.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ktsr.c; sourceTree = "<group>"; };
@ -2007,7 +2007,6 @@
83B69B212845A26600D2435A /* bw_mp3_riff.c */,
835C883122CC17BD001B4B3F /* bwav.c */,
8306B0CF2098458F000302D4 /* caf.c */,
836F6E3B18BDC2180095E648 /* capdsp.c */,
834FE0E8215C79EC000A5D3D /* ck.c */,
8346D97825BF838C00D1A8B0 /* compresswave.c */,
83A8BAE425667AA7000F5F3F /* cpk.c */,
@ -2078,6 +2077,7 @@
8323894F1D2246C300482226 /* hca.c */,
834FE0DF215C79EB000A5D3D /* hd3_bd3.c */,
836F6E5318BDC2180095E648 /* his.c */,
836F6E9818BDC2180095E648 /* hxd.c */,
834FE0E0215C79EB000A5D3D /* idsp_ie.c */,
8346D97525BF838C00D1A8B0 /* idtech_streamfile.h */,
8346D97425BF838C00D1A8B0 /* idtech.c */,
@ -2201,7 +2201,6 @@
836F6E9518BDC2180095E648 /* ps2_bg00.c */,
836F6E9618BDC2180095E648 /* ps2_bmdx.c */,
836F6E9718BDC2180095E648 /* ps2_ccc.c */,
836F6E9818BDC2180095E648 /* ps2_dxh.c */,
83A8BAE125667AA7000F5F3F /* ps2_enth_streamfile.h */,
836F6E9918BDC2180095E648 /* ps2_enth.c */,
836F6E9B18BDC2180095E648 /* ps2_filp.c */,
@ -2297,8 +2296,8 @@
836F6EF318BDC2190095E648 /* spt_spd.c */,
834FE0D6215C79E9000A5D3D /* sqex_scd_sscf.c */,
836F6EF418BDC2190095E648 /* sqex_scd.c */,
837CEAF023487F2C00E62A4A /* sqex_streamfile.h */,
83A21F84201D8981000F04B9 /* sqex_sead.c */,
837CEAF023487F2C00E62A4A /* sqex_streamfile.h */,
8339B322280FDF250076F74B /* sspf.c */,
8317C24826982CC1007DD0B8 /* sspr.c */,
836F6EBA18BDC2180095E648 /* ster.c */,
@ -2336,6 +2335,7 @@
8349A8F41FE6257D00E26435 /* ubi_sb.c */,
834FE0C5215C79E6000A5D3D /* ue4opus.c */,
834FE0DD215C79EB000A5D3D /* utk.c */,
83D1189228B2F33400AF3370 /* vab.c */,
834FE0E4215C79EC000A5D3D /* vag.c */,
834FE0D3215C79E9000A5D3D /* vai.c */,
836F6ECA18BDC2190095E648 /* vgs_ps.c */,
@ -2772,6 +2772,7 @@
8306B0EE20984590000302D4 /* smc_smh.c in Sources */,
836F6FAD18BDC2190095E648 /* ngc_dsp_konami.c in Sources */,
836F6FF818BDC2190095E648 /* ps2_smpl.c in Sources */,
83D1189328B2F33400AF3370 /* vab.c in Sources */,
836F703818BDC2190095E648 /* ubi_ckd.c in Sources */,
8373341B23F60C7B00DE14DC /* g7221_decoder_lib.c in Sources */,
836F705318BDC2190095E648 /* streamfile.c in Sources */,
@ -2780,7 +2781,7 @@
8346D98325BF83B300D1A8B0 /* speex_decoder.c in Sources */,
836F6F7218BDC2190095E648 /* baf.c in Sources */,
83F5F8831908D0A400C8E65F /* fsb5.c in Sources */,
836F6FD418BDC2190095E648 /* ps2_dxh.c in Sources */,
836F6FD418BDC2190095E648 /* hxd.c in Sources */,
836F700C18BDC2190095E648 /* ps2_wb.c in Sources */,
836F6F7D18BDC2190095E648 /* dc_str.c in Sources */,
83A5F75F198DF021009AF94C /* bfwav.c in Sources */,
@ -3142,7 +3143,6 @@
8306B0BB20984552000302D4 /* blocked_gsb.c in Sources */,
83031ECC243C50CC00C3F3E0 /* blocked_ubi_sce.c in Sources */,
8315868A26F586F900803A3A /* m2_psb.c in Sources */,
836F6F7718BDC2190095E648 /* capdsp.c in Sources */,
836F6FB018BDC2190095E648 /* ngc_dsp_ygo.c in Sources */,
836F703318BDC2190095E648 /* str_snds.c in Sources */,
832BF82221E0514B006F50F1 /* vs_str.c in Sources */,

View File

@ -167,7 +167,7 @@ void decode_msadpcm_stereo(VGMSTREAM* vgmstream, sample_t* outbuf, int32_t first
void decode_msadpcm_mono(VGMSTREAM* vgmstream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, int config);
void decode_msadpcm_ck(VGMSTREAM* vgmstream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel);
long msadpcm_bytes_to_samples(long bytes, int block_size, int channels);
int msadpcm_check_coefs(STREAMFILE* sf, off_t offset);
int msadpcm_check_coefs(STREAMFILE* sf, uint32_t offset);
/* yamaha_decoder */

View File

@ -34,7 +34,7 @@ struct ffmpeg_codec_data {
int bad_init;
// FFmpeg context used for metadata
AVCodec* codec;
const AVCodec* codec;
/* FFmpeg decoder state */
unsigned char* buffer;

View File

@ -501,7 +501,7 @@ static size_t make_opus_header(uint8_t* buf, int buf_size, opus_config *cfg) {
int mapping_family = 0;
/* special multichannel config */
if (cfg->channels > 2) {
if (cfg->channels > 2 || cfg->stream_count > 1) {
/* channel config: 0=standard (single stream mono/stereo), 1=vorbis, 255: not defined */
mapping_family = 1;
header_size += 0x01+0x01+cfg->channels;

View File

@ -248,11 +248,11 @@ long msadpcm_bytes_to_samples(long bytes, int block_size, int channels) {
}
/* test if MSADPCM coefs were re-defined (possible in theory but not used in practice) */
int msadpcm_check_coefs(STREAMFILE* sf, off_t offset) {
int msadpcm_check_coefs(STREAMFILE* sf, uint32_t offset) {
int i;
int count = read_u16le(offset, sf);
if (count != 7) {
vgm_logi("MSADPCM: bad count %i at %lx (report)\n", count, offset);
vgm_logi("MSADPCM: bad count %i at %x (report)\n", count, offset);
goto fail;
}

View File

@ -2,151 +2,172 @@
#include "coding.h"
#include "../util.h"
/* PSVita ADPCM table */
static const int16_t hevag_coefs[128][4] = {
{ 0, 0, 0, 0 },
{ 7680, 0, 0, 0 },
{ 14720, -6656, 0, 0 },
{ 12544, -7040, 0, 0 },
{ 15616, -7680, 0, 0 },
{ 14731, -7059, 0, 0 },
{ 14507, -7366, 0, 0 },
{ 13920, -7522, 0, 0 },
{ 13133, -7680, 0, 0 },
{ 12028, -7680, 0, 0 },
{ 10764, -7680, 0, 0 },
{ 9359, -7680, 0, 0 },
{ 7832, -7680, 0, 0 },
{ 6201, -7680, 0, 0 },
{ 4488, -7680, 0, 0 },
{ 2717, -7680, 0, 0 },
{ 910, -7680, 0, 0 },
{ -910, -7680, 0, 0 },
{ -2717, -7680, 0, 0 },
{ -4488, -7680, 0, 0 },
{ -6201, -7680, 0, 0 },
{ -7832, -7680, 0, 0 },
{ -9359, -7680, 0, 0 },
{ -10764, -7680, 0, 0 },
{ -12028, -7680, 0, 0 },
{ -13133, -7680, 0, 0 },
{ -13920, -7522, 0, 0 },
{ -14507, -7366, 0, 0 },
{ -14731, -7059, 0, 0 },
{ 5376, -9216, 3328, -3072 },
{ -6400, -7168, -3328, -2304 },
{ -10496, -7424, -3584, -1024 },
{ -167, -2722, -494, -541 },
{ -7430, -2221, -2298, 424 },
{ -8001, -3166, -2814, 289 },
{ 6018, -4750, 2649, -1298 },
{ 3798, -6946, 3875, -1216 },
{ -8237, -2596, -2071, 227 },
{ 9199, 1982, -1382, -2316 },
{ 13021, -3044, -3792, 1267 },
{ 13112, -4487, -2250, 1665 },
{ -1668, -3744, -6456, 840 },
{ 7819, -4328, 2111, -506 },
{ 9571, -1336, -757, 487 },
{ 10032, -2562, 300, 199 },
{ -4745, -4122, -5486, -1493 },
{ -5896, 2378, -4787, -6947 },
{ -1193, -9117, -1237, -3114 },
{ 2783, -7108, -1575, -1447 },
{ -7334, -2062, -2212, 446 },
{ 6127, -2577, -315, -18 },
{ 9457, -1858, 102, 258 },
{ 7876, -4483, 2126, -538 },
{ -7172, -1795, -2069, 482 },
{ -7358, -2102, -2233, 440 },
{ -9170, -3509, -2674, -391 },
{ -2638, -2647, -1929, -1637 },
{ 1873, 9183, 1860, -5746 },
{ 9214, 1859, -1124, -2427 },
{ 13204, -3012, -4139, 1370 },
{ 12437, -4792, -256, 622 },
{ -2653, -1144, -3182, -6878 },
{ 9331, -1048, -828, 507 },
{ 1642, -620, -946, -4229 },
{ 4246, -7585, -533, -2259 },
{ -8988, -3891, -2807, 44 },
{ -2562, -2735, -1730, -1899 },
{ 3182, -483, -714, -1421 },
{ 7937, -3844, 2821, -1019 },
{ 10069, -2609, 314, 195 },
{ 8400, -3297, 1551, -155 },
{ -8529, -2775, -2432, -336 },
{ 9477, -1882, 108, 256 },
{ 75, -2241, -298, -6937 },
{ -9143, -4160, -2963, 5 },
{ -7270, -1958, -2156, 460 },
{ -2740, 3745, 5936, -1089 },
{ 8993, 1948, -683, -2704 },
{ 13101, -2835, -3854, 1055 },
{ 9543, -1961, 130, 250 },
{ 5272, -4270, 3124, -3157 },
{ -7696, -3383, -2907, -456 },
{ 7309, 2523, 434, -2461 },
{ 10275, -2867, 391, 172 },
{ 10940, -3721, 665, 97 },
{ 24, -310, -1262, 320 },
{ -8122, -2411, -2311, -271 },
{ -8511, -3067, -2337, 163 },
{ 326, -3846, 419, -933 },
{ 8895, 2194, -541, -2880 },
{ 12073, -1876, -2017, -601 },
{ 8729, -3423, 1674, -169 },
{ 12950, -3847, -3007, 1946 },
{ 10038, -2570, 302, 198 },
{ 9385, -2757, 1008, 41 },
{ -4720, -5006, -2852, -1161 },
{ 7869, -4326, 2135, -501 },
{ 2450, -8597, 1299, -2780 },
{ 10192, -2763, 360, 181 },
{ 11313, -4213, 833, 53 },
{ 10154, -2716, 345, 185 },
{ 9638, -1417, -737, 482 },
{ 3854, -4554, 2843, -3397 },
{ 6699, -5659, 2249, -1074 },
{ 11082, -3908, 728, 80 },
{ -1026, -9810, -805, -3462 },
{ 10396, -3746, 1367, -96 },
{ 10287, 988, -1915, -1437 },
{ 7953, 3878, -764, -3263 },
{ 12689, -3375, -3354, 2079 },
{ 6641, 3166, 231, -2089 },
{ -2348, -7354, -1944, -4122 },
{ 9290, -4039, 1885, -246 },
{ 4633, -6403, 1748, -1619 },
{ 11247, -4125, 802, 61 },
{ 9807, -2284, 219, 222 },
{ 9736, -1536, -706, 473 },
{ 8440, -3436, 1562, -176 },
{ 9307, -1021, -835, 509 },
{ 1698, -9025, 688, -3037 },
{ 10214, -2791, 368, 179 },
{ 8390, 3248, -758, -2989 },
{ 7201, 3316, 46, -2614 },
{ -88, -7809, -538, -4571 },
{ 6193, -5189, 2760, -1245 },
{ 12325, -1290, -3284, 253 },
{ 13064, -4075, -2824, 1877 },
{ 5333, 2999, 775, -1132 }
};
/**
* Sony's HEVAG (High Efficiency VAG) ADPCM, used in PSVita games (hardware decoded).
* Sony's HEVAG (High Efficiency VAG) ADPCM, used in Vita/PS4 games (hardware decoded).
* Evolution of the regular VAG (same flags and frames), uses 4 history samples and a bigger table.
*
* Original research and algorithm by id-daemon / daemon1.
* Reverse engineered from PC tools, base int code by daemon1 (using K=2^13 aka coefs * 8192, 0.9375 > 7680,
* ).
*
* HEVAG is made to be compatible with original VAG, so table indexes <= 0x1c behave like old codec
* setting hist3/4 coefs to 0 (as a minor optimization og code won't mult hist3/4 in lower table indexes).
*
* Original code uses SIMD to do 4 codes at once (unrolled doing 7 groups of 4 codes = 28 samples).
* Tables are rather complex to (presumable) handle hist dependencies, and since it's VAG compatible
* should be the same (maybe rounding diffs?). Hist and output are floats (output converted to +-1.0
* or +-32768.0 +-0.5 based on a flag). Code below just does it linearly 1 by 1 in pcm16.
*/
/* ADPCM table */
static const float hevag_coefs_f[128][4] = {
{-0.0, -0.0, -0.0, -0.0 },
{ 0.9375, -0.0, -0.0, -0.0 },
{ 1.796875, -0.8125, -0.0, -0.0 },
{ 1.53125, -0.859375, -0.0, -0.0 },
{ 1.90625, -0.9375, -0.0, -0.0 },
{ 1.7982178, -0.86169434, -0.0, -0.0 },
{ 1.770874, -0.89916992, -0.0, -0.0 },
{ 1.6992188, -0.91821289, -0.0, -0.0 },
{ 1.6031494, -0.9375, -0.0, -0.0 },
{ 1.4682617, -0.9375, -0.0, -0.0 },
{ 1.3139648, -0.9375, -0.0, -0.0 },
{ 1.1424561, -0.9375, -0.0, -0.0 },
{ 0.95605469, -0.9375, -0.0, -0.0 },
{ 0.75695801, -0.9375, -0.0, -0.0 },
{ 0.54785156, -0.9375, -0.0, -0.0 },
{ 0.33166504, -0.9375, -0.0, -0.0 },
{ 0.11108398, -0.9375, -0.0, -0.0 },
{-0.11108398, -0.9375, -0.0, -0.0 },
{-0.33166504, -0.9375, -0.0, -0.0 },
{-0.54785156, -0.9375, -0.0, -0.0 },
{-0.75695801, -0.9375, -0.0, -0.0 },
{-0.95605469, -0.9375, -0.0, -0.0 },
{-1.1424561, -0.9375, -0.0, -0.0 },
{-1.3139648, -0.9375, -0.0, -0.0 },
{-1.4682617, -0.9375, -0.0, -0.0 },
{-1.6031494, -0.9375, -0.0, -0.0 },
{-1.6992188, -0.91821289, -0.0, -0.0 },
{-1.770874, -0.89916992, -0.0, -0.0 },
{-1.7982178, -0.86169434, -0.0, -0.0 },
{ 0.65625, -1.125, 0.40625, -0.375 },
{-0.78125, -0.875, -0.40625, -0.28125 },
{-1.28125, -0.90625, -0.4375, -0.125 },
{-0.020385742, -0.33227539, -0.060302734, -0.066040039 },
{-0.90698242, -0.27111816, -0.28051758, 0.051757812 },
{-0.97668457, -0.38647461, -0.34350586, 0.03527832 },
{ 0.73461914, -0.57983398, 0.32336426, -0.15844727 },
{ 0.46362305, -0.84790039, 0.47302246, -0.1484375 },
{-1.0054932, -0.31689453, -0.25280762, 0.027709961 },
{ 1.1229248, 0.24194336, -0.16870117, -0.28271484 },
{ 1.5894775, -0.37158203, -0.46289062, 0.15466309 },
{ 1.6005859, -0.54772949, -0.2746582, 0.20324707 },
{-0.20361328, -0.45703125, -0.78808594, 0.10253906 },
{ 0.95446777, -0.52832031, 0.25769043, -0.061767578 },
{ 1.168335, -0.16308594, -0.092407227, 0.059448242 },
{ 1.2246094, -0.31274414, 0.036621094, 0.024291992 },
{-0.57922363, -0.50317383, -0.66967773, -0.18225098 },
{-0.71972656, 0.2902832, -0.58435059, -0.84802246 },
{-0.14562988, -1.112915, -0.15100098, -0.38012695 },
{ 0.33972168, -0.86767578, -0.19226074, -0.17663574 },
{-0.89526367, -0.25170898, -0.27001953, 0.054443359 },
{ 0.7479248, -0.3145752, -0.038452148, -0.0021972656 },
{ 1.1544189, -0.22680664, 0.012451172, 0.031494141 },
{ 0.96142578, -0.54724121, 0.25952148, -0.065673828 },
{-0.87548828, -0.21911621, -0.25256348, 0.058837891 },
{-0.89819336, -0.2565918, -0.27258301, 0.053710938 },
{-1.1193848, -0.42834473, -0.32641602, -0.047729492 },
{-0.32202148, -0.32312012, -0.23547363, -0.1998291 },
{ 0.2286377, 1.1209717, 0.22705078, -0.70141602 },
{ 1.1247559, 0.22692871, -0.13720703, -0.29626465 },
{ 1.6118164, -0.36767578, -0.50524902, 0.16723633 },
{ 1.5181885, -0.58496094, -0.03125, 0.075927734 },
{-0.32385254, -0.13964844, -0.38842773, -0.83959961 },
{ 1.1390381, -0.12792969, -0.10107422, 0.061889648 },
{ 0.20043945, -0.075683594, -0.11547852, -0.51623535 },
{ 0.51831055, -0.92590332, -0.065063477, -0.27575684 },
{-1.097168, -0.47497559, -0.34265137, 0.0053710938 },
{-0.31274414, -0.3338623, -0.21118164, -0.23181152 },
{ 0.38842773, -0.058959961, -0.087158203, -0.17346191 },
{ 0.96887207, -0.46923828, 0.34436035, -0.12438965 },
{ 1.229126, -0.31848145, 0.038330078, 0.023803711 },
{ 1.0253906, -0.40246582, 0.18933105, -0.018920898 },
{-1.0411377, -0.33874512, -0.296875, -0.041015625 },
{ 1.1568604, -0.22973633, 0.013183594, 0.03125 },
{ 0.0091552734, -0.27355957, -0.036376953, -0.84680176 },
{-1.1160889, -0.5078125, -0.36169434, 0.00061035156 },
{-0.88745117, -0.23901367, -0.26318359, 0.056152344 },
{-0.33447266, 0.45715332, 0.72460938, -0.13293457 },
{ 1.0977783, 0.23779297, -0.083374023, -0.33007812 },
{ 1.5992432, -0.34606934, -0.47045898, 0.12878418 },
{ 1.164917, -0.23937988, 0.015869141, 0.030517578 },
{ 0.64355469, -0.52124023, 0.38134766, -0.38537598 },
{-0.93945312, -0.41296387, -0.3548584, -0.055664062 },
{ 0.89221191, 0.3079834, 0.052978516, -0.30041504 },
{ 1.2542725, -0.34997559, 0.047729492, 0.020996094 },
{ 1.3354492, -0.45422363, 0.081176758, 0.01184082 },
{ 0.0029296875, -0.037841797, -0.15405273, 0.0390625 },
{-0.99145508, -0.29431152, -0.28210449, -0.033081055 },
{-1.0389404, -0.37438965, -0.28527832, 0.019897461 },
{ 0.039794922, -0.46948242, 0.051147461, -0.1138916 },
{ 1.0858154, 0.26782227, -0.066040039, -0.3515625 },
{ 1.4737549, -0.22900391, -0.24621582, -0.073364258 },
{ 1.0655518, -0.41784668, 0.2043457, -0.020629883 },
{ 1.5808105, -0.46960449, -0.36706543, 0.23754883 },
{ 1.2253418, -0.3137207, 0.036865234, 0.024169922 },
{ 1.1456299, -0.33654785, 0.12304688, 0.0050048828 },
{-0.57617188, -0.61108398, -0.34814453, -0.14172363 },
{ 0.96057129, -0.52807617, 0.26062012, -0.061157227 },
{ 0.29907227, -1.0494385, 0.15856934, -0.33935547 },
{ 1.2441406, -0.33728027, 0.043945312, 0.022094727 },
{ 1.3809814, -0.51428223, 0.10168457, 0.0064697266 },
{ 1.239502, -0.33154297, 0.042114258, 0.022583008 },
{ 1.1765137, -0.17297363, -0.08996582, 0.058837891 },
{ 0.47045898, -0.5559082, 0.3470459, -0.41467285 },
{ 0.81774902, -0.6907959, 0.27453613, -0.13110352 },
{ 1.3527832, -0.47705078, 0.088867188, 0.009765625 },
{-0.12524414, -1.1975098, -0.098266602, -0.42260742 },
{ 1.269043, -0.45727539, 0.16687012, -0.01171875 },
{ 1.2557373, 0.12060547, -0.23376465, -0.17541504 },
{ 0.9708252, 0.47338867, -0.093261719, -0.39831543 },
{ 1.5489502, -0.4119873, -0.40942383, 0.25378418 },
{ 0.81066895, 0.38647461, 0.028198242, -0.25500488 },
{-0.28662109, -0.89770508, -0.23730469, -0.50317383 },
{ 1.1340332, -0.49304199, 0.23010254, -0.030029297 },
{ 0.56555176, -0.78161621, 0.21337891, -0.19763184 },
{ 1.3729248, -0.50354004, 0.097900391, 0.0074462891 },
{ 1.1971436, -0.27880859, 0.026733398, 0.027099609 },
{ 1.1884766, -0.1875, -0.086181641, 0.057739258 },
{ 1.0302734, -0.41943359, 0.19067383, -0.021484375 },
{ 1.1361084, -0.12463379, -0.10192871, 0.062133789 },
{ 0.20727539, -1.1016846, 0.083984375, -0.37072754 },
{ 1.2468262, -0.34069824, 0.044921875, 0.021850586 },
{ 1.0241699, 0.39648438, -0.092529297, -0.36486816 },
{ 0.87902832, 0.40478516, 0.0056152344, -0.3190918 },
{-0.010742188, -0.95324707, -0.065673828, -0.5579834 },
{ 0.75598145, -0.63342285, 0.33691406, -0.15197754 },
{ 1.5045166, -0.1574707, -0.40087891, 0.030883789 },
{ 1.5947266, -0.49743652, -0.34472656, 0.22912598 },
{ 0.65100098, 0.36608887, 0.094604492, -0.13818359 },
};
#if 0
/* original scale used instead of "shift = 20 - i"
* adjusted for +-1.0 floats, so if used for pcm16 needs to be scaled up ([i] = 1.0 / 128.0*i) */
static const float scale_table_f[16] = {
0.0078125f, 0.00390625f, 0.001953125f, 0.0009765625f,
0.00048828125f, 0.000244140625f, 0.0001220703125f, 0.00006103515625f,
0.000030517578125f, 0.0000152587890625f, 0.00000762939453125f, 0.000003814697265625f,
0.0000019073486328125f, 0.00000095367431640625f, 0.000000476837158203125f, 0.000000238418579101562f,
};
#endif
void decode_hevag(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) {
uint8_t frame[0x10] = {0};
off_t frame_offset;
int i, frames_in, sample_count = 0;
size_t bytes_per_frame, samples_per_frame;
int coef_index, shift_factor, flag;
int index, shift, flag;
int32_t hist1 = stream->adpcm_history1_32;
int32_t hist2 = stream->adpcm_history2_32;
int32_t hist3 = stream->adpcm_history3_32;
@ -162,36 +183,45 @@ void decode_hevag(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing
/* parse frame header */
frame_offset = stream->offset + bytes_per_frame * frames_in;
read_streamfile(frame, frame_offset, bytes_per_frame, stream->streamfile); /* ignore EOF errors */
coef_index = (frame[0] >> 4) & 0xf;
shift_factor = (frame[0] >> 0) & 0xf;
coef_index = ((frame[1] >> 0) & 0xf0) | coef_index;
index = (frame[0] >> 4) & 0xf;
shift = (frame[0] >> 0) & 0xf;
index = ((frame[1] >> 0) & 0xf0) | index;
flag = (frame[1] >> 0) & 0xf; /* same flags */
VGM_ASSERT_ONCE(coef_index > 127 || shift_factor > 12, "HEVAG: in+correct coefs/shift at %x\n", (uint32_t)frame_offset);
if (coef_index > 127)
coef_index = 127; /* ? */
if (shift_factor > 12)
shift_factor = 9; /* ? */
VGM_ASSERT_ONCE(index > 127 || shift > 12, "HEVAG: in+correct coefs/shift at %x\n", (uint32_t)frame_offset);
if (index > 127) /* not validated so would read garbage, simulate with 0 */
index = 0;
//if (shift > 12) /* shouldn't happen but accepted in scale_table (just zeroes codes) */
// shift = 9;
shift_factor = 20 - shift_factor;
/* decode nibbles */
for (i = first_sample; i < first_sample + samples_to_do; i++) {
int32_t sample = 0;
int32_t code, sample = 0;
if (flag < 0x07) { /* with flag 0x07 decoded sample must be 0 */
uint8_t nibbles = frame[0x02 + i/2];
sample = (i&1 ? /* low nibble first */
code = (i&1 ? /* low nibble first */
get_high_nibble_signed(nibbles):
get_low_nibble_signed(nibbles)) << shift_factor; /*scale*/
sample = ((hist1 * hevag_coefs[coef_index][0] +
hist2 * hevag_coefs[coef_index][1] +
hist3 * hevag_coefs[coef_index][2] +
hist4 * hevag_coefs[coef_index][3]) >> 5) + sample;
get_low_nibble_signed(nibbles));
//code = (code << 0x4) * scale_table_f[shift]; /* OG s8 sign extension w/ scale table (+-1.0) */
code = ((code << 12) >> shift);
sample = hist1 * hevag_coefs_f[index][0] +
hist2 * hevag_coefs_f[index][1] +
hist3 * hevag_coefs_f[index][2] +
hist4 * hevag_coefs_f[index][3];
sample = code + sample; /* no clamping here */
#if 0
// base int code
sample = code << (20 - shift_factor);
sample = (hists... * coefs....) >> 5) + sample;
sample >>= 8;
#endif
}
outbuf[sample_count] = clamp16(sample); /*clamping*/
outbuf[sample_count] = clamp16(sample);
sample_count += channelspacing;
hist4 = hist3;

View File

@ -110,6 +110,7 @@ static const char* extension_list[] = {
"bik2",
//"bin", //common
"bk2",
"bkr", //txth/reserved [P.N.03 (GC), Viewtiful Joe (GC)]
"blk",
"bmdx",
"bms",
@ -128,7 +129,6 @@ static const char* extension_list[] = {
"bwav",
"caf",
"capdsp",
"cbd2",
"ccc",
"cd",
@ -166,7 +166,6 @@ static const char* extension_list[] = {
"dspw",
"dtk",
"dvi",
"dxh",
"dyx", //txth/reserved [Shrek 4 (iOS)]
"e4x",
@ -258,6 +257,7 @@ static const char* extension_list[] = {
"kces",
"kcey", //fake extension/header id for .pcm (renamed, to be removed)
"km9",
"kmx",
"kovs", //fake extension/header id for .kvs
"kno",
"kns",
@ -442,6 +442,7 @@ static const char* extension_list[] = {
"sab",
"sad",
"saf",
"sag",
"sam", //txth/reserved [Lost Kingdoms 2 (GC)]
"sap",
"sb0",
@ -456,6 +457,7 @@ static const char* extension_list[] = {
"sbin",
"sbr",
"sbv",
"sig",
"sm0",
"sm1",
"sm2",
@ -468,6 +470,7 @@ static const char* extension_list[] = {
"scd",
"sch",
"sd9",
"sdp", //txth/reserved [Metal Gear Arcade (AC)]
"sdf",
"sdt",
"seb",
@ -538,7 +541,6 @@ static const char* extension_list[] = {
"szd3",
"tad",
"tec",
"tgq",
"tgv",
"thp",
@ -560,12 +562,13 @@ static const char* extension_list[] = {
"v0",
//"v1", //dual channel with v0
"va3",
"vab",
"vag",
"vai",
"vam", //txth/reserved [Rocket Power: Beach Bandits (PS2)]
"vas",
"vawx",
"vb",
"vb", //txth/reserved [Tantei Jinguji Saburo: Mikan no Rupo (PS1)]
"vbk",
"vbx", //txth/reserved [THE Taxi 2 (PS2)]
"vds",
@ -574,6 +577,7 @@ static const char* extension_list[] = {
"vgm", //txth/reserved [Maximo (PS2)]
"vgs",
"vgv",
"vh",
"vid",
"vig",
"vis",
@ -1045,7 +1049,7 @@ static const meta_info meta_info_list[] = {
{meta_ACM, "InterPlay ACM Header"},
{meta_MUS_ACM, "InterPlay MUS ACM header"},
{meta_PS2_KCES, "Konami KCES Header"},
{meta_PS2_DXH, "Tokobot Plus DXH Header"},
{meta_HXD, "Tecmo HXD Header"},
{meta_VSV, "Square Enix .vsv Header"},
{meta_RIFF_WAVE_labl, "RIFF WAVE header with loop markers"},
{meta_RIFF_WAVE_smpl, "RIFF WAVE header with sample looping info"},
@ -1057,7 +1061,6 @@ static const meta_info meta_info_list[] = {
{meta_PS2_PCM, "Konami KCEJ East .PCM header"},
{meta_PS2_RKV, "Legacy of Kain - Blood Omen 2 RKV PS2 header"},
{meta_PS2_VAS, "Konami .VAS header"},
{meta_PS2_TEC, "assumed TECMO badflagged stream by .tec extension"},
{meta_PS2_ENTH, ".enth Header"},
{meta_SDT, "High Voltage .sdt header"},
{meta_NGC_TYDSP, ".tydsp Header"},
@ -1149,7 +1152,6 @@ static const meta_info meta_info_list[] = {
{meta_HIS, "Her Interactive HIS header"},
{meta_AST_MV, "MicroVision AST header"},
{meta_AST_MMV, "Marvelous AST header"},
{meta_CAPDSP, "Capcom DSP header"},
{meta_DMSG, "Microsoft RIFF DMSG header"},
{meta_PONA_3DO, "Policenauts BGM header"},
{meta_PONA_PSX, "Policenauts BGM header"},
@ -1407,6 +1409,7 @@ static const meta_info meta_info_list[] = {
{meta_ADM3, "Crankcase ADM3 header"},
{meta_TT_AD, "Traveller's Tales AUDIO_DATA header"},
{meta_SNDZ, "Sony SNDZ header"},
{meta_VAB, "Sony VAB header"},
};
void get_vgmstream_coding_description(VGMSTREAM* vgmstream, char* out, size_t out_size) {

View File

@ -25,16 +25,16 @@ VGMSTREAM* init_vgmstream_adx(STREAMFILE* sf) {
VGMSTREAM* init_vgmstream_adx_subkey(STREAMFILE* sf, uint16_t subkey) {
VGMSTREAM* vgmstream = NULL;
off_t start_offset, hist_offset = 0;
int loop_flag = 0, channel_count;
int32_t loop_start_sample = 0, loop_end_sample = 0;
int loop_flag = 0, channels, sample_rate;
int32_t num_samples, loop_start_sample = 0, loop_end_sample = 0;
uint16_t cutoff;
uint16_t version;
uint8_t encoding_type;
uint8_t frame_size;
uint8_t encoding_type, frame_size;
int16_t coef1, coef2;
uint16_t xor_start = 0, xor_mult = 0, xor_add = 0;
meta_t header_type;
coding_t coding_type;
int16_t coef1, coef2;
uint16_t xor_start=0,xor_mult=0,xor_add=0;
/* checks*/
@ -46,11 +46,13 @@ VGMSTREAM* init_vgmstream_adx_subkey(STREAMFILE* sf, uint16_t subkey) {
if (!check_extensions(sf,"adx,adp"))
goto fail;
/* CRI checks both 0x8000 and memcmps this */
start_offset = read_u16be(0x02,sf) + 0x04;
if (read_u16be(start_offset - 0x06,sf) != 0x2863 || /* "(c" */
read_u32be(start_offset - 0x04,sf) != 0x29435249) /* ")CRI" */
goto fail;
encoding_type = read_u8(0x04, sf);
switch (encoding_type) {
case 0x02:
@ -67,19 +69,19 @@ VGMSTREAM* init_vgmstream_adx_subkey(STREAMFILE* sf, uint16_t subkey) {
}
/* ADX encoders can't set this value, but is honored by ADXPlay if changed and multiple of 0x12,
* though output is unusual and may not be fully supported (works in mono so not an interleave) */
frame_size = read_8bit(0x05, sf);
* though output is unusual and may not be fully supported (works in mono so not an interleave)
* Later versions of the decode just use constant 0x12 ignoring it, though. */
frame_size = read_u8(0x05, sf);
if (read_u8(0x06,sf) != 4) /* bits per sample */
goto fail;
/* older ADX (adxencd) up to 2ch, newer ADX (criatomencd) up to 8 */
channel_count = read_u8(0x07,sf);
/* 0x08: sample rate */
/* 0x0c: samples */
/* 0x10: high-pass frequency */
version = read_u16be(0x12,sf);
channels = read_u8(0x07,sf);
sample_rate = read_s32be(0x08,sf);
num_samples = read_s32be(0x0c,sf);
cutoff = read_u16be(0x10,sf); /* high-pass cutoff frequency, always 500 */
version = read_u16be(0x12,sf); /* version + revision, originally read as separate */
/* encryption */
if (version == 0x0408) {
@ -111,12 +113,12 @@ VGMSTREAM* init_vgmstream_adx_subkey(STREAMFILE* sf, uint16_t subkey) {
/* 0x00 (2): initial loop padding (the encoder adds a few blank samples so loop start is block-aligned; max 31)
* ex. loop_start=12: enc_start=32, padding=20 (32-20=12); loop_start=35: enc_start=64, padding=29 (64-29=35)
* 0x02 (2): loop sample(?) flag (always 1) */
loop_flag = read_32bitBE(loops_offset+0x04,sf) != 0; /* loop offset(?) flag (always 1) */
loop_start_sample = read_32bitBE(loops_offset+0x08,sf);
//loop_start_offset = read_32bitBE(loops_offset+0x0c,sf);
loop_end_sample = read_32bitBE(loops_offset+0x10,sf);
//loop_end_offset = read_32bitBE(loops_offset+0x14,sf);
* 0x02 (2): loop flag? (always 1) */
loop_flag = read_s32be(loops_offset+0x04,sf) != 0; /* loop count + loop type? (always 1) */
loop_start_sample = read_s32be(loops_offset+0x08,sf);
//loop_start_offset = read_u32be(loops_offset+0x0c,sf);
loop_end_sample = read_s32be(loops_offset+0x10,sf);
//loop_end_offset = read_u32be(loops_offset+0x14,sf);
}
}
else if (version == 0x0400) { /* common */
@ -126,23 +128,23 @@ VGMSTREAM* init_vgmstream_adx_subkey(STREAMFILE* sf, uint16_t subkey) {
header_type = meta_ADX_04;
hist_offset = base_size; /* always present but often blank */
hist_size = (channel_count > 1 ? 0x04 * channel_count : 0x04 + 0x04); /* min is 8, even in 1ch files */
hist_size = (channels > 1 ? 0x04 * channels : 0x04 + 0x04); /* min is 0x8, even in 1ch files */
ainf_offset = base_size + hist_size + 0x04; /* not seen with >2ch though */
if (read_u32be(ainf_offset+0x00,sf) == 0x41494E46) /* "AINF" */
ainf_size = read_32bitBE(ainf_offset+0x04,sf);
if (is_id32be(ainf_offset+0x00,sf, "AINF"))
ainf_size = read_u32be(ainf_offset+0x04,sf);
if (start_offset - ainf_size - 0x06 >= hist_offset + hist_size + loops_size) { /* enough space for loop info? */
off_t loops_offset = base_size + hist_size;
/* 0x00 (2): initial loop padding (the encoder adds a few blank samples so loop start is block-aligned; max 31)
* ex. loop_start=12: enc_start=32, padding=20 (32-20=12); loop_start=35: enc_start=64, padding=29 (64-29=35)
* 0x02 (2): loop sample(?) flag (always 1) */
loop_flag = read_32bitBE(loops_offset+0x04,sf) != 0; /* loop offset(?) flag (always 1) */
loop_start_sample = read_32bitBE(loops_offset+0x08,sf);
//loop_start_offset = read_32bitBE(loops_offset+0x0c,sf);
loop_end_sample = read_32bitBE(loops_offset+0x10,sf);
//loop_end_offset = read_32bitBE(loops_offset+0x14,sf);
* 0x02 (2): loop flag? (always 1) */
loop_flag = read_s32be(loops_offset+0x04,sf) != 0; /* loop count + loop type? (always 1) */
loop_start_sample = read_s32be(loops_offset+0x08,sf);
//loop_start_offset = read_u32be(loops_offset+0x0c,sf);
loop_end_sample = read_s32be(loops_offset+0x10,sf);
//loop_end_offset = read_u32be(loops_offset+0x14,sf);
}
/* AINF header info (may be inserted by CRI's tools but is rarely used)
@ -171,11 +173,11 @@ VGMSTREAM* init_vgmstream_adx_subkey(STREAMFILE* sf, uint16_t subkey) {
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
vgmstream = allocate_vgmstream(channels, loop_flag);
if (!vgmstream) goto fail;
vgmstream->sample_rate = read_32bitBE(0x08,sf);
vgmstream->num_samples = read_32bitBE(0x0c,sf);
vgmstream->sample_rate = sample_rate;
vgmstream->num_samples = num_samples;
vgmstream->loop_start_sample = loop_start_sample;
vgmstream->loop_end_sample = loop_end_sample;
@ -189,7 +191,7 @@ VGMSTREAM* init_vgmstream_adx_subkey(STREAMFILE* sf, uint16_t subkey) {
if (coding_type == coding_CRI_ADX_fixed) {
int i;
/* standard XA coefs * (2<<11) */
for (i = 0; i < channel_count; i++) {
for (i = 0; i < channels; i++) {
vgmstream->ch[i].adpcm_coef[0] = 0x0000;
vgmstream->ch[i].adpcm_coef[1] = 0x0000;
vgmstream->ch[i].adpcm_coef[2] = 0x0F00;
@ -201,23 +203,22 @@ VGMSTREAM* init_vgmstream_adx_subkey(STREAMFILE* sf, uint16_t subkey) {
}
}
else {
double x,y,z,a,b,c;
/* coefs from cutoff frequency (some info from decomps, uses floats but no diffs if using doubles due to rounding) */
int i;
/* high-pass cutoff frequency, always 500 that I've seen */
uint16_t cutoff = read_u16be(0x10,sf);
float x, y, z, a, b, c;
x = cutoff;
y = vgmstream->sample_rate;
z = cos(2.0 * M_PI * x / y);
y = sample_rate;
z = cosf(2.0 * M_PI * x / y); /* 2.0 * M_PI: 6.28318548202515f (decomp) */
a = M_SQRT2 - z;
b = M_SQRT2 - 1.0;
c = (a - sqrt((a + b) * (a - b))) / b;
a = M_SQRT2 - z; /* M_SQRT2: 1.41421353816986f (decomp) */
b = M_SQRT2 - 1.0; /* M_SQRT2 - 1: 0.414213538169861f (decomp) */
c = (a - sqrtf((a + b) * (a - b))) / b; /* this seems calculated with a custom algorithm */
coef1 = (short)(c * 8192);
coef2 = (short)(c * c * -4096);
for (i = 0; i < channel_count; i++) {
for (i = 0; i < channels; i++) {
vgmstream->ch[i].adpcm_coef[0] = coef1;
vgmstream->ch[i].adpcm_coef[1] = coef2;
}
@ -227,17 +228,17 @@ VGMSTREAM* init_vgmstream_adx_subkey(STREAMFILE* sf, uint16_t subkey) {
{
int i;
for (i = 0; i < channel_count; i++) {
for (i = 0; i < channels; i++) {
/* 2 hist shorts per ch, corresponding to the very first original sample repeated (verified with CRI's encoders).
* Not vital as their effect is small, after a few samples they don't matter, and most songs start in silence. */
if (hist_offset) {
vgmstream->ch[i].adpcm_history1_32 = read_16bitBE(hist_offset + i*4 + 0x00,sf);
vgmstream->ch[i].adpcm_history2_32 = read_16bitBE(hist_offset + i*4 + 0x02,sf);
vgmstream->ch[i].adpcm_history1_32 = read_s16be(hist_offset + i*4 + 0x00,sf);
vgmstream->ch[i].adpcm_history2_32 = read_s16be(hist_offset + i*4 + 0x02,sf);
}
if (coding_type == coding_CRI_ADX_enc_8 || coding_type == coding_CRI_ADX_enc_9) {
int j;
vgmstream->ch[i].adx_channels = channel_count;
vgmstream->ch[i].adx_channels = channels;
vgmstream->ch[i].adx_xor = xor_start;
vgmstream->ch[i].adx_mult = xor_mult;
vgmstream->ch[i].adx_add = xor_add;
@ -249,7 +250,7 @@ VGMSTREAM* init_vgmstream_adx_subkey(STREAMFILE* sf, uint16_t subkey) {
}
if ( !vgmstream_open_stream(vgmstream, sf, start_offset) )
if (!vgmstream_open_stream(vgmstream, sf, start_offset))
goto fail;
return vgmstream;
@ -293,9 +294,9 @@ static int find_adx_key(STREAMFILE* sf, uint8_t type, uint16_t *xor_start, uint1
}
if (key_size == 0x06 && !is_ascii) {
*xor_start = get_16bitBE(keybuf + 0x00);
*xor_mult = get_16bitBE(keybuf + 0x02);
*xor_add = get_16bitBE(keybuf + 0x04);
*xor_start = get_u16be(keybuf + 0x00);
*xor_mult = get_u16be(keybuf + 0x02);
*xor_add = get_u16be(keybuf + 0x04);
return 1;
}
else if (type == 8 && is_ascii) {
@ -321,11 +322,11 @@ static int find_adx_key(STREAMFILE* sf, uint8_t type, uint16_t *xor_start, uint1
/* setup totals */
{
int frame_count;
int channels = read_8bit(0x07, sf);
int num_samples = read_32bitBE(0x0c, sf);
int channels = read_u8(0x07, sf);
int num_samples = read_s32be(0x0c, sf);
off_t end_offset;
start_offset = read_16bitBE(0x02, sf) + 0x4;
start_offset = read_u16be(0x02, sf) + 0x4;
end_offset = (num_samples + 31) / 32 * frame_size * channels + start_offset; /* samples-to-bytes */
frame_count = (end_offset - start_offset) / frame_size;

View File

@ -101,7 +101,7 @@ static const adxkey_info adxkey8_list[] = {
{0x5f5d,0x552b,0x5507, "DATAM-KK2",0},
/* Sakura Taisen: Atsuki Chishio ni (PS2) [Sega] */
{0x645d,0x6011,0x5c29, NULL,0}, // keystring may be printf'd "%08X" + number (unlikely key: "[Seq][ADX] illegal cri or libsd status.")
{0x645d,0x6011,0x5c29, "[Seq][ADX] illegal cri or libsd status.",0}, // actual keystring (obfuscation probably)
/* Sakura Taisen Monogatari: Mysterious Paris (PS2) [Sega] */
{0x62ad,0x4b13,0x5957, "inoue4126",0},
@ -198,6 +198,9 @@ static const adxkey_info adxkey8_list[] = {
/* Mirai Nikki: 13-ninme no Nikki Shoyuusha Re-Write (PSP) */
{0x58a3,0x66f5,0x599f, "FDRW17th",0},
/* Shoujo Yoshitsune-den Ni - Toki wo Koeru Chigiri (PS2) */
{0x62d7,0x483d,0x4fb7, "YOSHI2",0},
};

View File

@ -83,7 +83,6 @@ 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

View File

@ -1,68 +0,0 @@
#include "meta.h"
#include "../util.h"
/* CAPDSP (found in Capcom games) */
VGMSTREAM * init_vgmstream_capdsp(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
char filename[PATH_LIMIT];
off_t start_offset;
int loop_flag;
int channel_count;
/* check extension, case insensitive */
streamFile->get_name(streamFile,filename,sizeof(filename));
if (strcasecmp("capdsp",filename_extension(filename))) goto fail;
loop_flag = (read_32bitBE(0x14,streamFile) !=2);
channel_count = read_32bitBE(0x10,streamFile);
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail;
/* fill in the vital statistics */
start_offset = 0x80;
vgmstream->channels = channel_count;
vgmstream->sample_rate = read_32bitBE(0x0C,streamFile);
vgmstream->coding_type = coding_NGC_DSP;
vgmstream->num_samples = read_32bitBE(0x04,streamFile);
if (loop_flag) {
vgmstream->loop_start_sample = read_32bitBE(0x14,streamFile)/8/channel_count*14;
vgmstream->loop_end_sample = read_32bitBE(0x18,streamFile)/8/channel_count*14;
}
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = 0x4000;
vgmstream->meta_type = meta_CAPDSP;
if (vgmstream->coding_type == coding_NGC_DSP) {
int i;
for (i=0;i<8;i++) {
vgmstream->ch[0].adpcm_coef[i*2]=read_16bitBE(0x20+i*2,streamFile);
vgmstream->ch[0].adpcm_coef[i*2+1]=read_16bitBE(0x30+i*2,streamFile);
vgmstream->ch[1].adpcm_coef[i*2]=read_16bitBE(0x40+i*2,streamFile);
vgmstream->ch[1].adpcm_coef[i*2+1]=read_16bitBE(0x50+i*2,streamFile);
}
}
/* open the file for reading */
{
int i;
STREAMFILE * file;
file = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE);
if (!file) goto fail;
for (i=0;i<channel_count;i++) {
vgmstream->ch[i].streamfile = file;
vgmstream->ch[i].channel_start_offset=
vgmstream->ch[i].offset=start_offset+
vgmstream->interleave_block_size*i;
}
}
return vgmstream;
/* clean up anything we may have opened */
fail:
if (vgmstream) close_vgmstream(vgmstream);
return NULL;
}

View File

@ -610,12 +610,13 @@ static STREAMFILE *open_mapfile_pair(STREAMFILE* sf, int track /*, int num_track
/* EA MPF/MUS combo - used in older 7th gen games for storing interactive music */
VGMSTREAM* init_vgmstream_ea_mpf_mus_eaac(STREAMFILE* sf) {
uint32_t num_tracks, track_start, track_checksum = 0, mus_sounds, mus_stream = 0;
uint32_t num_tracks, track_start, track_checksum = 0, mus_sounds, mus_stream = 0, bnk_index = 0, bnk_sound_index = 0;
uint32_t tracks_table, samples_table, eof_offset, table_offset, entry_offset, snr_offset, sns_offset;
uint16_t num_subbanks;
uint16_t num_subbanks, index, sub_index;
uint8_t version, sub_version;
STREAMFILE *musFile = NULL;
STREAMFILE *sf_mus = NULL;
VGMSTREAM* vgmstream = NULL;
segmented_layout_data* data_s = NULL;
int i;
int target_stream = sf->stream_index, total_streams, is_ram = 0;
uint32_t(*read_u32)(off_t, STREAMFILE *);
@ -663,20 +664,8 @@ VGMSTREAM* init_vgmstream_ea_mpf_mus_eaac(STREAMFILE* sf) {
track_checksum = read_u32be(entry_offset + 0x08, sf);
is_ram = (num_subbanks != 0);
if (num_subbanks > 1) {
VGM_LOG("EA MPF: Found EAAC MPF with more than 1 RAM sub-bank.\n");
goto fail;
}
/* checks to distinguish it from older versions */
if (is_ram) {
if (read_u32(entry_offset + 0x0c, sf) != 0x00)
goto fail;
track_checksum = read_u32be(entry_offset + 0x14, sf);
} else {
if (read_u32(entry_offset + 0x0c, sf) == 0x00)
goto fail;
}
mus_stream = target_stream - 1 - track_start;
@ -685,37 +674,115 @@ VGMSTREAM* init_vgmstream_ea_mpf_mus_eaac(STREAMFILE* sf) {
}
/* open MUS file that matches this track */
musFile = open_mapfile_pair(sf, i);//, num_tracks
if (!musFile)
sf_mus = open_mapfile_pair(sf, i);//, num_tracks
if (!sf_mus)
goto fail;
if (read_u32be(0x00, musFile) != track_checksum)
goto fail;
/* sample offsets table is still there but it just holds SNS offsets, we only need it for RAM sound indexes */
/* 0x00 - offset/index, 0x04 - duration (in milliseconds) */
sns_offset = read_u32(samples_table + (target_stream - 1) * 0x08 + 0x00, sf);
if (is_ram) {
bnk_sound_index = (sns_offset & 0x0000FFFF);
bnk_index = (sns_offset & 0xFFFF0000) >> 16;
if (bnk_index != 0) {
/* HACK: open proper .mus now since open_mapfile_pair doesn't let us adjust the name */
char filename[PATH_LIMIT], basename[PATH_LIMIT], ext[32];
int basename_len;
STREAMFILE* sf_temp;
get_streamfile_basename(sf_mus, basename, PATH_LIMIT);
basename_len = strlen(basename);
get_streamfile_ext(sf_mus, ext, sizeof(ext));
/* strip off 0 at the end */
basename[basename_len - 1] = '\0';
/* append bank index to the name */
snprintf(filename, PATH_LIMIT, "%s%u.%s", basename, bnk_index, ext);
sf_temp = open_streamfile_by_filename(sf_mus, filename);
if (!sf_temp) goto fail;
close_streamfile(sf_mus);
sf_mus = sf_temp;
}
track_checksum = read_u32be(entry_offset + 0x14 + bnk_index * 0x10, sf);
if (track_checksum && read_u32be(0x00, sf_mus) != track_checksum)
goto fail;
} else {
if (track_checksum && read_u32be(0x00, sf_mus) != track_checksum)
goto fail;
}
/* sample offsets table is still there but it just holds SNS offsets, it's of little use to us */
/* MUS file has a header, however */
if (sub_version == 2) {
if (read_u32(0x04, musFile) != 0x00)
if (read_u32(0x04, sf_mus) != 0x00)
goto fail;
/*
* 0x00: flags? index?
* 0x00: index
* 0x02: sub-index
* 0x04: SNR offset
* 0x08: SNS offset (contains garbage for RAM sounds)
*/
table_offset = 0x08;
entry_offset = table_offset + mus_stream * 0x0c;
snr_offset = read_u32(entry_offset + 0x04, musFile);
sns_offset = read_u32(entry_offset + 0x08, musFile);
if (is_ram) {
int ram_segments;
/* find number of parts for this node */
for (i = 0; ; i++) {
entry_offset = table_offset + (bnk_sound_index + i) * 0x0c;
index = read_u16(entry_offset + 0x00, sf_mus);
sub_index = read_u16(entry_offset + 0x02, sf_mus);
if (index == 0xffff) /* EOF check */
goto fail;
entry_offset += 0x0c;
if (read_u16(entry_offset + 0x00, sf_mus) != index ||
read_u16(entry_offset + 0x02, sf_mus) != sub_index + 1)
break;
}
ram_segments = i + 1;
/* init layout */
data_s = init_layout_segmented(ram_segments);
if (!data_s) goto fail;
for (i = 0; i < ram_segments; i++) {
entry_offset = table_offset + (bnk_sound_index + i) * 0x0c;
snr_offset = read_u32(entry_offset + 0x04, sf_mus);
data_s->segments[i] = init_vgmstream_eaaudiocore_header(sf_mus, sf_mus,
snr_offset, 0,
meta_EA_SNR_SNS, 0);
if (!data_s->segments[i]) goto fail;
}
/* setup segmented VGMSTREAMs */
if (!setup_layout_segmented(data_s)) goto fail;
vgmstream = allocate_segmented_vgmstream(data_s, 0, 0, 0);
} else {
entry_offset = table_offset + mus_stream * 0x0c;
snr_offset = read_u32(entry_offset + 0x04, sf_mus);
sns_offset = read_u32(entry_offset + 0x08, sf_mus);
vgmstream = init_vgmstream_eaaudiocore_header(sf_mus, sf_mus,
snr_offset, sns_offset,
meta_EA_SNR_SNS, 0);
}
} else if (sub_version == 3) {
/* number of samples is always little endian */
mus_sounds = read_u32le(0x04, musFile);
mus_sounds = read_u32le(0x04, sf_mus);
if (mus_stream >= mus_sounds)
goto fail;
if (is_ram) {
/* not seen so far */
VGM_LOG("Found RAM SNR in MPF v5.3.\n");
VGM_LOG("Found RAM track in MPF v5.3.\n");
goto fail;
}
@ -731,23 +798,27 @@ VGMSTREAM* init_vgmstream_ea_mpf_mus_eaac(STREAMFILE* sf) {
*/
table_offset = 0x28;
entry_offset = table_offset + mus_stream * 0x1c;
snr_offset = read_u32(entry_offset + 0x08, musFile) * 0x10;
sns_offset = read_u32(entry_offset + 0x0c, musFile) * 0x80;
snr_offset = read_u32(entry_offset + 0x08, sf_mus) * 0x10;
sns_offset = read_u32(entry_offset + 0x0c, sf_mus) * 0x80;
vgmstream = init_vgmstream_eaaudiocore_header(sf_mus, sf_mus,
snr_offset, sns_offset,
meta_EA_SNR_SNS, 0);
} else {
goto fail;
}
vgmstream = init_vgmstream_eaaudiocore_header(musFile, musFile, snr_offset, sns_offset, meta_EA_SNR_SNS, 0);
if (!vgmstream)
goto fail;
vgmstream->num_streams = total_streams;
get_streamfile_filename(musFile, vgmstream->stream_name, STREAM_NAME_SIZE);
close_streamfile(musFile);
get_streamfile_filename(sf_mus, vgmstream->stream_name, STREAM_NAME_SIZE);
close_streamfile(sf_mus);
return vgmstream;
fail:
close_streamfile(musFile);
close_streamfile(sf_mus);
free_layout_segmented(data_s);
return NULL;
}
@ -810,15 +881,16 @@ fail:
/* EA Harmony Sample Bank - used in 8th gen EA Sports games */
VGMSTREAM* init_vgmstream_ea_sbr_harmony(STREAMFILE* sf) {
uint64_t base_offset, sound_offset, offset, prev_offset;
uint32_t chunk_id, data_offset, table_offset, dset_offset, set_values, set_sounds, sound_table_offset;
uint32_t dset_id, dset_offset, num_values, num_fields, field_id,
data_offset, table_offset, set_sounds, sound_table_offset;
int16_t flag;
uint16_t num_dsets;
uint8_t set_type, offset_size;
uint32_t i, j;
char sound_name[STREAM_NAME_SIZE];
STREAMFILE *sf_sbs = NULL, *sf_data = NULL;
VGMSTREAM* vgmstream = NULL;
int target_stream = sf->stream_index, total_sounds, local_target, is_streamed = 0;
int i, j;
uint64_t(*read_u64)(off_t, STREAMFILE *);
uint32_t(*read_u32)(off_t, STREAMFILE*);
uint16_t(*read_u16)(off_t, STREAMFILE*);
@ -857,27 +929,26 @@ VGMSTREAM* init_vgmstream_ea_sbr_harmony(STREAMFILE* sf) {
if (read_u32(dset_offset, sf) != 0x44534554) /* "DSET" */
goto fail;
set_values = read_u32(dset_offset + 0x38, sf);
dset_id = read_u32(dset_offset + 0x08, sf);
num_values = read_u32(dset_offset + 0x38, sf);
num_fields = read_u32(dset_offset + 0x3c, sf);
local_target = target_stream - total_sounds - 1;
dset_offset += 0x48;
/* Find RAM or OFF chunk */
while(1) {
chunk_id = read_u32(dset_offset, sf);
if (chunk_id == 0x2E52414D) { /* ".RAM" */
/* find RAM or OFF field */
for (j = 0; j < num_fields; j++) {
field_id = read_u32(dset_offset, sf);
if (field_id == 0x2E52414D || /* ".RAM" */
field_id == 0x2E4F4646) { /* ".OFF" */
break;
} else if (chunk_id == 0x2E4F4646) { /* ".OFF" */
break;
} else if (chunk_id == 0x2E4C4452 || /* ".LDR" */
chunk_id == 0x2E4F424A || /* ".OBJ" */
chunk_id == 0x2E445552 || /* ".DUR" */
(chunk_id & 0xFF00FFFF) == 0x2E00534C) { /* ".?SL */
dset_offset += 0x18;
} else {
goto fail;
}
dset_offset += 0x18;
}
if (j == num_fields)
goto fail;
/* different set types store offsets differently */
set_type = read_u8(dset_offset + 0x05, sf);
@ -894,7 +965,7 @@ VGMSTREAM* init_vgmstream_ea_sbr_harmony(STREAMFILE* sf) {
flag = (int16_t)read_u16(dset_offset + 0x06, sf);
base_offset = read_u64(dset_offset + 0x08, sf);
set_sounds = set_values;
set_sounds = num_values;
total_sounds += set_sounds;
if (local_target < 0 || local_target >= set_sounds)
continue;
@ -908,7 +979,7 @@ VGMSTREAM* init_vgmstream_ea_sbr_harmony(STREAMFILE* sf) {
set_sounds = 0;
prev_offset = UINT64_MAX;
for (j = 0; j < set_values; j++) {
for (j = 0; j < num_values; j++) {
if (offset_size == 0x01) {
offset = read_u8(sound_table_offset + 0x01 * j, sf);
} else if (offset_size == 0x02) {
@ -955,7 +1026,7 @@ VGMSTREAM* init_vgmstream_ea_sbr_harmony(STREAMFILE* sf) {
set_sounds = 0;
prev_offset = UINT64_MAX;
for (j = 0; j < set_values; j++) {
for (j = 0; j < num_values; j++) {
offset = read_u64(sound_table_offset + 0x08 * j, sf);
if (sound_offset != prev_offset) {
@ -973,11 +1044,11 @@ VGMSTREAM* init_vgmstream_ea_sbr_harmony(STREAMFILE* sf) {
goto fail;
}
snprintf(sound_name, STREAM_NAME_SIZE, "DSET %02d/%04d", i, local_target);
snprintf(sound_name, STREAM_NAME_SIZE, "DSET %08x/%04d", dset_id, local_target);
if (chunk_id == 0x2E52414D) { /* ".RAM" */
if (field_id == 0x2E52414D) { /* ".RAM" */
is_streamed = 0;
} else if (chunk_id == 0x2E4F4646) { /* ".OFF" */
} else if (field_id == 0x2E4F4646) { /* ".OFF" */
is_streamed = 1;
}
}
@ -1153,7 +1224,7 @@ static VGMSTREAM* init_vgmstream_eaaudiocore_header(STREAMFILE* sf_head, STREAMF
break;
case EAAC_TYPE_GIGASAMPLE: /* rarely seen [Def Jam Icon (X360)] */
header_size += 0x04;
eaac.prefetch_samples = read_32bitBE(header_offset + eaac.loop_flag ? 0x0c : 0x08, sf_head);
eaac.prefetch_samples = read_32bitBE(header_offset + (eaac.loop_flag ? 0x0c : 0x08), sf_head);
if (eaac.loop_flag && eaac.loop_start >= eaac.prefetch_samples) {
header_size += 0x04;
@ -1230,7 +1301,7 @@ static VGMSTREAM* init_vgmstream_eaaudiocore_header(STREAMFILE* sf_head, STREAMF
if (!sf) goto fail;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(eaac.channels,eaac.loop_flag);
vgmstream = allocate_vgmstream(eaac.channels, eaac.loop_flag);
if (!vgmstream) goto fail;
vgmstream->sample_rate = eaac.sample_rate;
@ -1404,8 +1475,8 @@ static VGMSTREAM* init_vgmstream_eaaudiocore_header(STREAMFILE* sf_head, STREAMF
}
#endif
#ifdef VGM_USE_FFMPEG
//case EAAC_CODEC_EAOPUSMU: /* "MSU0": Multi-Stream Opus Uncoupled (not seen) */
case EAAC_CODEC_EAOPUSM: { /* "MSO0": Multi-Stream Opus */
case EAAC_CODEC_EAOPUSM: /* "MSO0": Multi-Stream Opus [FIFA 2021 (PC)] */
case EAAC_CODEC_EAOPUSMU: { /* "MSU0": Multi-Stream Opus Uncoupled [FIFA 2022 (PC)] */
off_t offset = 0x00; // eaac.stream_offset;
off_t data_size = get_streamfile_size(sf);
opus_config cfg = {0};
@ -1427,12 +1498,11 @@ static VGMSTREAM* init_vgmstream_eaaudiocore_header(STREAMFILE* sf_head, STREAMF
else {
switch(eaac.channels) {
//case 8: cfg.coupled_count = 3; break; /* 2ch+2ch+2ch+1ch+1ch, 5 streams */
//case 6: /* 2ch+2ch+1ch+1ch, 4 streams */
//case 6: cfg.coupled_count = 2; break; /* 2ch+2ch+1ch+1ch, 4 streams */
case 4: cfg.coupled_count = 2; break; /* 2ch+2ch, 2 streams */
//case 3: /* 2ch+1ch, 2 streams */
case 2: cfg.coupled_count = 1; break; /* 2ch, 1 stream */
case 1: cfg.coupled_count = 0; break; /* 1ch, 1 stream [Madden 22 (PC)] */
default: goto fail;
default: goto fail; /* possibly: streams = Nch / 2, coupled = Nch % 2 */
}
}
@ -1779,7 +1849,7 @@ static layered_layout_data* build_layered_eaaudiocore(STREAMFILE *sf_data, eaac_
stream_size = get_streamfile_size(temp_sf);
block_size = 0x10000; /* unused */
block_count = stream_size / block_size + (stream_size % block_size ? 1 : 0);
block_count = stream_size / block_size + ((stream_size % block_size) ? 1 : 0);
/* EA adopted XMA2 when it appeared around 2006, but detection isn't so easy
* (SNS with XMA2 do exist). Decoder should work when playing XMA1 as XMA2, but

View File

@ -827,7 +827,7 @@ VGMSTREAM* init_vgmstream_ea_mpf_mus(STREAMFILE* sf) {
uint16_t num_nodes, num_subbanks = 0;
uint8_t version, sub_version, num_tracks, num_sections, num_events, num_routers, num_vars, subentry_num = 0;
int i;
int target_stream = sf->stream_index, total_streams, big_endian, is_bnk = 0;
int target_stream = sf->stream_index, total_streams, big_endian, is_ram = 0;
uint32_t(*read_u32)(off_t, STREAMFILE *);
uint16_t(*read_u16)(off_t, STREAMFILE *);
@ -984,10 +984,10 @@ VGMSTREAM* init_vgmstream_ea_mpf_mus(STREAMFILE* sf) {
if (track_start <= target_stream - 1) {
num_subbanks = read_u16(entry_offset + 0x04, sf);
track_checksum = read_u32be(entry_offset + 0x08, sf);
is_bnk = (num_subbanks != 0);
is_ram = (num_subbanks != 0);
/* checks to distinguish it from SNR/SNS version */
if (is_bnk) {
if (is_ram) {
if (read_u32(entry_offset + 0x0c, sf) == 0x00)
goto fail;
} else {
@ -1010,13 +1010,13 @@ VGMSTREAM* init_vgmstream_ea_mpf_mus(STREAMFILE* sf) {
goto fail;
if (version < 5) {
is_bnk = (read_u32be(0x00, sf_mus) == (big_endian ? EA_BNK_HEADER_BE : EA_BNK_HEADER_LE));
is_ram = (read_u32be(0x00, sf_mus) == (big_endian ? EA_BNK_HEADER_BE : EA_BNK_HEADER_LE));
}
/* 0x00 - offset/BNK index, 0x04 - duration (in milliseconds) */
sound_offset = read_u32(samples_table + (target_stream - 1) * 0x08 + 0x00, sf);
if (is_bnk) {
if (is_ram) {
/* for some reason, RAM segments are almost always split into multiple sounds (usually 4) */
off_t bnk_offset = version < 5 ? 0x00 : 0x100;
uint32_t bnk_sound_index = (sound_offset & 0x0000FFFF);
@ -1024,12 +1024,12 @@ VGMSTREAM* init_vgmstream_ea_mpf_mus(STREAMFILE* sf) {
uint32_t next_entry;
uint32_t bnk_total_sounds = read_u16(bnk_offset + 0x06, sf_mus);
int bnk_segments;
STREAMFILE *sf_bnk = sf_mus;
if (version == 5 && bnk_index != 0) {
/* HACK: open proper .mus now since open_mapfile_pair doesn't let us adjust the name */
char filename[PATH_LIMIT], basename[PATH_LIMIT], ext[32];
int basename_len;
STREAMFILE* sf_temp;
get_streamfile_basename(sf_mus, basename, PATH_LIMIT);
basename_len = strlen(basename);
@ -1039,13 +1039,13 @@ VGMSTREAM* init_vgmstream_ea_mpf_mus(STREAMFILE* sf) {
basename[basename_len - 1] = '\0';
/* append bank index to the name */
snprintf(filename, PATH_LIMIT, "%s%d.%s", basename, bnk_index, ext);
snprintf(filename, PATH_LIMIT, "%s%u.%s", basename, bnk_index, ext);
sf_bnk = open_streamfile_by_filename(sf_mus, filename);
if (!sf_bnk) goto fail;
bnk_total_sounds = read_u16(bnk_offset + 0x06, sf_bnk);
sf_temp = open_streamfile_by_filename(sf_mus, filename);
if (!sf_temp) goto fail;
bnk_total_sounds = read_u16(bnk_offset + 0x06, sf_temp);
close_streamfile(sf_mus);
sf_mus = sf_bnk;
sf_mus = sf_temp;
}
if (version == 5) {
@ -1081,10 +1081,7 @@ VGMSTREAM* init_vgmstream_ea_mpf_mus(STREAMFILE* sf) {
/* setup segmented VGMSTREAMs */
if (!setup_layout_segmented(data_s)) goto fail;
vgmstream = allocate_segmented_vgmstream(data_s, 0, 0, 0);
if (!vgmstream)
goto fail;
} else {
if (version == 5 && track_checksum && read_u32be(0x00, sf_mus) != track_checksum)
goto fail;
@ -1094,10 +1091,11 @@ VGMSTREAM* init_vgmstream_ea_mpf_mus(STREAMFILE* sf) {
goto fail;
vgmstream = parse_schl_block(sf_mus, sound_offset, 0);
if (!vgmstream)
goto fail;
}
if (!vgmstream)
goto fail;
vgmstream->num_streams = total_streams;
get_streamfile_filename(sf_mus, vgmstream->stream_name, STREAM_NAME_SIZE);
close_streamfile(sf_mus);

View File

@ -293,7 +293,7 @@ VGMSTREAM* init_vgmstream_fsb(STREAMFILE* sf) {
/* sometimes there is garbage at the end or missing bytes due to improper ripping */
vgm_asserti(fsb.base_header_size + fsb.sample_headers_size + fsb.sample_data_size != get_streamfile_size(sf),
"FSB wrong head/data_size found (expected 0x%x vs 0x%x)\n",
fsb.base_header_size + fsb.sample_headers_size + fsb.sample_data_size, get_streamfile_size(sf));
fsb.base_header_size + fsb.sample_headers_size + fsb.sample_data_size, (uint32_t)get_streamfile_size(sf));
/* autodetect unwanted loops */
{

View File

@ -7,16 +7,16 @@
VGMSTREAM* init_vgmstream_fsb_encrypted(STREAMFILE* sf) {
VGMSTREAM* vgmstream = NULL;
/* ignore non-encrypted FSB */
if ((read_u32be(0x00,sf) & 0xFFFFFF00) == get_id32be("FSB\0"))
goto fail;
/* checks */
/* .fsb: standard
* .fsb.xen: various Guitar Hero (X360/PC) */
if (!check_extensions(sf, "fsb,xen"))
goto fail;
/* ignore non-encrypted FSB */
if ((read_u32be(0x00,sf) & 0xFFFFFF00) == 0x46534200) /* "FSB\0" */
goto fail;
/* try fsbkey + all combinations of FSB4/5 and decryption algorithms */
{

View File

@ -32,13 +32,13 @@ static const uint8_t key_mkx[] = { 0x39,0x39,0x36,0x31,0x36,0x34,0x42,0x35,0x46,
/* Xian Xia Chuan (PC) */ //"gat@tcqs2010"
static const uint8_t key_xxc[] = { 0x67,0x61,0x74,0x40,0x74,0x63,0x71,0x73,0x32,0x30,0x31,0x30 };
/* Mirror War Reincarnation of Holiness (PC) */ //"logicsounddesignmwsdev"
/* Mirror War: Reincarnation of Holiness (PC) */ //"logicsounddesignmwsdev"
static const uint8_t key_mwr[] = { 0x6C,0x6F,0x67,0x69,0x63,0x73,0x6F,0x75,0x6E,0x64,0x64,0x65,0x73,0x69,0x67,0x6E,0x6D,0x77,0x73,0x64,0x65,0x76 };
/* Need for Speed Shift 2 Unleashed (PC demo?) */ //"p&oACY^c4LK5C2v^x5nIO6kg5vNH$tlj"
static const uint8_t key_n2u[] = { 0x70,0x26,0x6F,0x41,0x43,0x59,0x5E,0x63,0x34,0x4C,0x4B,0x35,0x43,0x32,0x76,0x5E,0x78,0x35,0x6E,0x49,0x4F,0x36,0x6B,0x67,0x35,0x76,0x4E,0x48,0x24,0x74,0x6C,0x6A };
/* Critter Crunch, Superbrothers: Sword & Sworcery */ //"j1$Mk0Libg3#apEr42mo"
/* Critter Crunch (PC), Superbrothers: Sword & Sworcery (PC) */ //"j1$Mk0Libg3#apEr42mo"
static const uint8_t key_ccr[] = { 0x6A,0x31,0x24,0x4D,0x6B,0x30,0x4C,0x69,0x62,0x67,0x33,0x23,0x61,0x70,0x45,0x72,0x34,0x32,0x6D,0x6F };
/* Cyphers */ //"@kdj43nKDN^k*kj3ndf02hd95nsl(NJG"
@ -50,7 +50,7 @@ static const uint8_t key_xdz[] = { 0x58,0x69,0x61,0x79,0x75,0x77,0x75,0x36,0x39,
/* Ji Feng Zhi Ren / Kritika Online */ //"kri_tika_5050_"
static const uint8_t key_jzz[] = { 0x6B,0x72,0x69,0x5F,0x74,0x69,0x6B,0x61,0x5F,0x35,0x30,0x35,0x30,0x5F };
/* Invisible Inc. */ //"mint78run52"
/* Invisible Inc. (PC?) */ //"mint78run52"
static const uint8_t key_inv[] = { 0x6D,0x69,0x6E,0x74,0x37,0x38,0x72,0x75,0x6E,0x35,0x32 };
/* Guitar Hero 3 */ //"5atu6w4zaw"
@ -83,12 +83,18 @@ static const uint8_t key_wrb[] = { 0x46,0x58,0x6E,0x54,0x66,0x66,0x47,0x4A,0x39,
/* Bubble Fighter (PC) */ //"qjvkeoqkrdhkdckd"
static const uint8_t key_bbf[] = { 0x71,0x6A,0x76,0x6B,0x65,0x6F,0x71,0x6B,0x72,0x64,0x68,0x6B,0x64,0x63,0x6B,0x64 };
/* Fall Guys (PC)-update */ //"p@4_ih*srN:UJk&8"
static const uint8_t key_fgs[] = { 0x70,0x40,0x34,0x5F,0x69,0x68,0x2A,0x73,0x72,0x4E,0x3A,0x55,0x4A,0x6B,0x26,0x38 };
/* Fall Guys (PC) update ~2021-11 */ //"p@4_ih*srN:UJk&8"
static const uint8_t key_fg1[] = { 0x70,0x40,0x34,0x5F,0x69,0x68,0x2A,0x73,0x72,0x4E,0x3A,0x55,0x4A,0x6B,0x26,0x38 };
/* Fall Guys (PC) update ~2022-07 */ //",&.XZ8]fLu%caPF+"
static const uint8_t key_fg2[] = { 0x2c,0x26,0x2e,0x58,0x5a,0x38,0x5d,0x66,0x4c,0x75,0x25,0x63,0x61,0x50,0x46,0x2b };
/* Achilles: Legends Untold (PC) */ //"Achilles_0_15_DpG"
static const uint8_t key_alu[] = { 0x41,0x63,0x68,0x69,0x6C,0x6C,0x65,0x73,0x5F,0x30,0x5F,0x31,0x35,0x5F,0x44,0x70,0x47 };
/* Cult of the Lamb Demo (PC) */ //"4FB8CC894515617939F4E1B7D50972D27213B8E6"
static const uint8_t key_col[] = { 0x34,0x46,0x42,0x38,0x43,0x43,0x38,0x39,0x34,0x35,0x31,0x35,0x36,0x31,0x37,0x39,0x33,0x39,0x46,0x34,0x45,0x31,0x42,0x37,0x44,0x35,0x30,0x39,0x37,0x32,0x44,0x32,0x37,0x32,0x31,0x33,0x42,0x38,0x45,0x36 };
// Unknown:
// - Battle: Los Angeles
// - Guitar Hero: Warriors of Rock, DJ hero FSB
@ -105,8 +111,7 @@ typedef struct {
static const fsbkey_info fsbkey_list[] = {
{ 0,0, sizeof(key_dj2),key_dj2 },
{ 0,0, sizeof(key_dfp),key_dfp },//FSB4
{ 1,0, sizeof(key_dfp),key_dfp },//untested
{ 1,1, sizeof(key_dfp),key_dfp },//untested
{ 1,0, sizeof(key_dfp),key_dfp },//FSB5
{ 1,0, sizeof(key_npp),key_npp },//FSB5
{ 1,0, sizeof(key_sms),key_sms },//FSB5
{ 1,0, sizeof(key_gfs),key_gfs },//FSB5
@ -118,14 +123,9 @@ static const fsbkey_info fsbkey_list[] = {
{ 0,1, sizeof(key_xxc),key_xxc },//untested
{ 1,0, sizeof(key_xxc),key_xxc },//untested
{ 1,1, sizeof(key_xxc),key_xxc },//untested
{ 0,0, sizeof(key_mwr),key_mwr },//untested
{ 0,1, sizeof(key_mwr),key_mwr },//untested
{ 1,0, sizeof(key_mwr),key_mwr },//untested
{ 1,1, sizeof(key_mwr),key_mwr },//untested
{ 1,0, sizeof(key_mwr),key_mwr },//FSB5
{ 0,0, sizeof(key_n2u),key_n2u },//untested
{ 0,1, sizeof(key_n2u),key_n2u },//untested
{ 1,0, sizeof(key_n2u),key_n2u },//untested
{ 1,1, sizeof(key_n2u),key_n2u },//untested
{ 0,0, sizeof(key_ccr),key_ccr },//untested
{ 0,1, sizeof(key_ccr),key_ccr },//untested
{ 1,0, sizeof(key_ccr),key_ccr },//untested
@ -162,8 +162,10 @@ static const fsbkey_info fsbkey_list[] = {
{ 0,1, sizeof(key_ghm),key_ghm },// FSB4
{ 1,0, sizeof(key_wrb),key_wrb },// FSB5
{ 0,0, sizeof(key_bbf),key_bbf },// FSB4
{ 1,0, sizeof(key_fgs),key_fgs },// FSB5
{ 1,0, sizeof(key_fg1),key_fg1 },// FSB5
{ 1,0, sizeof(key_fg2),key_fg2 },// FSB5
{ 1,0, sizeof(key_alu),key_alu },// FSB5
{ 1,0, sizeof(key_col),key_col },// FSB5
};
static const int fsbkey_list_count = sizeof(fsbkey_list) / sizeof(fsbkey_list[0]);

View File

@ -446,6 +446,8 @@ static const hcakey_info hcakey_list[] = {
{0xd7359fa9ec49e2c2}, //music_0110024
{0xd91127e6c2f22539}, //music_0110025
{0x120db58e6c835175}, //music_0110026
{0x3613e2a7fdfc0784}, //music_0110027
{0xc239d244b6d3b722}, //music_0110028
{0xfb647d074e53fab6}, //music_0120001
{0xc24049b9f7ed3105}, //music_0120002
{0xdc128f2fd48bf4b}, //music_0120003
@ -480,6 +482,8 @@ static const hcakey_info hcakey_list[] = {
{0x6dc5ff77263450a5}, //music_0210012
{0x1dca436afdd18d9}, //music_0210013
{0xaecee65d0f181d3b}, //music_0210014
{0x2822bba0a5c4f18c}, //music_0210015
{0xff579d3fcfa8453a}, //music_0210016
{0x15bb78c31db0a0b6}, //music_0220001
{0x59b1257242c40109}, //music_0220002
{0xdb402bd08d522f34}, //music_0220003
@ -499,6 +503,7 @@ static const hcakey_info hcakey_list[] = {
{0xd23bdacd616fc4c9}, //music_0220018
{0xfceaa73248868ec5}, //music_0220019
{0x3b6b0023a2dd8c3d}, //music_0220020
{0x318f65f564daa549}, //music_0220021
{0x6a15a9610d10d210}, //music_0310001
{0x57111c24801b44a1}, //music_0310002
{0x40443974a0a86b8b}, //music_0310003
@ -515,6 +520,7 @@ static const hcakey_info hcakey_list[] = {
{0x227b85948bb3d899}, //music_0310014
{0xfff671f0ddb660b1}, //music_0310015
{0x22e33db9b5625a96}, //music_0310016
{0xb78070802414de7a}, //music_0310017
{0xb921c3992807dadd}, //music_0320001
{0x38ad99a045dc971f}, //music_0320002
{0xf616642579ba5850}, //music_0320003
@ -546,6 +552,7 @@ static const hcakey_info hcakey_list[] = {
{0xa144f6d7de02e000}, //music_0410012
{0x1da4370c9c20319c}, //music_0410013
{0xd8cdd53589ad3634}, //music_0410014
{0x88007190e0bfa1ce}, //music_0410015
{0x5d1f3fdbbb036f8d}, //music_0420001
{0xc04264e8f34ad5c0}, //music_0420002
{0x8f0e96b4f71f724f}, //music_0420003
@ -581,6 +588,7 @@ static const hcakey_info hcakey_list[] = {
{0xda4ce04dbda1bd7e}, //music_0510016
{0x7878df60f0549c4}, //music_0510017
{0x8e5b7068022828e0}, //music_0510018
{0xb069a5c5e2d93edf}, //music_0510019
{0x15f82c1617013c36}, //music_0520001
{0xc7da8e6f0e2fe399}, //music_0520002
{0xe350bffcdc9cb686}, //music_0520003
@ -612,6 +620,8 @@ static const hcakey_info hcakey_list[] = {
{0x7081f083ac3d6f0a}, //music_0610013
{0xfcfa4dbd1ec6cfcb}, //music_0610014
{0x750ab11487f9d6e8}, //music_0610015
{0xd22171fda74c5615}, //music_0610016
{0x3aa02e0a37543b5c}, //music_0610017
{0x8258ddd6a1d0849b}, //music_0620001
{0x1dd21a1244ca12f1}, //music_0620002
{0xfdec74b23d8b494b}, //music_0620003
@ -710,7 +720,9 @@ static const hcakey_info hcakey_list[] = {
{0xdfe954b617357381}, //music_5030053
{0x52c5dfb61fe4c87a}, //music_5030054
{0x3ebbccab07c9a9ba}, //music_5030055
{0x50a0167818f4d51b}, //music_5030056
{0xceaed3996196281b}, //music_5030057
{0xe0590c04ec4906f1}, //music_5030058
{0x400b11e930008a58}, //music_5030059
{0x15aaecc4725a5f70}, //music_5030060
{0x7a5e0865ba8cafa7}, //music_5030061
@ -728,6 +740,14 @@ static const hcakey_info hcakey_list[] = {
{0x5ed84aea8ad0d05b}, //music_5030073
{0xf5074d6db1cec319}, //music_5030074
{0xae83f69d4d7ff064}, //music_5030075
{0x29a3d7b03fe66d8f}, //music_5030076
{0xb30288656e565401}, //music_5030077
{0x5a420864cd1fa3fb}, //music_5030078
{0xe1c992cc05d8a203}, //music_5030079
{0x79ad5329b9b46034}, //music_5030080
{0x1dd99ac6f1a07f00}, //music_5030081
{0x2fd298ade03f7f0f}, //music_5030082
{0xbeb0df818f88b99c}, //music_5030083
{0x444dda6d55d76095}, //music_5040001
{0xcbf4f1324081e0a6}, //music_5040002
{0xf1db3c1d9542063a}, //music_5040003
@ -892,6 +912,15 @@ static const hcakey_info hcakey_list[] = {
{0x1be3e8255dde31e3}, //music_5050147
{0x58b735f1a68c9a81}, //music_5050148
{0x5b3cb281d89019db}, //music_5050149
{0x2b1a0269a3d890d4}, //music_5050151
{0x74e058fd628364c9}, //music_5050152
{0x3ade68d4857395c0}, //music_5050153
{0x68f7f556af1c2fc6}, //music_5050154
{0x1838300a8bb9b8ff}, //music_5050158
{0xffb4afe38bb4a3dd}, //music_5050159
{0x22cfb3e29832a25a}, //music_5050160
{0xa86bbfe35949b53e}, //music_5050161
{0xa86bbfe35949b53e}, //music_5050163
{0x52c250eade92393b}, //music_9010001
{0xf66e6bb5b0599b07}, //music_9010002
{0x8582b5a60dbbf948}, //music_9010003
@ -991,6 +1020,11 @@ static const hcakey_info hcakey_list[] = {
// Pachislot Gyakuten Saiban (iOS/Android)
{2220022040477322}, // 0007E319291DBE8A
// Alice Fiction (Android)
{112089817}, // 0000000006AE5AD9
// Taiko no Tatsujin: Rhythm Festival Demo (Switch)
{52539816150204134}, // 00baa8af36327ee6
};
#endif/*_HCA_KEYS_H_*/

View File

@ -0,0 +1,143 @@
#include "meta.h"
#include "../coding/coding.h"
/* HXD - from Tecmo games [Tokobot Plus (PS2), Fatal Frame 2/3 (PS2), Gallop Racer 2004 (PS2)] */
VGMSTREAM* init_vgmstream_hxd(STREAMFILE* sf) {
VGMSTREAM* vgmstream = NULL;
STREAMFILE* sf_body = NULL;
uint32_t stream_offset, header_size, stream_size, interleave, loop_start, loop_end;
int channels, loop_flag, bank, sample_rate;
int total_subsongs, target_subsong = sf->stream_index;
/* checks */
if (!is_id32be(0x00,sf, "\0DXH"))
goto fail;
/* .hxd: actual extension (filenames in companion files/exe) */
if (!check_extensions(sf, "hxd"))
goto fail;
/* 0x04: version? (0x1000) */
total_subsongs = read_u32le(0x08,sf);
bank = read_u32le(0x0c,sf);
header_size = read_u32le(0x10,sf);
interleave = read_u32le(0x14,sf); /* 0 in banks */
/* 0x18-1c: null */
/* Reject incorrectly ripped files, as .hxd is the header and data is always separate.
* Rips with header+data pasted were allowed before, but since bigfiles may store
* data first then header, audio could play wrong for no apparent reason. */
if (header_size != get_streamfile_size(sf))
goto fail;
/* .hxd has 2 modes, banks with N subsongs or bgm with N channels. In both cases
* there is header info per stream, though bgm just repeats values. */
if (bank) {
channels = 1;
}
else {
channels = total_subsongs; /* seen 1/2 */
total_subsongs = 1;
}
if (target_subsong == 0) target_subsong = 1;
if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail;
{
uint32_t info_offset = 0x20 + (target_subsong - 1) * 0x1c;
uint32_t flags;
sample_rate = read_s32le(info_offset + 0x00,sf);
stream_offset = read_u32le(info_offset + 0x04,sf);
/* 0x08: pitch? (44100=0x0EB3, 32000=0x0AAA, 22050=0759, 16000=0x5505, etc) */
/* 0x0a: volume? (usually ~0x64, up to ~0x7F) */
/* 0x0c: config? (pan, etc?) */
flags = read_u16le(info_offset + 0x10,sf);
/* 0x12: ? (seen in FF2 XB) */
loop_start = read_u32le(info_offset + 0x14,sf) * 0x20;
loop_end = read_u32le(info_offset + 0x18,sf) * 0x20;
/* flags:
* - 0x20: loop flag
* - 0x10: ? (seen in banks)
* - 0x02: ? (common in streams, not always)
* - 0x01: ? (sometimes in streams) */
loop_flag = flags & 0x20;
/* different games use different combos */
if (bank) {
sf_body = open_streamfile_by_ext(sf, "bd"); /* Gallop Racer */
if (!sf_body) {
sf_body = open_streamfile_by_ext(sf, "str"); /* just in case */
}
if (!sf_body) goto fail;
}
else {
sf_body = open_streamfile_by_ext(sf, "str"); /* Fatal Frame 2/3, Gallop Racer */
if (!sf_body) {
sf_body = open_streamfile_by_ext(sf, "at3"); /* Tokobot Plus (still ADPCM) */
}
if (!sf_body) goto fail;
}
/* size is not in the header (probably just leaves it to PS-ADPCM's EOF markets) */
if (bank && target_subsong < total_subsongs) {
/* find next usable offset (sometimes offsets repeat) */ //TODO: meh
int i;
uint32_t next_offset = 0;
for (i = target_subsong; i < total_subsongs; i++) {
next_offset = read_u32le(0x20 + i * 0x1c + 0x04,sf);
if (next_offset > stream_offset)
break;
}
if (i == total_subsongs)
next_offset = get_streamfile_size(sf_body);
if (!next_offset)
goto fail;
stream_size = next_offset - stream_offset;
}
else {
stream_size = get_streamfile_size(sf_body) - stream_offset;
}
}
/* Xbox versions of Tecmo games use RIFF (music) or WBND/.xwb (sfx) in the body. Probably a quick
* hack since they reuse extensions like .pss for movies too, for now reject. */
if (read_u32be(0x00, sf_body) != 0)
goto fail;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channels, loop_flag);
if (!vgmstream) goto fail;
vgmstream->meta_type = meta_HXD;
vgmstream->sample_rate = sample_rate;
vgmstream->num_streams = total_subsongs;
vgmstream->stream_size = stream_size;
vgmstream->num_samples = ps_bytes_to_samples(stream_size, channels);
vgmstream->loop_start_sample = ps_bytes_to_samples(loop_start, channels);
vgmstream->loop_end_sample = ps_bytes_to_samples(loop_end, channels);
if (!vgmstream->loop_end_sample)
vgmstream->loop_end_sample = vgmstream->num_samples;
vgmstream->coding_type = coding_PSX;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = interleave;
if (!vgmstream_open_stream(vgmstream, sf_body, stream_offset))
goto fail;
close_streamfile(sf_body);
return vgmstream;
fail:
close_streamfile(sf_body);
close_vgmstream(vgmstream);
return NULL;
}

View File

@ -240,7 +240,7 @@ VGMSTREAM * init_vgmstream_acm(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_ps2_kces(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_ps2_dxh(STREAMFILE * streamFile);
VGMSTREAM* init_vgmstream_hxd(STREAMFILE* sf);
VGMSTREAM * init_vgmstream_vsv(STREAMFILE * streamFile);
@ -255,8 +255,6 @@ VGMSTREAM * init_vgmstream_ps2_rkv(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_ps2_vas(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_ps2_vas_container(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_ps2_tec(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_ps2_enth(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_sdt(STREAMFILE * streamFile);
@ -265,8 +263,6 @@ VGMSTREAM * init_vgmstream_aix(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_ngc_tydsp(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_capdsp(STREAMFILE * streamFile);
VGMSTREAM* init_vgmstream_wvs_xbox(STREAMFILE* sf);
VGMSTREAM* init_vgmstream_wvs_ngc(STREAMFILE* sf);
@ -993,4 +989,6 @@ VGMSTREAM* init_vgmstream_bw_riff_mp3(STREAMFILE* sf);
VGMSTREAM* init_vgmstream_sndz(STREAMFILE* sf);
VGMSTREAM* init_vgmstream_vab(STREAMFILE* sf);
#endif /*_META_H*/

View File

@ -91,7 +91,7 @@ static int read_dsp_header_endian(struct dsp_header *header, off_t offset, STREA
if (header->channels > 64) /* arbitrary max */
header->channels = 0;
if (header->block_size > 0x100000)
if (header->block_size >= 0xF000) /* same, 16b (usually 0) */
header->block_size = 0;
return 1;

View File

@ -184,8 +184,9 @@ static VGMSTREAM* _init_vgmstream_ogg_vorbis_common(STREAMFILE* sf) {
* .acm: Planescape Torment Enhanced Edition (PC)
* .sod: Zone 4 (PC)
* .msa: Metal Slug Attack (Mobile)
* .aif/laif/aif-Loop: Psychonauts (PC) raw extractions (named) */
if (check_extensions(sf,"ogg,logg,adx,rof,acm,sod,msa,aif,laif,aif-Loop")) {
* .aif/laif/aif-Loop: Psychonauts (PC) raw extractions (named)
* .bin/lbin: Devil May Cry 3: Special Edition (PC) */
if (check_extensions(sf,"ogg,logg,adx,rof,acm,sod,msa,aif,laif,aif-Loop,bin,lbin")) {
is_ogg = 1;
} else if (check_extensions(sf,"um3")) {
is_um3 = 1;

View File

@ -1,76 +0,0 @@
#include "meta.h"
#include "../util.h"
/* DXH (from Tokobot Plus - Mysteries of the Karakuri) */
VGMSTREAM * init_vgmstream_ps2_dxh(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
char filename[PATH_LIMIT];
off_t start_offset;
int loop_flag = 0;
int channel_count;
/* check extension, case insensitive */
streamFile->get_name(streamFile,filename,sizeof(filename));
if (strcasecmp("dxh",filename_extension(filename))) goto fail;
/* check header */
if (read_32bitBE(0x00,streamFile) != 0x00445848) /* 0\DXH" */
goto fail;
loop_flag = (read_32bitLE(0x50,streamFile)!=0);
channel_count = read_32bitLE(0x08,streamFile);
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail;
/* fill in the vital statistics */
start_offset = 0x800;
vgmstream->channels = channel_count;
vgmstream->sample_rate = read_32bitLE(0x20,streamFile);
if (read_32bitBE(0x54,streamFile) == 0) {
/* if (loop_flag) { */
vgmstream->loop_start_sample = 0;
vgmstream->loop_end_sample = get_streamfile_size(streamFile)*28/16/channel_count;
vgmstream->num_samples = get_streamfile_size(streamFile)*28/16/channel_count;
/* } */
} else {
if (loop_flag) {
vgmstream->loop_start_sample = (read_32bitLE(0x50,streamFile)*0x20)*28/16/channel_count;
vgmstream->loop_end_sample = (read_32bitLE(0x54,streamFile)*0x20)*28/16/channel_count;
vgmstream->num_samples = (read_32bitLE(0x54,streamFile)*0x20)*28/16/channel_count;
}
}
vgmstream->coding_type = coding_PSX;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = read_32bitLE(0x14,streamFile);
vgmstream->meta_type = meta_PS2_DXH;
/* open the file for reading */
{
int i;
STREAMFILE * file;
file = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE);
if (!file) goto fail;
for (i=0;i<channel_count;i++) {
vgmstream->ch[i].streamfile = file;
vgmstream->ch[i].channel_start_offset=
vgmstream->ch[i].offset=start_offset+
vgmstream->interleave_block_size*i;
}
}
return vgmstream;
/* clean up anything we may have opened */
fail:
if (vgmstream) close_vgmstream(vgmstream);
return NULL;
}

View File

@ -1,93 +0,0 @@
#include "meta.h"
#include "../util.h"
/* TEC (from TECMO games) */
/* probably TECMO Vag Stream */
VGMSTREAM * init_vgmstream_ps2_tec(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
char filename[PATH_LIMIT];
int loop_flag;
int channel_count;
int current_chunk;
off_t start_offset;
int dataBuffer = 0;
int Founddata = 0;
/* check extension, case insensitive */
streamFile->get_name(streamFile,filename,sizeof(filename));
if (strcasecmp("tec",filename_extension(filename))) goto fail;
loop_flag = 0;
channel_count = 2;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail;
/* fill in the vital statistics */
start_offset = 0x0;
vgmstream->channels = channel_count;
vgmstream->sample_rate = 44100;
vgmstream->coding_type = coding_PSX_badflags;
vgmstream->num_samples = get_streamfile_size(streamFile)*28/16/channel_count;
if (loop_flag) {
vgmstream->loop_start_sample = 0;
vgmstream->loop_end_sample = get_streamfile_size(streamFile)*28/16/channel_count;
}
// Check the first frame header (should be always zero)
if ((uint8_t)(read_8bit(0x00,streamFile) != 0x0))
goto fail;
// Scan for Interleave
{
current_chunk = 16;
while (!Founddata && current_chunk < 65536) {
dataBuffer = (uint8_t)(read_8bit(current_chunk,streamFile));
if (dataBuffer == 0x0) { /* "0x0" */
Founddata = 1;
break;
}
current_chunk = current_chunk + 16;
}
}
// Cancel if we can't find an interleave
if (Founddata == 0) {
goto fail;
} else if (Founddata == 1) {
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = current_chunk;
}
// Cancel if the first flag isn't invalid/bad
if ((uint8_t)(read_8bit(0x01,streamFile) == 0x0))
goto fail;
if ((uint8_t)(read_8bit(0x01+current_chunk,streamFile) == 0x0))
goto fail;
vgmstream->meta_type = meta_PS2_TEC;
/* open the file for reading */
{
int i;
STREAMFILE * file;
file = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE);
if (!file) goto fail;
for (i=0;i<channel_count;i++) {
vgmstream->ch[i].streamfile = file;
vgmstream->ch[i].channel_start_offset=
vgmstream->ch[i].offset=start_offset+
vgmstream->interleave_block_size*i;
}
}
return vgmstream;
/* clean up anything we may have opened */
fail:
if (vgmstream) close_vgmstream(vgmstream);
return NULL;
}

View File

@ -33,13 +33,10 @@ VGMSTREAM * init_vgmstream_ps_headerless(STREAMFILE *streamFile) {
/* checks
* .mib: common, but many ext-less files are renamed to this.
* .mi4: fake .mib to force another sample rate
* .vb: Tantei Jinguuji Saburo - Mikan no Rupo (PS1)
* */
* .mi4: fake .mib to force another sample rate */
streamFile->get_name(streamFile,filename,sizeof(filename));
if (strcasecmp("mib",filename_extension(filename)) &&
strcasecmp("mi4",filename_extension(filename)) &&
strcasecmp("vb",filename_extension(filename)))
strcasecmp("mi4",filename_extension(filename)))
goto fail;
/* test if raw PS-ADPCM */
@ -126,10 +123,6 @@ VGMSTREAM * init_vgmstream_ps_headerless(STREAMFILE *streamFile) {
if(channel_count==0)
channel_count=1;
// force no loop
if(!strcasecmp("vb",filename_extension(filename)))
loopStart=0;
// Calc Loop Points & Interleave ...
if(loopStartPointsCount>=2) {
// can't get more then 0x10 loop point !
@ -191,9 +184,6 @@ VGMSTREAM * init_vgmstream_ps_headerless(STREAMFILE *streamFile) {
channel_count=newChannelCount;
}
if (!strcasecmp("vb",filename_extension(filename)))
channel_count=1;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,(loopEnd!=0));
@ -210,9 +200,6 @@ VGMSTREAM * init_vgmstream_ps_headerless(STREAMFILE *streamFile) {
if(!strcasecmp("mi4",filename_extension(filename)))
vgmstream->sample_rate = 48000;
if (!strcasecmp("vb",filename_extension(filename)))
vgmstream->sample_rate = 22050;
vgmstream->num_samples = (int32_t)(fileLength/16/channel_count*28);
if(loopEnd!=0) {

View File

@ -16,6 +16,7 @@ typedef struct {
const char* uniq; /* unique name, typically same as file without extension (optional) */
const char* wav; /* same as file (optional) */
} psb_temp_t;
typedef struct {
psb_temp_t* tmp;
psb_codec_t codec;
@ -49,6 +50,7 @@ typedef struct {
int32_t intro_samples;
int32_t skip_samples;
int loop_flag;
int loop_range;
int32_t loop_start;
int32_t loop_end;
int loop_test;
@ -263,7 +265,7 @@ static segmented_layout_data* build_segmented_psb_opus(STREAMFILE* sf, psb_heade
uint32_t skips[] = {0, psb->skip_samples};
/* intro + body (looped songs) or just body (standard songs)
in full loops intro is 0 samples with a micro 1-frame opus [Nekopara (Switch)] */
* In full loops intro is 0 samples with a micro 1-frame opus [Nekopara (Switch)] */
if (offsets[0] && samples[0])
segment_count++;
if (offsets[1] && samples[1])
@ -458,6 +460,16 @@ static int prepare_codec(STREAMFILE* sf, psb_header_t* psb) {
psb->codec = OPUSNX;
psb->body_samples -= psb->skip_samples;
/* When setting loopstr="range:N,M", doesn't seem to transition properly (clicks) unless aligned (not always?)
* > N=intro's sampleCount, M=intro+body's sampleCount - skipSamples - default_skip, but not always
* [Anonymous;Code (Switch)-bgm08, B-Project: Ryuusei Fantasia (Switch)-bgm27] */
if (psb->loop_range) {
//TODO read actual default skip
psb->intro_samples -= 120;
psb->body_samples -= 120;
}
if (!psb->loop_flag)
psb->loop_flag = psb->intro_samples > 0;
psb->loop_start = psb->intro_samples;
@ -504,25 +516,29 @@ fail:
static int prepare_name(psb_header_t* psb) {
char* buf = psb->readable_name;
int buf_size = sizeof(psb->readable_name);
const char* main_name = psb->tmp->voice;
const char* sub_name = psb->tmp->uniq;
int main_len;
char* buf = psb->readable_name;
int buf_size = sizeof(psb->readable_name);
if (!main_name) /* shouldn't happen */
return 1;
if (!sub_name)
sub_name = psb->tmp->wav;
if (!sub_name)
sub_name = psb->tmp->file;
if (!main_name) /* shouldn't happen */
return 1;
/* sometimes we have main="bgm01", sub="bgm01.wav" = detect and ignore */
main_len = strlen(main_name);
if (sub_name && strncmp(main_name, sub_name, main_len) == 0) {
if (sub_name[main_len] == '\0' || strcmp(sub_name + main_len, ".wav") == 0)
sub_name = NULL;
if (sub_name) {
int main_len = strlen(main_name);
int sub_len = strlen(sub_name);
if (main_len > sub_len && strncmp(main_name, sub_name, main_len) == 0) {
if (sub_name[main_len] == '\0' || strcmp(sub_name + main_len, ".wav") == 0)
sub_name = NULL;
}
}
if (sub_name) {
@ -618,7 +634,7 @@ static int parse_psb_channels(psb_header_t* psb, psb_node_t* nchans) {
psb->body_offset = data.offset;
psb->body_size = data.size;
psb->body_samples = psb_node_get_integer(&node, "sampleCount");
psb->skip_samples = psb_node_get_integer(&node, "skipSampleCount");
psb->skip_samples = psb_node_get_integer(&node, "skipSampleCount"); /* fixed to seek_preroll? (80ms) */
}
if (psb_node_by_key(&narch, "intro", &node)) {
@ -698,9 +714,14 @@ static int parse_psb_voice(psb_header_t* psb, psb_node_t* nvoice) {
/* optional loop flag (loop points go in channels, or implicit in fmt/RIFF) */
if (!psb->loop_flag) {
const char* loopstr = psb_node_get_string(&nsong, "loopstr");
psb->loop_flag = psb_node_get_integer(&nsong, "loop") > 1;
/* There is also loopstr/loopStr = "all" when "loop"=2 and "none" when "loop"=0
* SFX set loop=0, and sometimes songs that look like they could do full loops do too */
/* loopstr values:
* - "none", w/ loop=0
* - "all", w/ loop = 2 [Legend of Mana (multi)]
* - "range:N,M", w/ loop = 2 [Anonymous;Code (Switch)] */
psb->loop_range = loopstr && strncmp(loopstr, "range:", 6) == 0; /* slightly different in rare cases */
}
/* other optional keys:
@ -733,7 +754,7 @@ fail:
* Keys are (seemingly) stored in text order.
*/
static int parse_psb(STREAMFILE* sf, psb_header_t* psb) {
psb_temp_t tmp;
psb_temp_t tmp = {0};
psb_context_t* ctx = NULL;
psb_node_t nroot, nvoice;
float version;

View File

@ -984,12 +984,16 @@ fail:
}
/* for maximum annoyance later UE4 versions (~v4.2x?) interleave single frames instead of
* half interleave, but don't have flags to detect so we need some heuristics */
* half interleave, but don't have flags to detect so we need some heuristics. Most later
* games with 0x36 chunk size use v2_interleave but notably Travis Strikes Again doesn't */
static size_t get_ue4_msadpcm_interleave(STREAMFILE* sf, riff_fmt_chunk* fmt, off_t start, size_t size) {
size_t v1_interleave = size / fmt->channels;
size_t v2_interleave = fmt->block_size;
uint8_t nibbles1[0x08] = {0};
uint8_t nibbles2[0x08] = {0};
uint8_t nibbles_half[0x20] = {0};
uint8_t nibbles_full[0x20] = {0};
int nibbles_size = sizeof(nibbles_full);
uint8_t empty[0x20] = {0};
int is_blank_half, is_blank_full;
/* old versions */
@ -997,43 +1001,56 @@ static size_t get_ue4_msadpcm_interleave(STREAMFILE* sf, riff_fmt_chunk* fmt, of
return v1_interleave;
/* 6ch only observed in later versions [Fortnite (PC)], not padded */
if (fmt->channels > 2)
if (fmt->channels > 2 || fmt->channels < 2)
return v2_interleave;
read_streamfile(nibbles1, start + size - 0x08, sizeof(nibbles2), sf);
read_streamfile(nibbles2, start + v1_interleave - 0x08, sizeof(nibbles2), sf);
read_streamfile(nibbles_half, start + v1_interleave - nibbles_size, nibbles_size, sf);
is_blank_half = memcmp(nibbles_half, empty, nibbles_size) == 0;
read_streamfile(nibbles_full, start + size - nibbles_size, nibbles_size, sf);
is_blank_full = memcmp(nibbles_full, empty, nibbles_size) == 0;
/* last frame is almost always padded, so should at half interleave */
if (get_u64be(nibbles1) == 0 && get_u64be(nibbles2) == 0)
if (!is_blank_half && !is_blank_full) {
VGM_LOG("v1 a\n");
return v1_interleave;
}
/* last frame is padded, and half interleave is not: should be regular interleave*/
if (!is_blank_half && is_blank_full) {
VGM_LOG("v2 a\n");
return v2_interleave;
}
VGM_LOG("i=%i, i=%i\n", is_blank_half, is_blank_full);
/* last frame is silent-ish, so should at half interleave (TSA's SML_DarknessLoop_01, TSA_CAD_YAKATA)
* this doesn't work too well b/c num_samples at 0x36 uses all data, may need adjustment */
{
int i;
int empty_nibbles1 = 1, empty_nibbles2 = 1;
int empty_nibbles_full = 1, empty_nibbles_half = 1;
for (i = 0; i < sizeof(nibbles1); i++) {
uint8_t n1 = ((nibbles1[i] >> 0) & 0x0f);
uint8_t n2 = ((nibbles1[i] >> 4) & 0x0f);
for (i = 0; i < sizeof(nibbles_full); i++) {
uint8_t n1 = ((nibbles_full[i] >> 0) & 0x0f);
uint8_t n2 = ((nibbles_full[i] >> 4) & 0x0f);
if ((n1 != 0x0 && n1 != 0xf && n1 != 0x1) || (n2 != 0x0 && n2 != 0xf && n2 != 0x1)) {
empty_nibbles1 = 0;
empty_nibbles_full = 0;
break;
}
}
for (i = 0; i < sizeof(nibbles2); i++) {
uint8_t n1 = ((nibbles2[i] >> 0) & 0x0f);
uint8_t n2 = ((nibbles2[i] >> 4) & 0x0f);
for (i = 0; i < sizeof(nibbles_half); i++) {
uint8_t n1 = ((nibbles_half[i] >> 0) & 0x0f);
uint8_t n2 = ((nibbles_half[i] >> 4) & 0x0f);
if ((n1 != 0x0 && n1 != 0xf && n1 != 0x1) || (n2 != 0x0 && n2 != 0xf && n2 != 0x1)) {
empty_nibbles2 = 0;
empty_nibbles_half = 0;
break;
}
}
if (empty_nibbles1 && empty_nibbles2)
if (empty_nibbles_full && empty_nibbles_half){
VGM_LOG("v1 b\n");
return v1_interleave;
}
}
/* other tests? */

View File

@ -97,7 +97,7 @@ VGMSTREAM* init_vgmstream_sgxd(STREAMFILE* sf) {
* - WSUR: ?
* - WMKR: ?
* - CONF: ? (name offset + config offset)
* - BUSS: bus config?
* - BUSS: bus config? */
/* WAVE chunk (size 0x10 + files * 0x38 + optional padding) */
if (is_sgx) { /* position after chunk+size */

View File

@ -3,6 +3,7 @@
#include "../layout/layout.h"
#include "txth_streamfile.h"
#include "../util/text_reader.h"
#include "../util/endianness.h"
#define TXT_LINE_MAX 2048 /* probably ~1000 would be ok */
#define TXT_LINE_KEY_MAX 128
@ -49,6 +50,7 @@ typedef enum {
PCM_FLOAT_LE,
IMA_HV,
PCM8_SB,
HEVAG,
UNKNOWN = 99,
} txth_codec_t;
@ -153,6 +155,8 @@ typedef struct {
int sf_head_opened;
int sf_body_opened;
int debug;
} txth_header;
static VGMSTREAM* init_subfile(txth_header* txth);
@ -217,7 +221,8 @@ VGMSTREAM* init_vgmstream_txth(STREAMFILE* sf) {
uint32_t interleave = 0;
switch(txth.codec) {
case PSX:
case PSX_bf: interleave = 0x10; break;
case PSX_bf:
case HEVAG: interleave = 0x10; break;
case NGC_DSP: interleave = 0x08; break;
case PCM16LE:
case PCM16BE: interleave = 0x02; break;
@ -235,6 +240,8 @@ VGMSTREAM* init_vgmstream_txth(STREAMFILE* sf) {
/* type to coding conversion */
switch (txth.codec) {
case PSX: coding = coding_PSX; break;
case PSX_bf: coding = coding_PSX_badflags; break;
case HEVAG: coding = coding_HEVAG; break;
case XBOX: coding = coding_XBOX_IMA; break;
case NGC_DTK: coding = coding_NGC_DTK; break;
case PCM16LE: coding = coding_PCM16LE; break;
@ -254,7 +261,6 @@ VGMSTREAM* init_vgmstream_txth(STREAMFILE* sf) {
case AICA: coding = coding_AICA; break;
case MSADPCM: coding = coding_MSADPCM; break;
case NGC_DSP: coding = coding_NGC_DSP; break;
case PSX_bf: coding = coding_PSX_badflags; break;
case MS_IMA: coding = coding_MS_IMA; break;
case APPLE_IMA4: coding = coding_APPLE_IMA4; break;
#ifdef VGM_USE_FFMPEG
@ -320,6 +326,7 @@ VGMSTREAM* init_vgmstream_txth(STREAMFILE* sf) {
case coding_SDX2:
case coding_PSX:
case coding_PSX_badflags:
case coding_HEVAG:
case coding_DVI_IMA:
case coding_IMA:
case coding_HV_IMA:
@ -351,6 +358,7 @@ VGMSTREAM* init_vgmstream_txth(STREAMFILE* sf) {
if (!txth.interleave && (
coding == coding_PSX ||
coding == coding_PSX_badflags ||
coding == coding_HEVAG ||
coding == coding_IMA_int ||
coding == coding_DVI_IMA_int ||
coding == coding_SDX2_int ||
@ -473,40 +481,37 @@ VGMSTREAM* init_vgmstream_txth(STREAMFILE* sf) {
/* get coefs */
{
int16_t (*read_16bit)(off_t, STREAMFILE*) = txth.coef_big_endian ? read_16bitBE : read_16bitLE;
int16_t (*get_16bit)(const uint8_t* p) = txth.coef_big_endian ? get_16bitBE : get_16bitLE;
read_s16_t read_s16 = txth.coef_big_endian ? read_s16be : read_s16le;
get_s16_t get_s16 =txth.coef_big_endian ? get_s16be : get_s16le;
for (i = 0; i < vgmstream->channels; i++) {
if (txth.coef_mode == 0) { /* normal coefs */
for (j = 0; j < 16; j++) {
int16_t coef;
if (txth.coef_table_set)
coef = get_16bit(txth.coef_table + i*txth.coef_spacing + j*2);
coef = get_s16(txth.coef_table + i*txth.coef_spacing + j*2);
else
coef = read_16bit(txth.coef_offset + i*txth.coef_spacing + j*2, txth.sf_head);
coef = read_s16(txth.coef_offset + i*txth.coef_spacing + j*2, txth.sf_head);
vgmstream->ch[i].adpcm_coef[j] = coef;
}
}
else { /* split coefs */
goto fail; //IDK what is this
/*
else { /* split coefs (first all 8 positive, then all 8 negative [P.N.03 (GC), Viewtiful Joe (GC)] */
for (j = 0; j < 8; j++) {
vgmstream->ch[i].adpcm_coef[j*2] = read_16bit(genh.coef_offset + i*genh.coef_spacing + j*2, txth.sf_head);
vgmstream->ch[i].adpcm_coef[j*2+1] = read_16bit(genh.coef_split_offset + i*genh.coef_split_spacing + j*2, txth.sf_head);
vgmstream->ch[i].adpcm_coef[j*2+0] = read_s16(txth.coef_offset + i*txth.coef_spacing + j*2 + 0x00, txth.sf_head);
vgmstream->ch[i].adpcm_coef[j*2+1] = read_s16(txth.coef_offset + i*txth.coef_spacing + j*2 + 0x10, txth.sf_head);
}
*/
}
}
}
/* get hist */
if (txth.hist_set) {
int16_t (*read_16bit)(off_t , STREAMFILE*) = txth.hist_big_endian ? read_16bitBE : read_16bitLE;
read_s16_t read_s16 = txth.coef_big_endian ? read_s16be : read_s16le;
for (i = 0; i < vgmstream->channels; i++) {
off_t offset = txth.hist_offset + i*txth.hist_spacing;
vgmstream->ch[i].adpcm_history1_16 = read_16bit(offset + 0x00, txth.sf_head);
vgmstream->ch[i].adpcm_history2_16 = read_16bit(offset + 0x02, txth.sf_head);
vgmstream->ch[i].adpcm_history1_16 = read_s16(offset + 0x00, txth.sf_head);
vgmstream->ch[i].adpcm_history2_16 = read_s16(offset + 0x02, txth.sf_head);
}
}
@ -977,6 +982,7 @@ static txth_codec_t parse_codec(txth_header* txth, const char* val) {
else if (is_string(val,"CP_YM")) return CP_YM;
else if (is_string(val,"PCM_FLOAT_LE")) return PCM_FLOAT_LE;
else if (is_string(val,"IMA_HV")) return IMA_HV;
else if (is_string(val,"HEVAG")) return HEVAG;
/* special handling */
else if (is_string(val,"name_value")) return txth->name_values[0];
else if (is_string(val,"name_value1")) return txth->name_values[0];
@ -987,21 +993,37 @@ static txth_codec_t parse_codec(txth_header* txth, const char* val) {
return UNKNOWN;
}
static int parse_be(txth_header* txth, const char* val, uint32_t* p_value) {
if (is_string(val, "BE"))
static int parse_endianness(txth_header* txth, const char* val, uint32_t* p_value, uint32_t* mode) {
if (is_string(val, "BE")) {
*p_value = 1;
else if (is_string(val, "LE"))
if (mode) *mode = 0;
}
else if (is_string(val, "LE")) {
*p_value = 0;
else
if (mode) *mode = 0;
}
else if (is_string(val, "BE_split")) {
*p_value = 1;
if (mode) *mode = 1;
}
else if (is_string(val, "LE_split")) {
*p_value = 0;
if (mode) *mode = 1;
}
else {
if (!parse_num(txth->sf_head,txth,val, p_value))
goto fail;
}
return 1;
fail:
return 0;
}
static int parse_keyval(STREAMFILE* sf_, txth_header* txth, const char* key, char* val) {
//;VGM_LOG("TXTH: key=%s, val=%s\n", key, val);
if (txth->debug)
vgm_logi("TXTH: reading key=%s, val=%s\n", key, val);
/* CODEC */
if (is_string(key,"codec")) {
@ -1202,6 +1224,8 @@ static int parse_keyval(STREAMFILE* sf_, txth_header* txth, const char* key, cha
else if (txth->loop_behavior == POSITIVE) {
if (txth->loop_flag == 0xFF || txth->loop_flag == 0xFFFF || txth->loop_flag == 0xFFFFFFFF)
txth->loop_flag = 0;
else if (txth->loop_flag == 0)
txth->loop_flag = 1;
}
else if (txth->loop_behavior == INVERTED) {
txth->loop_flag = (txth->loop_flag == 0);
@ -1237,10 +1261,7 @@ static int parse_keyval(STREAMFILE* sf_, txth_header* txth, const char* key, cha
if (!parse_num(txth->sf_head,txth,val, &txth->coef_spacing)) goto fail;
}
else if (is_string(key,"coef_endianness")) {
if (!parse_be(txth, val, &txth->coef_big_endian)) goto fail;
}
else if (is_string(key,"coef_mode")) {
if (!parse_num(txth->sf_head,txth,val, &txth->coef_mode)) goto fail;
if (!parse_endianness(txth, val, &txth->coef_big_endian, &txth->coef_mode)) goto fail;
}
else if (is_string(key,"coef_table")) {
if (!parse_coef_table(txth->sf_head,txth,val, txth->coef_table, sizeof(txth->coef_table))) goto fail;
@ -1260,7 +1281,7 @@ static int parse_keyval(STREAMFILE* sf_, txth_header* txth, const char* key, cha
if (!parse_num(txth->sf_head,txth,val, &txth->hist_spacing)) goto fail;
}
else if (is_string(key,"hist_endianness")) {
if (!parse_be(txth, val, &txth->hist_big_endian)) goto fail;
if (!parse_endianness(txth, val, &txth->hist_big_endian, NULL)) goto fail;
}
/* SUBSONGS */
@ -1416,7 +1437,7 @@ static int parse_keyval(STREAMFILE* sf_, txth_header* txth, const char* key, cha
if (!parse_num(txth->sf_head,txth,val, &txth->chunk_size_offset)) goto fail;
}
else if (is_string(key,"chunk_endianness")) {
if (!parse_be(txth, val, &txth->chunk_be)) goto fail;
if (!parse_endianness(txth, val, &txth->chunk_be, NULL)) goto fail;
}
@ -1435,6 +1456,10 @@ static int parse_keyval(STREAMFILE* sf_, txth_header* txth, const char* key, cha
if (!parse_multi_txth(txth,val)) goto fail;
}
/* DEBUG */
else if (is_string(key,"debug")) {
txth->debug = 1;
}
/* DEFAULT */
else {
@ -1917,7 +1942,9 @@ static int parse_num(STREAMFILE* sf, txth_header* txth, const char* val, uint32_
if (subsong_spacing)
offset = offset + subsong_spacing * (txth->target_subsong - 1);
//;VGM_LOG("TXTH: read at offset %x + %x\n", offset - txth->base_offset, txth->base_offset);
if (txth->debug)
vgm_logi("TXTH: use value at 0x%x (%s %ib)\n", offset, big_endian ? "BE" : "LE", size * 8);
switch(size) {
case 1: value = read_u8(offset,sf); break;
case 2: value = big_endian ? read_u16be(offset,sf) : read_u16le(offset,sf); break;
@ -1933,6 +1960,9 @@ static int parse_num(STREAMFILE* sf, txth_header* txth, const char* val, uint32_
if (sscanf(val, hex ? "%x%n" : "%u%n", &value, &n) != 1)
goto fail;
value_read = 1;
if (txth->debug)
vgm_logi(hex ? "TXTH: use constant 0x%x\n" : "TXTH: use constant %i\n", value);
}
else { /* known field */
if ((n = is_string_field(val,"interleave"))) value = txth->interleave;
@ -1987,6 +2017,9 @@ static int parse_num(STREAMFILE* sf, txth_header* txth, const char* val, uint32_
else if ((n = is_string_field(val,"name_value16"))) value = txth->name_values[15];
else goto fail;
value_read = 1;
if (txth->debug)
vgm_logi("TXTH: use field value 0x%x\n", value);
}
/* apply simple left-to-right math though, for now "(" ")" are counted and validated
@ -2028,10 +2061,13 @@ static int parse_num(STREAMFILE* sf, txth_header* txth, const char* val, uint32_
*out_value = result;
//;VGM_LOG("TXTH: final result %u (0x%x)\n", result, result);
if (txth->debug)
vgm_logi("TXTH: final value: %u (0x%x)\n", result, result);
return 1;
fail:
//VGM_LOG("TXTH: error parsing num '%s'\n", val);
if (txth->debug)
vgm_logi("TXTH: error parsing num '%s'\n", val);
return 0;
}
@ -2045,6 +2081,7 @@ static int get_bytes_to_samples(txth_header* txth, uint32_t bytes) {
return dsp_bytes_to_samples(bytes, txth->channels);
case PSX:
case PSX_bf:
case HEVAG:
return ps_bytes_to_samples(bytes, txth->channels);
case PCM16BE:
case PCM16LE:

View File

@ -1712,7 +1712,7 @@ fail:
}
static uint32_t ubi_ps2_pitch_to_freq(uint32_t pitch) {
/* old PS2 games store sample rate in a weird range of 0-0x10000 remapped from 0-48000 */
/* old PS2 games store sample rate in a weird range of 0-65536 remapped from 0-48000 */
/* strangely, audio res type does have sample rate value but it's unused */
double sample_rate = (((double)pitch / 65536) * 48000);
return (uint32_t)ceil(sample_rate);
@ -1850,7 +1850,7 @@ static int parse_type_audio(ubi_sb_header* sb, off_t offset, STREAMFILE* sf) {
/* PC can have subblock 2 based on two fields near the end but it wasn't seen so far */
/* stream_type field is not used if the flag is not set (it even contains garbage in some versions)
/* stream_type field is not used for HW sounds and may contain garbage
* except for PS3 and new PSP which have two hardware codecs (PSX and AT3) */
if (!software_flag && sb->platform != UBI_PS3 && !(sb->platform == UBI_PSP && !sb->is_psp_old))
sb->stream_type = 0x00;
@ -2491,6 +2491,13 @@ static int parse_header(ubi_sb_header* sb, STREAMFILE* sf, off_t offset, int ind
if (!parse_type_random(sb, offset, sf))
goto fail;
break;
case 0x00:
if (sb->is_dat) {
/* weird dummy entries in Donald Duck: Goin' Quackers (DC) */
sb->type = UBI_SILENCE;
sb->duration = 1.0f;
break;
}
default:
VGM_LOG("UBI SB: unknown header type %x at %x\n", sb->header_type, (uint32_t)offset);
goto fail;

View File

@ -0,0 +1,265 @@
#include "meta.h"
#include "../coding/coding.h"
static uint16_t SsPitchFromNote(int16_t note, int16_t fine, uint8_t center, uint8_t shift);
#define VAB_MIN(x,y) ((x)<(y)?(x):(y))
#define VAB_MAX(x,y) ((x)>(y)?(x):(y))
#define VAB_CLAMP(x,min,max) VAB_MIN(VAB_MAX(x,min),max)
static int read_vabcfg_file(STREAMFILE* sf, int program, int tone, int* note, int* fine, int* uselimits) {
char filename[PATH_LIMIT];
off_t txt_offset, file_size;
STREAMFILE* sf_cfg = NULL;
size_t file_len, key_len;
sf_cfg = open_streamfile_by_filename(sf, ".vab_config");
if (!sf_cfg) goto fail;
get_streamfile_filename(sf, filename, sizeof(filename));
txt_offset = read_bom(sf_cfg);
file_size = get_streamfile_size(sf_cfg);
file_len = strlen(filename);
/* read lines and find target filename, format is (filename): value1, ... valueN */
while (txt_offset < file_size) {
char line[0x2000];
char key[PATH_LIMIT] = { 0 }, val[0x2000] = { 0 };
int ok, bytes_read, line_ok;
int cfg_program, cfg_tone, cfg_note, cfg_fine, cfg_limits;
bytes_read = read_line(line, sizeof(line), txt_offset, sf_cfg, &line_ok);
if (!line_ok) goto fail;
txt_offset += bytes_read;
/* get key/val (ignores lead/trailing spaces, stops at comment/separator) */
ok = sscanf(line, " %[^\t#:] : %[^\t#\r\n] ", key, val);
if (ok != 2) /* ignore line if no key=val (comment or garbage) */
continue;
if (key[0] == '*') {
key_len = strlen(key);
if (file_len < key_len)
continue;
if (strcmp(filename + (file_len - key_len + 1), key + 1) != 0)
continue;
} else {
if (strcmp(filename, key) != 0)
continue;
}
ok = sscanf(val, "%d , %d , %d , %d , %d", &cfg_program, &cfg_tone, &cfg_note, &cfg_fine, &cfg_limits);
if (ok != 5)
continue;
if (cfg_program >= 0 && program != cfg_program)
continue;
if (cfg_tone >= 0 && tone != cfg_tone)
continue;
*note = cfg_note;
*fine = cfg_fine;
*uselimits = cfg_limits;
close_streamfile(sf_cfg);
return 1;
}
fail:
close_streamfile(sf_cfg);
return 0;
}
/* .VAB - standard PS1 bank format */
VGMSTREAM* init_vgmstream_vab(STREAMFILE* sf) {
uint16_t programs, wave_num, pitch;
uint8_t center, shift, min_note, max_note;
off_t programs_off, tones_off, waves_off, entry_off, data_offset;
size_t data_size;
int target_subsong = sf->stream_index, is_vh = 0, program_num, tone_num = 0, total_subsongs,
note, fine, uselimits,
channels, loop_flag, loop_start = 0, loop_end = 0;
int i;
STREAMFILE* sf_data = NULL;
VGMSTREAM* vgmstream = NULL;
/* this format is intended for storing samples for sequenced music but
* some games use it for storing SFX as a hack */
/* checks */
if (!is_id32le(0x00, sf, "VABp"))
goto fail;
if (check_extensions(sf, "vh")) {
is_vh = 1;
sf_data = open_streamfile_by_ext(sf, "vb");
if (!sf_data) goto fail;
} else if (check_extensions(sf, "vab")) {
is_vh = 0;
sf_data = sf;
} else {
goto fail;
}
programs = read_u16le(0x12, sf);
//tones = read_u16le(0x14, sf);
//waves = read_u16le(0x16, sf);
programs_off = 0x20;
tones_off = programs_off + 128 * 0x10;
waves_off = tones_off + programs * 16 * 0x20;
if (target_subsong == 0) target_subsong = 1;
if (target_subsong < 0)
goto fail;
total_subsongs = 0;
program_num = -1;
for (i = 0; i < programs; i++) {
uint8_t program_tones;
int local_target;
local_target = target_subsong - total_subsongs - 1;
entry_off = programs_off + i * 0x10;
program_tones = read_u8(entry_off + 0x00, sf);
total_subsongs += program_tones;
if (local_target >= 0 && local_target < program_tones) {
program_num = i;
tone_num = local_target;
}
}
if (program_num == -1)
goto fail;
entry_off = tones_off + program_num * 16 * 0x20 + tone_num * 0x20;
center = read_u8(entry_off + 0x04, sf);
shift = read_u8(entry_off + 0x05, sf);
min_note = read_u8(entry_off + 0x06, sf);
max_note = read_u8(entry_off + 0x07, sf);
wave_num = read_u16le(entry_off + 0x16, sf);
if (read_vabcfg_file(sf, program_num, tone_num, &note, &fine, &uselimits)) {
if (uselimits)
note = VAB_CLAMP(note, min_note, max_note);
} else {
note = VAB_CLAMP(60, min_note, max_note);
fine = 0;
}
/* play default note */
pitch = SsPitchFromNote(note, fine, center, shift);
data_offset = is_vh ? 0x00 : (waves_off + 256 * 0x02);
for (i = 0; i < wave_num; i++) {
data_offset += read_u16le(waves_off + i * 0x02, sf) << 3;
}
data_size = read_u16le(waves_off + i * 0x02, sf) << 3;
if (data_size == 0 && center == 0 && shift == 0) {
// hack for empty sounds in Critical Depth
vgmstream = init_vgmstream_silence(1, 44100, 44100);
if (!vgmstream) goto fail;
vgmstream->meta_type = meta_VAB;
vgmstream->num_streams = total_subsongs;
snprintf(vgmstream->stream_name, STREAM_NAME_SIZE, "%02d/%02d (empty)", program_num, tone_num);
if (is_vh) close_streamfile(sf_data);
return vgmstream;
}
channels = 1;
loop_flag = ps_find_loop_offsets(sf_data, data_offset, data_size, channels, 0, &loop_start, &loop_end);
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channels, loop_flag);
if (!vgmstream) goto fail;
vgmstream->meta_type = meta_VAB;
vgmstream->coding_type = coding_PSX;
vgmstream->layout_type = layout_none;
vgmstream->sample_rate = (pitch * 44100) / 4096; // FIXME: Maybe use actual pitching if implemented.
vgmstream->num_samples = ps_bytes_to_samples(data_size, channels);
vgmstream->loop_start_sample = loop_start;
vgmstream->loop_end_sample = loop_end;
vgmstream->stream_size = data_size;
vgmstream->num_streams = total_subsongs;
snprintf(vgmstream->stream_name, STREAM_NAME_SIZE, "%02d/%02d", program_num, tone_num);
if (!vgmstream_open_stream(vgmstream, sf_data, data_offset))
goto fail;
if (is_vh) close_streamfile(sf_data);
return vgmstream;
fail:
if (is_vh) close_streamfile(sf_data);
close_vgmstream(vgmstream);
return NULL;
}
/* Converts VAB note to PS1 pitch value (0-4096 where 4096 is 44100 Hz).
* Function reversed from PS1 SDK. */
static uint16_t _svm_ptable[] =
{
4096, 4110, 4125, 4140, 4155, 4170, 4185, 4200,
4216, 4231, 4246, 4261, 4277, 4292, 4308, 4323,
4339, 4355, 4371, 4386, 4402, 4418, 4434, 4450,
4466, 4482, 4499, 4515, 4531, 4548, 4564, 4581,
4597, 4614, 4630, 4647, 4664, 4681, 4698, 4715,
4732, 4749, 4766, 4783, 4801, 4818, 4835, 4853,
4870, 4888, 4906, 4924, 4941, 4959, 4977, 4995,
5013, 5031, 5050, 5068, 5086, 5105, 5123, 5142,
5160, 5179, 5198, 5216, 5235, 5254, 5273, 5292,
5311, 5331, 5350, 5369, 5389, 5408, 5428, 5447,
5467, 5487, 5507, 5527, 5547, 5567, 5587, 5607,
5627, 5648, 5668, 5688, 5709, 5730, 5750, 5771,
5792, 5813, 5834, 5855, 5876, 5898, 5919, 5940,
5962, 5983, 6005, 6027, 6049, 6070, 6092, 6114,
6137, 6159, 6181, 6203, 6226, 6248, 6271, 6294,
6316, 6339, 6362, 6385, 6408, 6431, 6455, 6478,
6501, 6525, 6549, 6572, 6596, 6620, 6644, 6668,
6692, 6716, 6741, 6765, 6789, 6814, 6839, 6863,
6888, 6913, 6938, 6963, 6988, 7014, 7039, 7064,
7090, 7116, 7141, 7167, 7193, 7219, 7245, 7271,
7298, 7324, 7351, 7377, 7404, 7431, 7458, 7485,
7512, 7539, 7566, 7593, 7621, 7648, 7676, 7704,
7732, 7760, 7788, 7816, 7844, 7873, 7901, 7930,
7958, 7987, 8016, 8045, 8074, 8103, 8133, 8162,
8192
};
static uint16_t SsPitchFromNote(int16_t note, int16_t fine, uint8_t center, uint8_t shift) {
uint32_t pitch;
int16_t calc, type;
int32_t add, sfine;//, ret;
sfine = fine + shift;
if (sfine < 0) sfine += 7;
sfine >>= 3;
add = 0;
if (sfine > 15) {
add = 1;
sfine -= 16;
}
calc = add + (note - (center - 60));//((center + 60) - note) + add;
pitch = _svm_ptable[16 * (calc % 12) + (int16_t)sfine];
type = calc / 12 - 5;
// regular shift
if (type > 0) return pitch << type;
// negative shift
if (type < 0) return pitch >> -type;
return pitch;
}

View File

@ -15,17 +15,17 @@ VGMSTREAM* init_vgmstream_vag(STREAMFILE* sf) {
/* checks */
if (((read_u32be(0x00,sf) & 0xFFFFFF00) != get_id32be("VAG\0")) &&
((read_u32le(0x00,sf) & 0xFFFFFF00) != get_id32be("VAG\0")))
goto fail;
/* .vag: standard
* .swag: Frantix (PSP)
* .str: Ben10 Galactic Racing
* .vig: MX vs. ATV Untamed (PS2)
* .l/r: Crash Nitro Kart (PS2), Gradius V (PS2)
* .vas: Kingdom Hearts II (PS2) */
if ( !check_extensions(sf,"vag,swag,str,vig,l,r,vas") )
goto fail;
if (((read_u32be(0x00,sf) & 0xFFFFFF00) != 0x56414700) && /* "VAG" */
((read_u32le(0x00,sf) & 0xFFFFFF00) != 0x56414700))
if (!check_extensions(sf,"vag,swag,str,vig,l,r,vas"))
goto fail;
file_size = get_streamfile_size(sf);

View File

@ -767,6 +767,7 @@ static int parse_wwise(STREAMFILE* sf, wwise_header* ww) {
/* parse chunks (reads once linearly) */
{
chunk_t rc = {0};
uint32_t file_size = get_streamfile_size(sf);;
/* chunks are even-aligned and don't need to add padding byte, unlike real RIFFs */
rc.be_size = ww->big_endian;
@ -819,6 +820,13 @@ static int parse_wwise(STREAMFILE* sf, wwise_header* ww) {
/* "JUNK": optional padding for aligment (0-size JUNK exists too) */
/* "akd ": extra info for Wwise? (wave peaks/loudness/HDR envelope?) */
default:
/* mainly for incorrectly ripped wems, but should allow truncated wems
* (could also check that fourcc is ASCII) */
if (rc.offset + rc.size > file_size) {
vgm_logi("WWISE: broken .wem (bad extract?)\n");
goto fail;
}
break;
}
}

View File

@ -1,158 +1,191 @@
#include "meta.h"
#include "../coding/coding.h"
/* XMA - Microsoft format derived from RIFF, found in X360/XBone games */
VGMSTREAM * init_vgmstream_xma(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
off_t start_offset, chunk_offset, first_offset = 0xc;
size_t data_size, chunk_size;
int loop_flag, channel_count, sample_rate, is_xma2_old = 0, is_xma1 = 0;
int num_samples, loop_start_sample, loop_end_sample, loop_start_b = 0, loop_end_b = 0, loop_subframe = 0;
int fmt_be = 0;
/* checks */
/* .xma: standard
* .xma2: Skullgirls (X360)
* .wav: Super Meat Boy (X360)
* .nps: Beautiful Katamari (X360)
* .str: Sonic & Sega All Stars Racing (X360) */
if ( !check_extensions(streamFile, "xma,xma2,wav,nps,str") )
goto fail;
/* check header */
if (read_32bitBE(0x00, streamFile) != 0x52494646) /* "RIFF" */
goto fail;
{
size_t file_size = streamFile->get_size(streamFile);
size_t riff_size = read_32bitLE(0x04,streamFile);
/* +8 for some Beautiful Katamari files, unsure if bad rip */
if (riff_size != file_size && riff_size+8 > file_size)
goto fail;
}
/* parse LE RIFF header, with "XMA2" (XMA2WAVEFORMAT) or "fmt " (XMAWAVEFORMAT/XMA2WAVEFORMAT) main chunks
* Often comes with an optional "seek" chunk too */
/* parse sample data */
if ( find_chunk_le(streamFile, 0x584D4132,first_offset,0, &chunk_offset,&chunk_size) ) { /* old XMA2 */
is_xma2_old = 1;
xma2_parse_xma2_chunk(streamFile, chunk_offset, &channel_count,&sample_rate, &loop_flag, &num_samples, &loop_start_sample, &loop_end_sample);
}
else if ( find_chunk_le(streamFile, 0x666d7420,first_offset,0, &chunk_offset,&chunk_size)) {
int format = read_16bitLE(chunk_offset,streamFile);
/* some Fable Heroes and Fable 3 XMA have a BE fmt chunk, but the rest of the file is still LE
* (incidentally they come with a "frsk" chunk) */
if (format == 0x6601) { /* new XMA2 but BE */
fmt_be = 1;
format = 0x0166;
}
if (format == 0x165) { /* XMA1 */
is_xma1 = 1;
xma1_parse_fmt_chunk(streamFile, chunk_offset, &channel_count,&sample_rate, &loop_flag, &loop_start_b, &loop_end_b, &loop_subframe, fmt_be);
} else if (format == 0x166) { /* new XMA2 */
int32_t (*read_32bit)(off_t,STREAMFILE*) = fmt_be ? read_32bitBE : read_32bitLE;
int16_t (*read_16bit)(off_t,STREAMFILE*) = fmt_be ? read_16bitBE : read_16bitLE;
channel_count = read_16bit(chunk_offset+0x02,streamFile);
sample_rate = read_32bit(chunk_offset+0x04,streamFile);
xma2_parse_fmt_chunk_extra(streamFile, chunk_offset, &loop_flag, &num_samples, &loop_start_sample, &loop_end_sample, fmt_be);
} else {
goto fail;
}
}
else {
goto fail;
}
/* "data" chunk */
if (!find_chunk_le(streamFile, 0x64617461,first_offset,0, &start_offset,&data_size)) /*"data"*/
goto fail;
/* get xma1 samples, later fixed */
if (is_xma1) {
ms_sample_data msd = {0};
msd.xma_version = is_xma1 ? 1 : 2;
msd.channels = channel_count;
msd.data_offset = start_offset;
msd.data_size = data_size;
msd.loop_flag = loop_flag;
msd.loop_start_b= loop_start_b;
msd.loop_end_b = loop_end_b;
msd.loop_start_subframe = loop_subframe & 0xF; /* lower 4b: subframe where the loop starts, 0..4 */
msd.loop_end_subframe = loop_subframe >> 4; /* upper 4b: subframe where the loop ends, 0..3 */
msd.chunk_offset= chunk_offset;
xma_get_samples(&msd, streamFile);
num_samples = msd.num_samples;
loop_start_sample = msd.loop_start_sample;
loop_end_sample = msd.loop_end_sample;
}
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail;
vgmstream->meta_type = meta_XMA_RIFF;
vgmstream->sample_rate = sample_rate;
vgmstream->num_samples = num_samples;
vgmstream->loop_start_sample = loop_start_sample;
vgmstream->loop_end_sample = loop_end_sample;
#ifdef VGM_USE_FFMPEG
{
uint8_t buf[0x100];
size_t bytes;
if (is_xma2_old) {
bytes = ffmpeg_make_riff_xma2_from_xma2_chunk(buf,0x100, chunk_offset,chunk_size, data_size, streamFile);
} else {
bytes = ffmpeg_make_riff_xma_from_fmt_chunk(buf,0x100, chunk_offset,chunk_size, data_size, streamFile, fmt_be);
}
vgmstream->codec_data = init_ffmpeg_header_offset(streamFile, buf,bytes, start_offset,data_size);
if ( !vgmstream->codec_data ) goto fail;
vgmstream->coding_type = coding_FFmpeg;
vgmstream->layout_type = layout_none;
xma_fix_raw_samples(vgmstream, streamFile, start_offset, data_size, chunk_offset, 1,1);
}
#else
goto fail;
#endif
if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) )
goto fail;
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;
}
#if 0
/**
* Get real XMA sample rate (from Microsoft docs).
* Info only, not for playback as the encoder adjusts sample rate for looping purposes (sample<>data align),
* When converting to PCM, xmaencode does use the modified sample rate.
*/
static int32_t get_xma_sample_rate(int32_t general_rate) {
int32_t xma_rate = 48000; /* default XMA */
if (general_rate <= 24000) xma_rate = 24000;
else if (general_rate <= 32000) xma_rate = 32000;
else if (general_rate <= 44100) xma_rate = 44100;
return xma_rate;
}
#endif
#include "meta.h"
#include "../coding/coding.h"
#include "../util/chunks.h"
#include "../util/endianness.h"
/* XMA - Microsoft format derived from RIFF, found in X360/XBone games */
VGMSTREAM* init_vgmstream_xma(STREAMFILE* sf) {
VGMSTREAM* vgmstream = NULL;
uint32_t start_offset = 0, data_size = 0, chunk_offset = 0, chunk_size = 0;
int loop_flag, channels, sample_rate, is_xma2_new = 0, is_xma2_old = 0, is_xma1 = 0;
int32_t num_samples, loop_start_sample, loop_end_sample, loop_start_b = 0, loop_end_b = 0, loop_subframe = 0;
int fmt_be = 0;
/* checks */
if (!is_id32be(0x00, sf, "RIFF"))
goto fail;
/* .xma: standard
* .xma2: Skullgirls (X360)
* .wav: Super Meat Boy (X360)
* .nps: Beautiful Katamari (X360)
* .str: Sonic & Sega All Stars Racing (X360)
* .kmx: Warriors: Legends of Troy (X360) */
if (!check_extensions(sf, "xma,xma2,wav,lwav,nps,str,kmx"))
goto fail;
{
uint32_t file_size = get_streamfile_size(sf);
uint32_t riff_size = read_u32le(0x04,sf);
/* some Beautiful Katamari set same riff/file size (those with "XMA2" and without "fmt ") */
if (riff_size != file_size && riff_size + 0x08 > file_size)
goto fail;
}
/* parse LE RIFF header, with "XMA2" (XMA2WAVEFORMAT) or "fmt " (XMAWAVEFORMAT/XMA2WAVEFORMAT) main chunks
* Often comes with an optional "seek" chunk too */
/* parse chunks (reads once linearly, as XMA2 chunk often goes near EOF) */
{
chunk_t rc = {0};
/* chunks are even-aligned and don't need to add padding byte, unlike real RIFFs */
rc.current = 0x0c;
while (next_chunk(&rc, sf)) {
switch(rc.type) {
case 0x584D4132: /* "XMA2" */
chunk_offset = rc.offset;
chunk_size = rc.size;
is_xma2_old = 1;
xma2_parse_xma2_chunk(sf, chunk_offset, &channels,&sample_rate, &loop_flag, &num_samples, &loop_start_sample, &loop_end_sample);
break;
case 0x666d7420: { /* "fmt " */
int format;
if (is_xma2_old) break; /* fmt has XMA1 info, favor XMA2 chunk */
chunk_offset = rc.offset;
chunk_size = rc.size;
format = read_u16le(rc.offset + 0x00,sf);
/* some Fable Heroes and Fable 3 XMA have a BE fmt chunk, but the rest of the file is still LE
* (incidentally they come with a "frsk" chunk) */
if (format == 0x6601) { /* new XMA2 but BE */
fmt_be = 1;
format = 0x0166;
}
if (format == 0x165) { /* XMA1 */
is_xma1 = 1;
xma1_parse_fmt_chunk(sf, chunk_offset, &channels,&sample_rate, &loop_flag, &loop_start_b, &loop_end_b, &loop_subframe, fmt_be);
}
else if (format == 0x166) { /* new XMA2 */
read_s32_t read_s32 = fmt_be ? read_s32be : read_s32le;
read_u16_t read_u16 = fmt_be ? read_u16be : read_u16le;
is_xma2_new = 1;
channels = read_u16(chunk_offset + 0x02,sf);
sample_rate = read_s32(chunk_offset + 0x04,sf);
xma2_parse_fmt_chunk_extra(sf, chunk_offset, &loop_flag, &num_samples, &loop_start_sample, &loop_end_sample, fmt_be);
}
else {
goto fail;
}
break;
}
case 0x64617461: /* "data" */
start_offset = rc.offset;
data_size = rc.size;
break;
default: /* others: "seek", "ALGN" */
break;
}
}
if (!is_xma2_new && !is_xma2_old && !is_xma1)
goto fail;
}
/* get xma1 samples, later fixed */
if (!is_xma2_old && !is_xma2_new && is_xma1) {
ms_sample_data msd = {0};
msd.xma_version = is_xma1 ? 1 : 2;
msd.channels = channels;
msd.data_offset = start_offset;
msd.data_size = data_size;
msd.loop_flag = loop_flag;
msd.loop_start_b= loop_start_b;
msd.loop_end_b = loop_end_b;
msd.loop_start_subframe = loop_subframe & 0xF; /* lower 4b: subframe where the loop starts, 0..4 */
msd.loop_end_subframe = loop_subframe >> 4; /* upper 4b: subframe where the loop ends, 0..3 */
msd.chunk_offset = chunk_offset;
xma_get_samples(&msd, sf);
num_samples = msd.num_samples;
loop_start_sample = msd.loop_start_sample;
loop_end_sample = msd.loop_end_sample;
}
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channels, loop_flag);
if (!vgmstream) goto fail;
vgmstream->meta_type = meta_XMA_RIFF;
vgmstream->sample_rate = sample_rate;
vgmstream->num_samples = num_samples;
vgmstream->loop_start_sample = loop_start_sample;
vgmstream->loop_end_sample = loop_end_sample;
#ifdef VGM_USE_FFMPEG
{
uint8_t buf[0x100];
int bytes;
if (is_xma2_old) {
bytes = ffmpeg_make_riff_xma2_from_xma2_chunk(buf, sizeof(buf), chunk_offset,chunk_size, data_size, sf);
}
else {
bytes = ffmpeg_make_riff_xma_from_fmt_chunk(buf, sizeof(buf), chunk_offset,chunk_size, data_size, sf, fmt_be);
}
vgmstream->codec_data = init_ffmpeg_header_offset(sf, buf, bytes, start_offset, data_size);
if (!vgmstream->codec_data) goto fail;
vgmstream->coding_type = coding_FFmpeg;
vgmstream->layout_type = layout_none;
xma_fix_raw_samples(vgmstream, sf, start_offset, data_size, chunk_offset, 1,1);
}
#else
goto fail;
#endif
if (!vgmstream_open_stream(vgmstream, sf, start_offset))
goto fail;
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;
}
#if 0
/**
* Get real XMA sample rate (from Microsoft docs).
* Info only, not for playback as the encoder adjusts sample rate for looping purposes (sample<>data align),
* When converting to PCM, xmaencode does use the modified sample rate.
*/
static int32_t get_xma_sample_rate(int32_t general_rate) {
int32_t xma_rate = 48000; /* default XMA */
if (general_rate <= 24000) xma_rate = 24000;
else if (general_rate <= 32000) xma_rate = 32000;
else if (general_rate <= 44100) xma_rate = 44100;
return xma_rate;
}
#endif

View File

@ -89,16 +89,18 @@ VGMSTREAM* init_vgmstream_xwb(STREAMFILE* sf) {
/* checks */
/* .xwb: standard
* .xna: Touhou Makukasai ~ Fantasy Danmaku Festival (PC)
* (extensionless): Ikaruga (X360/PC), Grabbed by the Ghoulies (Xbox) */
if (!check_extensions(sf,"xwb,xna,"))
goto fail;
if ((read_u32be(0x00,sf) != 0x57424E44) && /* "WBND" (LE) */
(read_u32be(0x00,sf) != 0x444E4257)) /* "DNBW" (BE) */
if (!is_id32be(0x00,sf, "WBND") &&
!is_id32le(0x00,sf, "WBND")) /* X360 */
goto fail;
xwb.little_endian = read_u32be(0x00,sf) == 0x57424E44; /* WBND */
/* .xwb: standard
* .xna: Touhou Makukasai ~ Fantasy Danmaku Festival (PC)
* (extensionless): Ikaruga (X360/PC), Grabbed by the Ghoulies (Xbox)
* .bd: Fatal Frame 2 (Xbox) */
if (!check_extensions(sf,"xwb,xna,bd"))
goto fail;
xwb.little_endian = is_id32be(0x00,sf, "WBND"); /* Xbox/PC */
if (xwb.little_endian) {
read_u32 = read_u32le;
read_s32 = read_s32le;

View File

@ -9,9 +9,9 @@
/* ****************************************** */
int vgmstream_ctx_is_valid(const char* filename, vgmstream_ctx_valid_cfg *cfg) {
const char ** extension_list;
const char** extension_list;
size_t extension_list_len;
const char *extension;
const char* extension;
int i;
@ -21,12 +21,12 @@ int vgmstream_ctx_is_valid(const char* filename, vgmstream_ctx_valid_cfg *cfg) {
extension = filename_extension(filename);
}
/* some metas accept extensionless files, but make sure it's not a path */
/* some metas accept extensionless files, but make sure it's not a path (unlikely but...) */
if (strlen(extension) <= 0) {
int len = strlen(filename);
if (len <= 0)
int len = strlen(filename); /* foobar passes an extension as so len may be still 0 */
if (len <= 0 && !cfg->is_extension)
return 0;
if (filename[len - 1] == '/' || filename[len - 1] == '\\')
if (len > 1 && (filename[len - 1] == '/' || filename[len - 1] == '\\'))
return 0;
return !cfg->reject_extensionless;
}

View File

@ -16,6 +16,7 @@ typedef struct {
int alignment; /* chunks with odd size need to be aligned to even, per RIFF spec */
} chunk_t;
/* reads from current offset and updates chunk_t */
int next_chunk(chunk_t* chunk, STREAMFILE* sf);
#if 0

View File

@ -9,6 +9,8 @@ typedef uint16_t (*read_u16_t)(off_t, STREAMFILE*);
typedef int16_t (*read_s16_t)(off_t, STREAMFILE*);
typedef float (*read_f32_t)(off_t, STREAMFILE*);
typedef int16_t (*get_s16_t)(const uint8_t*);
//todo move here
#define guess_endian32 guess_endianness32bit
#define guess_endian16 guess_endianness16bit

View File

@ -11,32 +11,34 @@
* also https://github.com/number201724/psbfile and https://github.com/UlyssesWu/FreeMote
*
* PSB defines a header with offsets to sections within the header, binary format being type-value (where type could be
* int8/int16/float/array/object/etc). Example:
* 21 // object: root (x2 info lists + items)
* 0D 04 0D 06,0B,0D,0E // list8[4]: key indexes ("id/spec/version/voice")
* int8/int16/float/list/object/etc). Example:
* 21 // object: root (x2 listN + other data)
* 0D 04 0D 06,0B,0D,0E // list8[4]: key indexes (#0 "id", #1 "spec", #2 "version", #3 "voice"; found in a separate "key names" table)
* 0D 04 0D 00,02,04,09 // list8[4]: byte offsets of next 4 items
* 15 02 // 0 string8: string#2 ("spec")
* 1E 5C8F823F // 1 float32: 1.02
* 05 02 // 2 int8: 2
* 21 // 3 object
* 15 02 // #0 string8: string key #2 ("pc")
* 1E 5C8F823F // #1 float32: 1.02
* 05 02 // #2 int8: 2
* 21 // #3 object
* 0D 02 0D 02,05 // list8[2]: key indexes
* 0D 02 0D 00,02 // list8[2]: byte offsets
* 19 00 // 0 resource8: resource#0 (subfile)
* 20 // 1 array: loops
* 19 00 // #0 resource8: resource #0 (offset/size found in a separate "data resource" table)
* 20 // #1 array: loops
* 0D 02 0D 00,04 // list8[2]
* 07 D69107 // 0 int24
* 07 31A45C // 1 int24
* 07 D69107 // #0 int24
* 07 31A45C // #1 int24
*
* A game would then position on root object (offset in header) and ask for key "version".
* M2 lib finds the index for that key (#2), skips to that offset (0x04), reads type float32, returns 1.02
*/
//TODO: som
//TODO: add validations on buf over max size
//TODO: validate strings table ends with null (buf[max - 1] = '\0')
//TODO: add validations on buf over max size (like reading u32 on edge buf[len-1])
/******************************************************************************/
/* DEFS */
#define PSB_VERSION2 2 /* older (x360/ps3) games */
#define PSB_VERSION3 3 /* current games */
#define PSB_MAX_HEADER 0x40000 /* max seen ~0x1000 */
#define PSB_MAX_HEADER 0x40000 /* max seen ~0x1000 (alloc'd) */
/* Internal type used in binary data, that defines bytes used to store value.
@ -94,10 +96,10 @@ typedef enum {
typedef struct {
int bytes; /* total bytes (including headers) to skip this list */
int count; /* number of entries */
int esize; /* size per entry */
uint8_t* edata; /* start of entries */
int bytes; /* total bytes (including headers) to skip this list */
int count; /* number of entries */
int esize; /* size per entry */
uint8_t* edata; /* start of entries */
} list_t;
struct psb_context_t {
@ -109,21 +111,22 @@ struct psb_context_t {
uint32_t strings_list_offset;
uint32_t strings_data_offset;
uint32_t data_offsets_offset; //todo resources
uint32_t data_offsets_offset;
uint32_t data_sizes_offset;
uint32_t data_offset; //todo resources
uint32_t root_offset;
uint32_t data_offset; /* also "resources" */
uint32_t root_offset; /* initial node */
uint32_t unknown; /* hash/crc? (v3) */
/* main buf and derived stuff*/
/* main buf and derived stuff */
uint8_t* buf;
int buf_len;
list_t strings_list;
uint8_t* strings_data;
int strings_data_len;
list_t data_offsets_list;
list_t data_sizes_list;
/* keys buf */
char* keys;
int* keys_pos;
@ -189,7 +192,7 @@ static int list_init(list_t* lst, uint8_t* buf) {
default:
goto fail;
}
/* get entry info (0D + 00,01,02,03) */
entry_itype = buf[1 + count_size];
switch (entry_itype) {
@ -318,7 +321,6 @@ psb_context_t* psb_init(STREAMFILE* sf) {
psb_context_t* ctx;
uint8_t header[0x2c];
int bytes;
uint32_t buf_len;
ctx = calloc(1, sizeof(psb_context_t));
if (!ctx) goto fail;
@ -364,26 +366,38 @@ psb_context_t* psb_init(STREAMFILE* sf) {
ctx->root_offset >= ctx->data_offset)
goto fail;
/* copy data for easier access */
buf_len = ctx->data_offset;
if (buf_len > PSB_MAX_HEADER)
ctx->buf_len = (int)ctx->data_offset;
if (ctx->buf_len < 0 || ctx->buf_len > PSB_MAX_HEADER)
goto fail;
ctx->buf = malloc(buf_len);
ctx->buf = malloc(ctx->buf_len);
if (!ctx->buf) goto fail;
bytes = read_streamfile(ctx->buf, 0x00, buf_len, sf);
if (bytes != buf_len) goto fail;
bytes = read_streamfile(ctx->buf, 0x00, ctx->buf_len, sf);
if (bytes != ctx->buf_len) goto fail;
/* lists pointing to string block */
if (!list_init(&ctx->strings_list, &ctx->buf[ctx->strings_list_offset]))
goto fail;
ctx->strings_data = &ctx->buf[ctx->strings_data_offset];
/* block of plain c-strings (all null-terminated, including last) */
ctx->strings_data = &ctx->buf[ctx->strings_data_offset];
ctx->strings_data_len = ctx->data_offsets_offset - ctx->strings_data_offset;
if (ctx->strings_data_len < 0 || ctx->strings_data[ctx->strings_data_len - 1] != '\0') /* just in case to avoid overruns */
goto fail;
/* lists to access resources */
if (!list_init(&ctx->data_offsets_list, &ctx->buf[ctx->data_offsets_offset]))
goto fail;
if (!list_init(&ctx->data_sizes_list, &ctx->buf[ctx->data_sizes_offset]))
goto fail;
/* prepare key strings for easier handling */
if (!init_keys(ctx))
goto fail;
@ -397,7 +411,7 @@ fail:
void psb_close(psb_context_t* ctx) {
if (!ctx)
return;
free(ctx->keys_pos);
free(ctx->keys);
free(ctx->buf);
@ -532,7 +546,7 @@ int psb_node_by_index(const psb_node_t* node, int index, psb_node_t* p_out) {
p_out->data = &buf[1 + keys.bytes + offsets.bytes + skip];
return 1;
}
default:
goto fail;
}
@ -574,7 +588,7 @@ fail:
const char* psb_node_get_key(const psb_node_t* node, int index) {
uint8_t* buf;
int pos;
if (!node || !node->ctx || !node->data)
goto fail;
@ -592,7 +606,7 @@ const char* psb_node_get_key(const psb_node_t* node, int index) {
pos = node->ctx->keys_pos[keys_index];
return &node->ctx->keys[pos];
}
default:
goto fail;
}
@ -651,8 +665,11 @@ psb_result_t psb_node_get_result(psb_node_t* node) {
index = item_get_int(size, &buf[1]);
skip = list_get_entry(&node->ctx->strings_list, index);
res.str = (const char*)&node->ctx->strings_data[skip]; /* null-terminated */
//todo test max strlen to see if it's null-terminated
res.str = (const char*)&node->ctx->strings_data[skip]; /* null-terminated (validated on open) */
if (skip >= node->ctx->strings_data_len) { /* shouldn't happen */
vgm_logi("PSBLIB: bad skip over strings\n");
res.str = "";
}
break;
}
@ -665,7 +682,7 @@ psb_result_t psb_node_get_result(psb_node_t* node) {
res.data.offset = list_get_entry(&node->ctx->data_offsets_list, index);
res.data.size = list_get_entry(&node->ctx->data_sizes_list, index);
res.data.offset += node->ctx->data_offset;
break;
@ -754,8 +771,8 @@ psb_data_t psb_node_get_data(const psb_node_t* node, const char* key) {
return data;
}
return psb_node_get_result(&out).data;
}
int psb_node_exists(const psb_node_t* node, const char* key) {
psb_node_t out;
if (!psb_node_by_key(node, key, &out))

View File

@ -4,7 +4,7 @@
/* 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) {
int text_reader_init(text_reader_t* tr, uint8_t* buf, int buf_size, STREAMFILE* sf, uint32_t offset, uint32_t max_offset) {
memset(tr, 0, sizeof(text_reader_t));
if (buf_size <= 1 || !buf || !sf)
@ -15,9 +15,9 @@ int text_reader_init(text_reader_t* tr, uint8_t* buf, int buf_size, STREAMFILE*
tr->sf = sf;
tr->offset = offset;
if (!max)
max = get_streamfile_size(sf) - offset;
tr->max_offset = max;
if (!max_offset)
max_offset = get_streamfile_size(sf);
tr->max_offset = max_offset;
return 1;
}

View File

@ -95,19 +95,17 @@ VGMSTREAM* (*init_vgmstream_functions[])(STREAMFILE* sf) = {
init_vgmstream_acm,
init_vgmstream_mus_acm,
init_vgmstream_ps2_kces,
init_vgmstream_ps2_dxh,
init_vgmstream_hxd,
init_vgmstream_vsv,
init_vgmstream_scd_pcm,
init_vgmstream_ps2_pcm,
init_vgmstream_ps2_rkv,
init_vgmstream_ps2_vas,
init_vgmstream_ps2_vas_container,
init_vgmstream_ps2_tec,
init_vgmstream_ps2_enth,
init_vgmstream_sdt,
init_vgmstream_aix,
init_vgmstream_ngc_tydsp,
init_vgmstream_capdsp,
init_vgmstream_wvs_xbox,
init_vgmstream_wvs_ngc,
init_vgmstream_dc_str,
@ -527,6 +525,7 @@ VGMSTREAM* (*init_vgmstream_functions[])(STREAMFILE* sf) = {
init_vgmstream_bw_mp3_riff,
init_vgmstream_bw_riff_mp3,
init_vgmstream_sndz,
init_vgmstream_vab,
/* lower priority metas (no clean header identity, somewhat ambiguous, or need extension/companion file to identify) */
init_vgmstream_agsc,

View File

@ -398,17 +398,15 @@ typedef enum {
meta_BG00, /* Ibara, Mushihimesama */
meta_PS2_RSTM, /* Midnight Club 3 */
meta_PS2_KCES, /* Dance Dance Revolution */
meta_PS2_DXH, /* Tokobot Plus - Myteries of the Karakuri */
meta_HXD,
meta_VSV,
meta_SCD_PCM, /* Lunar - Eternal Blue */
meta_PS2_PCM, /* Konami KCEJ East: Ephemeral Fantasia, Yu-Gi-Oh! The Duelists of the Roses, 7 Blades */
meta_PS2_RKV, /* Legacy of Kain - Blood Omen 2 (PS2) */
meta_PS2_VAS, /* Pro Baseball Spirits 5 */
meta_PS2_TEC, /* TECMO badflagged stream */
meta_PS2_ENTH, /* Enthusia */
meta_SDT, /* Baldur's Gate - Dark Alliance */
meta_NGC_TYDSP, /* Ty - The Tasmanian Tiger */
meta_CAPDSP, /* Capcom DSP Header [no header_id] */
meta_DC_STR, /* SEGA Stream Asset Builder */
meta_DC_STR_V2, /* variant of SEGA Stream Asset Builder */
meta_NGC_BH2PCM, /* Bio Hazard 2 */
@ -772,6 +770,7 @@ typedef enum {
meta_ADM3,
meta_TT_AD,
meta_SNDZ,
meta_VAB,
} meta_t;