From 75485c388ee22ba8fafdc887f3398be201b21d95 Mon Sep 17 00:00:00 2001 From: bnnm Date: Tue, 20 Dec 2016 23:29:36 +0100 Subject: [PATCH 01/15] Moved VAG loop finder to function; comments, code preps --- src/meta/ps2_vag.c | 155 +++++++++++++++++++++++++-------------------- 1 file changed, 88 insertions(+), 67 deletions(-) diff --git a/src/meta/ps2_vag.c b/src/meta/ps2_vag.c index 5b433121..bda67750 100644 --- a/src/meta/ps2_vag.c +++ b/src/meta/ps2_vag.c @@ -1,101 +1,66 @@ #include "meta.h" #include "../util.h" -/* VAG - PS2 SVAG format is an interleaved format found in many SONY Games - The header start with a "VAG" id and is follow by : +static int vag_find_loop_points(STREAMFILE *streamFile, off_t * loop_start, off_t * loop_end); - i : interleaved format +/* PS2 VAG format, found in many Sony games 2008-05-17 - Fastelbja : First version ... */ - VGMSTREAM * init_vgmstream_ps2_vag(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; char filename[PATH_LIMIT]; - - // used for loop points ... - uint8_t eofVAG[16]={0x00,0x07,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77}; - uint8_t eofVAG2[16]={0x00,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; - uint8_t readbuf[16]; - - off_t readOffset = 0x20; off_t loopStart = 0; off_t loopEnd = 0; uint8_t vagID; off_t start_offset; - size_t fileLength; size_t interleave; int loop_flag=0; - int channel_count=1; + int channel_count=0; int i; + int loop_samples_found = 0; /* check extension, case insensitive */ streamFile->get_name(streamFile,filename,sizeof(filename)); if (strcasecmp("vag",filename_extension(filename))) goto fail; /* check VAG Header */ - if (((read_32bitBE(0x00,streamFile) & 0xFFFFFF00) != 0x56414700) && + if (((read_32bitBE(0x00,streamFile) & 0xFFFFFF00) != 0x56414700) && /* "VAG\0" */ ((read_32bitLE(0x00,streamFile) & 0xFFFFFF00) != 0x56414700)) goto fail; - /* Check for correct channel count */ + /* Check for correct channel count and loop flag */ vagID=read_8bit(0x03,streamFile); switch(vagID) { - case '1': + case '1': /* "VAG1" (1 channel) */ channel_count=1; break; - case '2': + case '2': /* "VAG2" (2 channels) */ channel_count=2; break; - case 'i': + case 'i': /* "VAGi" (interleaved) */ channel_count=2; break; - case 'V': - if(read_32bitBE(0x20,streamFile)==0x53746572) // vag Stereo + case 'V': /* pGAV (little endian header) */ + if(read_32bitBE(0x20,streamFile)==0x53746572) /* "Ster" */ channel_count=2; - case 'p': + else + channel_count=1; + break; + case 'p': /* "VAGp" (extended) */ if((read_32bitBE(0x04,streamFile)<=0x00000004) && (read_32bitBE(0x0c,streamFile)<(get_streamfile_size(streamFile)/2))) { loop_flag=(read_32bitBE(0x14,streamFile)!=0); channel_count=2; - } else { - /* Search for loop in VAG */ - fileLength = get_streamfile_size(streamFile); - - do { - readOffset+=0x10; - - // Loop Start ... - if(read_8bit(readOffset+0x01,streamFile)==0x06) { - if(loopStart==0) loopStart = readOffset; - } - - // Loop End ... - if(read_8bit(readOffset+0x01,streamFile)==0x03) { - if(loopEnd==0) loopEnd = readOffset; - } - - // Loop from end to beginning ... - if((read_8bit(readOffset+0x01,streamFile)==0x01)) { - // Check if we have the eof tag after the loop point ... - // if so we don't loop, if not present, we loop from end to start ... - read_streamfile(readbuf,readOffset+0x10,0x10,streamFile); - if((readbuf[0]!=0) && (readbuf[0]!=0x0c)) { - if(memcmp(readbuf,eofVAG,0x10) && (memcmp(readbuf,eofVAG2,0x10))) { - loopStart = 0x40; - loopEnd = readOffset; - } - } - } - - } while (streamFile->get_offset(streamFile)<(off_t)fileLength); - loop_flag = (loopEnd!=0); + } + else { + loop_flag = vag_find_loop_points(streamFile, &loopStart, &loopEnd); /* offset 0x30 */ + channel_count = 1; } break; default: @@ -108,8 +73,9 @@ VGMSTREAM * init_vgmstream_ps2_vag(STREAMFILE *streamFile) { /* fill in the vital statistics */ vgmstream->channels = channel_count; + vgmstream->coding_type = coding_PSX; - switch(vagID) { + switch(vagID) { case '1': // VAG1 vgmstream->layout_type=layout_none; vgmstream->sample_rate = read_32bitBE(0x10,streamFile); @@ -137,7 +103,7 @@ VGMSTREAM * init_vgmstream_ps2_vag(STREAMFILE *streamFile) { break; case 'p': // VAGp vgmstream->sample_rate = read_32bitBE(0x10,streamFile); - interleave=0x10; // used for loop calc + interleave=0x10; if((read_32bitBE(0x04,streamFile)==0x00000004) && (read_32bitBE(0x0c,streamFile)<(get_streamfile_size(streamFile)/2))) { vgmstream->channels=2; @@ -146,6 +112,7 @@ VGMSTREAM * init_vgmstream_ps2_vag(STREAMFILE *streamFile) { if(loop_flag) { vgmstream->loop_start_sample=read_32bitBE(0x14,streamFile); vgmstream->loop_end_sample =read_32bitBE(0x18,streamFile); + loop_samples_found = 1; } start_offset=0x80; @@ -158,8 +125,8 @@ VGMSTREAM * init_vgmstream_ps2_vag(STREAMFILE *streamFile) { interleave=0x1000; start_offset=0; } - - } else { + } + else { vgmstream->layout_type=layout_none; vgmstream->num_samples = read_32bitBE(0x0C,streamFile)/16*28; vgmstream->meta_type=meta_PS2_VAGp; @@ -186,20 +153,16 @@ VGMSTREAM * init_vgmstream_ps2_vag(STREAMFILE *streamFile) { vgmstream->interleave_block_size=interleave; /* Don't add the header size to loop calc points */ - if(vgmstream->meta_type!=meta_PS2_VAGs) { + if(loop_flag && !loop_samples_found) { loopStart-=start_offset; loopEnd-=start_offset; - if(loop_flag!=0) { - vgmstream->loop_start_sample = (int32_t)((loopStart/(interleave*channel_count))*interleave)/16*28; - vgmstream->loop_start_sample += (int32_t)(loopStart%(interleave*channel_count))/16*28; - vgmstream->loop_end_sample = (int32_t)((loopEnd/(interleave*channel_count))*interleave)/16*28; - vgmstream->loop_end_sample += (int32_t)(loopEnd%(interleave*channel_count))/16*28; - } + vgmstream->loop_start_sample = (int32_t)((loopStart/(interleave*channel_count))*interleave)/16*28; + vgmstream->loop_start_sample += (int32_t)(loopStart%(interleave*channel_count))/16*28; + vgmstream->loop_end_sample = (int32_t)((loopEnd/(interleave*channel_count))*interleave)/16*28; + vgmstream->loop_end_sample += (int32_t)(loopEnd%(interleave*channel_count))/16*28; } - /* Compression Scheme */ - vgmstream->coding_type = coding_PSX; /* open the file for reading by each channel */ { @@ -225,3 +188,61 @@ fail: if (vgmstream) close_vgmstream(vgmstream); return NULL; } + + +/** + * Finds loop points in VAG data using flag markers and updates loop_start and loop_end with the offsets. + * + * returns 0 if not found + */ +static int vag_find_loop_points(STREAMFILE *streamFile, off_t * loop_start, off_t * loop_end) { + off_t loopStart = 0; + off_t loopEnd = 0; + + /* used for loop points ... */ + uint8_t eofVAG[16]={0x00,0x07,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77}; + uint8_t eofVAG2[16]={0x00,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; + uint8_t readbuf[16]; + + /* Search for loop in VAG */ + size_t fileLength = get_streamfile_size(streamFile); + + + off_t readOffset = 0x20; + do { + readOffset+=0x10; + + // Loop Start ... + if(read_8bit(readOffset+0x01,streamFile)==0x06) { + if(loopStart==0) loopStart = readOffset; + } + + // Loop End ... + if(read_8bit(readOffset+0x01,streamFile)==0x03) { + if(loopEnd==0) loopEnd = readOffset; + } + + // Loop from end to beginning ... + if((read_8bit(readOffset+0x01,streamFile)==0x01)) { + // Check if we have the eof tag after the loop point ... + // if so we don't loop, if not present, we loop from end to start ... + read_streamfile(readbuf,readOffset+0x10,0x10,streamFile); + if((readbuf[0]!=0) && (readbuf[0]!=0x0c)) { + if(memcmp(readbuf,eofVAG,0x10) && (memcmp(readbuf,eofVAG2,0x10))) { + loopStart = 0x40; + loopEnd = readOffset; + } + } + } + + } while (streamFile->get_offset(streamFile)<(off_t)fileLength); + + + if (loopEnd) { + *loop_start = loopStart; + *loop_end = loopEnd; + return 1; + } + + return 0; +} From 4c25807b3dd8b015a20b2e8852cf28904967d48f Mon Sep 17 00:00:00 2001 From: bnnm Date: Wed, 21 Dec 2016 00:48:47 +0100 Subject: [PATCH 02/15] Prepare loop finder for HEVAG and minor touches --- src/meta/ps2_vag.c | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/src/meta/ps2_vag.c b/src/meta/ps2_vag.c index bda67750..07e5207a 100644 --- a/src/meta/ps2_vag.c +++ b/src/meta/ps2_vag.c @@ -2,7 +2,7 @@ #include "../util.h" -static int vag_find_loop_points(STREAMFILE *streamFile, off_t * loop_start, off_t * loop_end); +static int vag_find_loop_points(STREAMFILE *streamFile, off_t * loop_start, off_t * loop_end, off_t offset); /* PS2 VAG format, found in many Sony games @@ -59,7 +59,7 @@ VGMSTREAM * init_vgmstream_ps2_vag(STREAMFILE *streamFile) { channel_count=2; } else { - loop_flag = vag_find_loop_points(streamFile, &loopStart, &loopEnd); /* offset 0x30 */ + loop_flag = vag_find_loop_points(streamFile, &loopStart, &loopEnd, 0x30); channel_count = 1; } break; @@ -195,7 +195,7 @@ fail: * * returns 0 if not found */ -static int vag_find_loop_points(STREAMFILE *streamFile, off_t * loop_start, off_t * loop_end) { +static int vag_find_loop_points(STREAMFILE *streamFile, off_t * loop_start, off_t * loop_end, off_t offset) { off_t loopStart = 0; off_t loopEnd = 0; @@ -203,34 +203,41 @@ static int vag_find_loop_points(STREAMFILE *streamFile, off_t * loop_start, off_ uint8_t eofVAG[16]={0x00,0x07,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77}; uint8_t eofVAG2[16]={0x00,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; uint8_t readbuf[16]; + uint8_t flag; /* Search for loop in VAG */ size_t fileLength = get_streamfile_size(streamFile); - off_t readOffset = 0x20; + off_t readOffset = offset - 0x10; do { readOffset+=0x10; + flag = read_8bit(readOffset+0x01,streamFile) & 0x0F; /* lower nibble (for HEVAG) */ + // Loop Start ... - if(read_8bit(readOffset+0x01,streamFile)==0x06) { - if(loopStart==0) loopStart = readOffset; + if (flag == 0x06 && !loopStart) { + loopStart = readOffset; } // Loop End ... - if(read_8bit(readOffset+0x01,streamFile)==0x03) { - if(loopEnd==0) loopEnd = readOffset; + if (flag == 0x03 && !loopEnd) { + loopEnd = readOffset; + + if (loopStart && loopEnd) + break; } // Loop from end to beginning ... - if((read_8bit(readOffset+0x01,streamFile)==0x01)) { + if (flag == 0x01) { // Check if we have the eof tag after the loop point ... // if so we don't loop, if not present, we loop from end to start ... - read_streamfile(readbuf,readOffset+0x10,0x10,streamFile); - if((readbuf[0]!=0) && (readbuf[0]!=0x0c)) { - if(memcmp(readbuf,eofVAG,0x10) && (memcmp(readbuf,eofVAG2,0x10))) { - loopStart = 0x40; + int read = read_streamfile(readbuf,readOffset+0x10,0x10,streamFile); + if(read > 0 && readbuf[0]!=0x00 && readbuf[0]!=0x0c) { /* is there valid data after flag 0x1? */ + if(memcmp(readbuf,eofVAG,0x10) && (memcmp(readbuf,eofVAG2,0x10))) { /* probably could just check flag 0x7 */ + loopStart = offset + 0x10; loopEnd = readOffset; + break; } } } From 0b3050cf0a5797f00703a0ce3fd1e307f60e4708 Mon Sep 17 00:00:00 2001 From: bnnm Date: Wed, 21 Dec 2016 20:44:16 +0100 Subject: [PATCH 03/15] Added PSVita HEVAG (original algorithm by daemon1) --- src/coding/coding.h | 2 + src/coding/psx_decoder.c | 255 +++++++++++++++++++++++++++++++++++++-- src/meta/ps2_vag.c | 12 ++ src/vgmstream.c | 14 +++ src/vgmstream.h | 5 + 5 files changed, 275 insertions(+), 13 deletions(-) diff --git a/src/coding/coding.h b/src/coding/coding.h index 400f2eb8..f553c4f7 100644 --- a/src/coding/coding.h +++ b/src/coding/coding.h @@ -60,6 +60,8 @@ void decode_ffxi_adpcm(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelsp void decode_baf_adpcm(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); +void decode_hevag_adpcm(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); + void decode_xa(VGMSTREAM * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); void init_get_high_nibble(VGMSTREAM * vgmstream); diff --git a/src/coding/psx_decoder.c b/src/coding/psx_decoder.c index 2687bfef..21bc0b8c 100644 --- a/src/coding/psx_decoder.c +++ b/src/coding/psx_decoder.c @@ -2,17 +2,178 @@ #include "coding.h" #include "../util.h" -double VAG_f[5][2] = { { 0.0 , 0.0 }, - { 60.0 / 64.0 , 0.0 }, - { 115.0 / 64.0 , -52.0 / 64.0 }, - { 98.0 / 64.0 , -55.0 / 64.0 } , - { 122.0 / 64.0 , -60.0 / 64.0 } } ; -int32_t VAG_coefs[5][2] = { { 0 , 0 }, - { 60 , 0 }, - { 115 , -52 }, - { 98 , -55 } , - { 122 , -60 } } ; +/* for some algos, maybe closer to the real thing */ +#define VAG_USE_INTEGER_TABLE 0 + +/* PS ADPCM table (precalculated divs) */ +static const double VAG_f[5][2] = { + { 0.0 , 0.0 }, + { 60.0 / 64.0 , 0.0 }, + { 115.0 / 64.0 , -52.0 / 64.0 }, + { 98.0 / 64.0 , -55.0 / 64.0 }, + { 122.0 / 64.0 , -60.0 / 64.0 } +}; +#if VAG_USE_INTEGER_TABLE +/* PS ADPCM table */ +static const int8_t VAG_coefs[5][2] = { + { 0 , 0 }, + { 60 , 0 }, + { 115 , -52 }, + { 98 , -55 }, + { 122 , -60 } +}; +#endif + + +/* 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 VAG ADPCM, decodes 16 bytes into 28 samples. + * The first 2 bytes are a header (shift, predictor, optional flag) + * + * Flags: + * 0x0: Nothing + * 0x1: End marker + decode + * 0x2: Loop region + * 0x3: Loop end + * 0x4: Start marker + * 0x5: ? + * 0x6: Loop start + * 0x7: End marker + don't decode + * 0x8+ Not valid + */ void decode_psx(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) { int predict_nr, shift_factor, sample; @@ -28,7 +189,7 @@ void decode_psx(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, predict_nr = read_8bit(stream->offset+framesin*16,stream->streamfile) >> 4; shift_factor = read_8bit(stream->offset+framesin*16,stream->streamfile) & 0xf; - flag = read_8bit(stream->offset+framesin*16+1,stream->streamfile); + flag = read_8bit(stream->offset+framesin*16+1,stream->streamfile); /* only lower nibble needed */ first_sample = first_sample % 28; @@ -40,7 +201,7 @@ void decode_psx(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, short sample_byte = (short)read_8bit(stream->offset+(framesin*16)+2+i/2,stream->streamfile); - scale = ((i&1 ? + scale = ((i&1 ? /* odd/even byte */ sample_byte >> 4 : sample_byte & 0x0f)<<12); @@ -162,7 +323,7 @@ void decode_ffxi_adpcm(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelsp sample_byte >> 4 : sample_byte & 0x0f)<<12); -#if 1 +#if !VAG_USE_INTEGER_TABLE predictor = (int)((hist1*VAG_f[predict_nr][0]+hist2*VAG_f[predict_nr][1])); #else @@ -212,3 +373,71 @@ void decode_baf_adpcm(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspa stream->adpcm_history1_32=hist1; stream->adpcm_history2_32=hist2; } + + +/** + * Sony's HEVAG (High Efficiency VAG) ADPCM, used in PSVita games (hardware decoded). + * Variation of the regular VAG, uses 4 history samples and a bigger table. + * + * Original research and algorithm by id-daemon / daemon1. + */ +void decode_hevag_adpcm(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) { + + uint8_t predict_nr, shift, flag, byte; + int32_t scale = 0; + + int32_t sample; + int32_t hist1 = stream->adpcm_history1_32; + int32_t hist2 = stream->adpcm_history2_32; + int32_t hist3 = stream->adpcm_history3_32; + int32_t hist4 = stream->adpcm_history4_32; + + int i, sample_count; + + + int framesin = first_sample / 28; + + /* 4 byte header: predictor = 3rd and 1st, shift = 2nd, flag = 4th */ + byte = (uint8_t)read_8bit(stream->offset+framesin*16+0,stream->streamfile); + predict_nr = byte >> 4; + shift = byte & 0x0f; + byte = (uint8_t)read_8bit(stream->offset+framesin*16+1,stream->streamfile); + predict_nr = (byte & 0xF0) | predict_nr; + flag = byte & 0x0f; /* no change in flags */ + + first_sample = first_sample % 28; + + for (i = first_sample, sample_count = 0; i < first_sample + samples_to_do; i++, sample_count += channelspacing) { + sample = 0; + + if (flag < 7 && predict_nr < 128) { + + if (i & 1) {/* odd/even nibble */ + scale = byte >> 4; + } else { + byte = read_8bit(stream->offset+(framesin*16)+2+i/2,stream->streamfile); + scale = byte & 0x0f; + } + if (scale > 7) { /* sign fix */ + scale = scale - 16; + } + + sample = (hist1 * HEVAG_coefs[predict_nr][0] + + hist2 * HEVAG_coefs[predict_nr][1] + + hist3 * HEVAG_coefs[predict_nr][2] + + hist4 * HEVAG_coefs[predict_nr][3] ) / 32; + sample = (sample + (scale << (20 - shift)) + 128) >> 8; + } + + outbuf[sample_count] = clamp16(sample); + hist4 = hist3; + hist3 = hist2; + hist2 = hist1; + hist1 = sample; + } + + stream->adpcm_history1_32 = hist1; + stream->adpcm_history2_32 = hist2; + stream->adpcm_history3_32 = hist3; + stream->adpcm_history4_32 = hist4; +} diff --git a/src/meta/ps2_vag.c b/src/meta/ps2_vag.c index 07e5207a..98586dc3 100644 --- a/src/meta/ps2_vag.c +++ b/src/meta/ps2_vag.c @@ -58,6 +58,10 @@ VGMSTREAM * init_vgmstream_ps2_vag(STREAMFILE *streamFile) { loop_flag=(read_32bitBE(0x14,streamFile)!=0); channel_count=2; } + else if (read_32bitBE(0x04,streamFile) == 0x00020001) { /* HEVAG */ + loop_flag = vag_find_loop_points(streamFile, &loopStart, &loopEnd, 0x30); + channel_count = read_8bit(0x1e,streamFile); + } else { loop_flag = vag_find_loop_points(streamFile, &loopStart, &loopEnd, 0x30); channel_count = 1; @@ -125,6 +129,14 @@ VGMSTREAM * init_vgmstream_ps2_vag(STREAMFILE *streamFile) { interleave=0x1000; start_offset=0; } + } + else if (read_32bitBE(0x04,streamFile) == 0x00020001) { /* HEVAG */ + vgmstream->meta_type = meta_PS2_VAGs; + vgmstream->coding_type = coding_HEVAG_ADPCM; + vgmstream->layout_type = layout_interleave; + + vgmstream->num_samples = read_32bitBE(0x0C,streamFile) / channel_count / 16 * 28; + start_offset=0x30; } else { vgmstream->layout_type=layout_none; diff --git a/src/vgmstream.c b/src/vgmstream.c index 47b492df..c6439713 100644 --- a/src/vgmstream.c +++ b/src/vgmstream.c @@ -1032,6 +1032,7 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) { case coding_PSX: case coding_PSX_badflags: case coding_invert_PSX: + case coding_HEVAG_ADPCM: case coding_XA: return 28; case coding_XBOX: @@ -1159,6 +1160,7 @@ int get_vgmstream_frame_size(VGMSTREAM * vgmstream) { return 9; case coding_PSX: case coding_PSX_badflags: + case coding_HEVAG_ADPCM: case coding_invert_PSX: case coding_NDS_PROCYON: return 16; @@ -1435,6 +1437,13 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to samples_to_do); } break; + case coding_HEVAG_ADPCM: + for (chan=0;chanchannels;chan++) { + decode_hevag_adpcm(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, + vgmstream->channels,vgmstream->samples_into_block, + samples_to_do); + } + break; case coding_XA: for (chan=0;chanchannels;chan++) { decode_xa(vgmstream,buffer+samples_written*vgmstream->channels+chan, @@ -1746,6 +1755,8 @@ int vgmstream_do_loop(VGMSTREAM * vgmstream) { vgmstream->loop_ch[i].adpcm_history2_32 = vgmstream->ch[i].adpcm_history2_32; } } + /* todo preserve hevag, baf_adpcm, etc history? */ + #ifdef DEBUG { int i; @@ -1960,6 +1971,9 @@ void describe_vgmstream(VGMSTREAM * vgmstream, char * desc, int length) { case coding_BAF_ADPCM: snprintf(temp,TEMPSIZE,"Bizarre Creations Playstation-ish 4-bit ADPCM"); break; + case coding_HEVAG_ADPCM: + snprintf(temp,TEMPSIZE,"PSVita HEVAG ADPCM"); + break; case coding_XA: snprintf(temp,TEMPSIZE,"CD-ROM XA 4-bit ADPCM"); break; diff --git a/src/vgmstream.h b/src/vgmstream.h index b1045bf3..7dcddd27 100644 --- a/src/vgmstream.h +++ b/src/vgmstream.h @@ -93,6 +93,7 @@ typedef enum { coding_PSX_badflags, /* with garbage in the flags byte */ coding_FFXI, /* FF XI PSX-ish ADPCM */ coding_BAF_ADPCM, /* Bizarre Creations PSX-ish ADPCM */ + coding_HEVAG_ADPCM, /* PSVita games */ coding_XA, /* PSX CD-XA */ coding_XBOX, /* XBOX IMA */ coding_INT_XBOX, /* XBOX 'real interleaved' IMA */ @@ -633,6 +634,10 @@ typedef struct { int16_t adpcm_history3_16; int32_t adpcm_history3_32; }; + union { + int16_t adpcm_history4_16; + int32_t adpcm_history4_32; + }; double adpcm_history1_double; double adpcm_history2_double; From 08d3e7c62f5b10f091418fb541614c33e1c2f705 Mon Sep 17 00:00:00 2001 From: bnnm Date: Wed, 21 Dec 2016 20:46:24 +0100 Subject: [PATCH 04/15] Don't forget extlibs --- BUILD.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/BUILD.md b/BUILD.md index 5f3e927c..319f5a5c 100644 --- a/BUILD.md +++ b/BUILD.md @@ -98,4 +98,5 @@ For new simple formats, assuming existing layout/coding: *xml-vgmstream/DllMain.c*: add new extension to the format list - *src/Makefile* *src/meta/Makefile.unix.am* - *src/libvgmstream.vcproj/vcxproj*: to compile new (format-name).c parser + *src/libvgmstream.vcproj/vcxproj/filters*: to compile new (format-name).c parser +- if the format needs an external library don't forget to make it optional with: *#ifdef VGM_USE_X ... #endif* From ea7b8d25706a6df58004350408fe9b57f03b4ae8 Mon Sep 17 00:00:00 2001 From: bnnm Date: Wed, 21 Dec 2016 23:00:34 +0100 Subject: [PATCH 05/15] Added short VAG variation (SGXD type 5 found in PS3 Afrika) --- src/coding/coding.h | 2 ++ src/coding/psx_decoder.c | 60 ++++++++++++++++++++++++++++++++++++++-- src/meta/ps3_sgh_sgb.c | 17 +++--------- src/vgmstream.c | 14 ++++++++++ src/vgmstream.h | 1 + 5 files changed, 78 insertions(+), 16 deletions(-) diff --git a/src/coding/coding.h b/src/coding/coding.h index f553c4f7..92efe52d 100644 --- a/src/coding/coding.h +++ b/src/coding/coding.h @@ -62,6 +62,8 @@ void decode_baf_adpcm(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspa void decode_hevag_adpcm(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); +void decode_short_vag_adpcm(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); + void decode_xa(VGMSTREAM * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); void init_get_high_nibble(VGMSTREAM * vgmstream); diff --git a/src/coding/psx_decoder.c b/src/coding/psx_decoder.c index 21bc0b8c..c8484269 100644 --- a/src/coding/psx_decoder.c +++ b/src/coding/psx_decoder.c @@ -14,7 +14,6 @@ static const double VAG_f[5][2] = { { 98.0 / 64.0 , -55.0 / 64.0 }, { 122.0 / 64.0 , -60.0 / 64.0 } }; -#if VAG_USE_INTEGER_TABLE /* PS ADPCM table */ static const int8_t VAG_coefs[5][2] = { { 0 , 0 }, @@ -23,7 +22,6 @@ static const int8_t VAG_coefs[5][2] = { { 98 , -55 }, { 122 , -60 } }; -#endif /* PSVita ADPCM table */ @@ -418,7 +416,7 @@ void decode_hevag_adpcm(VGMSTREAMCHANNEL * stream, sample * outbuf, int channels byte = read_8bit(stream->offset+(framesin*16)+2+i/2,stream->streamfile); scale = byte & 0x0f; } - if (scale > 7) { /* sign fix */ + if (scale > 7) { /* sign extend */ scale = scale - 16; } @@ -441,3 +439,59 @@ void decode_hevag_adpcm(VGMSTREAMCHANNEL * stream, sample * outbuf, int channels stream->adpcm_history3_32 = hist3; stream->adpcm_history4_32 = hist4; } + + +/** + * Short VAG ADPCM, found in PS3 Afrika (SGDX type 5). + * Uses 8 byte blocks and no flag. + */ +void decode_short_vag_adpcm(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) { + + uint8_t predict_nr, shift, byte; + int16_t scale = 0; + + int32_t sample; + int32_t hist1 = stream->adpcm_history1_32; + int32_t hist2 = stream->adpcm_history2_32; + + int i, sample_count; + + + int framesin = first_sample / 6; + + /* 2 byte header: predictor = 1st, shift = 2nd */ + byte = (uint8_t)read_8bit(stream->offset+framesin*8+0,stream->streamfile); + predict_nr = byte >> 4; + shift = byte & 0x0f; + + first_sample = first_sample % 6; + + for (i = first_sample, sample_count = 0; i < first_sample + samples_to_do; i++, sample_count += channelspacing) { + sample = 0; + + if (predict_nr < 5) { + + if (i & 1) {/* odd/even nibble */ + scale = byte >> 4; + } else { + byte = (uint8_t)read_8bit(stream->offset+(framesin*8)+1+i/2,stream->streamfile); + scale = (byte & 0x0f); + } + /*if (scale > 7) { + scale = scale - 16; + }*/ + scale = scale << 12; /* shift + sign extend only if scale is int16_t */ + + sample = (hist1 * VAG_coefs[predict_nr][0] + + hist2 * VAG_coefs[predict_nr][1] ) / 64; + sample = sample + (scale >> shift); + } + + outbuf[sample_count] = clamp16(sample); + hist2 = hist1; + hist1 = sample; + } + + stream->adpcm_history1_32 = hist1; + stream->adpcm_history2_32 = hist2; +} diff --git a/src/meta/ps3_sgh_sgb.c b/src/meta/ps3_sgh_sgb.c index e299df3b..ce0ec106 100644 --- a/src/meta/ps3_sgh_sgb.c +++ b/src/meta/ps3_sgh_sgb.c @@ -194,21 +194,12 @@ VGMSTREAM * init_vgmstream_ps3_sgdx(STREAMFILE *streamFile) { break; } #endif - - case 0x05: /* todo PCM? */ - goto fail; - - /* - vgmstream->coding_type = coding_PCM16LE; - if (vgmstream->channels > 1) { - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = 0x1; - } else { - vgmstream->layout_type = layout_none; - } + case 0x05: /* Short VAG ADPCM */ + vgmstream->coding_type = coding_SHORT_VAG_ADPCM; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x4; break; - */ #ifdef VGM_USE_FFMPEG case 0x06: /* AC3 */ diff --git a/src/vgmstream.c b/src/vgmstream.c index c6439713..0595d37a 100644 --- a/src/vgmstream.c +++ b/src/vgmstream.c @@ -1035,6 +1035,8 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) { case coding_HEVAG_ADPCM: case coding_XA: return 28; + case coding_SHORT_VAG_ADPCM: + return 6; case coding_XBOX: case coding_INT_XBOX: case coding_BAF_ADPCM: @@ -1164,6 +1166,8 @@ int get_vgmstream_frame_size(VGMSTREAM * vgmstream) { case coding_invert_PSX: case coding_NDS_PROCYON: return 16; + case coding_SHORT_VAG_ADPCM: + return 4; case coding_XA: return 14*vgmstream->channels; case coding_XBOX: @@ -1444,6 +1448,13 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to samples_to_do); } break; + case coding_SHORT_VAG_ADPCM: + for (chan=0;chanchannels;chan++) { + decode_short_vag_adpcm(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, + vgmstream->channels,vgmstream->samples_into_block, + samples_to_do); + } + break; case coding_XA: for (chan=0;chanchannels;chan++) { decode_xa(vgmstream,buffer+samples_written*vgmstream->channels+chan, @@ -1974,6 +1985,9 @@ void describe_vgmstream(VGMSTREAM * vgmstream, char * desc, int length) { case coding_HEVAG_ADPCM: snprintf(temp,TEMPSIZE,"PSVita HEVAG ADPCM"); break; + case coding_SHORT_VAG_ADPCM: + snprintf(temp,TEMPSIZE,"Short VAG (SGXD type 5) ADPCM"); + break; case coding_XA: snprintf(temp,TEMPSIZE,"CD-ROM XA 4-bit ADPCM"); break; diff --git a/src/vgmstream.h b/src/vgmstream.h index 7dcddd27..58930651 100644 --- a/src/vgmstream.h +++ b/src/vgmstream.h @@ -94,6 +94,7 @@ typedef enum { coding_FFXI, /* FF XI PSX-ish ADPCM */ coding_BAF_ADPCM, /* Bizarre Creations PSX-ish ADPCM */ coding_HEVAG_ADPCM, /* PSVita games */ + coding_SHORT_VAG_ADPCM, /* SGXD type 5 (PS3 Afrika) */ coding_XA, /* PSX CD-XA */ coding_XBOX, /* XBOX IMA */ coding_INT_XBOX, /* XBOX 'real interleaved' IMA */ From 12760d2183695d3e32226b7679fe875c17f82b9f Mon Sep 17 00:00:00 2001 From: bnnm Date: Thu, 22 Dec 2016 23:25:18 +0100 Subject: [PATCH 06/15] Sanify loops: ignore negative start --- src/vgmstream.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/vgmstream.c b/src/vgmstream.c index 0595d37a..82e8c955 100644 --- a/src/vgmstream.c +++ b/src/vgmstream.c @@ -376,8 +376,9 @@ VGMSTREAM * init_vgmstream_internal(STREAMFILE *streamFile, int do_dfs) { /* Sanify loops! */ if (vgmstream->loop_flag) { - if ((vgmstream->loop_end_sample <= vgmstream->loop_start_sample) || - (vgmstream->loop_end_sample > vgmstream->num_samples)) + if ((vgmstream->loop_end_sample <= vgmstream->loop_start_sample) + || (vgmstream->loop_end_sample > vgmstream->num_samples) + || (vgmstream->loop_start_sample < 0) ) vgmstream->loop_flag = 0; } From 973c4bff1d64993e99b08c26f3a03ba4867f0a5b Mon Sep 17 00:00:00 2001 From: bnnm Date: Mon, 26 Dec 2016 13:27:47 +0100 Subject: [PATCH 07/15] Added namco XMA (.xma), Soul Calibur II HD XMA (.past) variations --- src/meta/xma.c | 128 +++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 107 insertions(+), 21 deletions(-) diff --git a/src/meta/xma.c b/src/meta/xma.c index 18444e81..32a7a31d 100644 --- a/src/meta/xma.c +++ b/src/meta/xma.c @@ -23,6 +23,7 @@ typedef struct { int32_t fmt_codec; uint8_t xma2_version; int needs_header; + int force_little_endian; /* FFmpeg can't parse big endian "fmt" chunks */ /* info */ int loop_flag; @@ -39,6 +40,7 @@ typedef struct { static int parse_header(xma_header_data * xma, STREAMFILE *streamFile); static void parse_xma1_sample_data(xma_header_data * xma, STREAMFILE *streamFile); static int create_riff_header(uint8_t * buf, size_t buf_size, xma_header_data * xma, STREAMFILE *streamFile); +static int fmt_chunk_swap_endian(uint8_t * chunk, uint16_t codec); #if ADJUST_SAMPLE_RATE static int get_xma_sample_rate(int32_t general_rate); #endif @@ -61,7 +63,9 @@ VGMSTREAM * init_vgmstream_xma(STREAMFILE *streamFile) { /* check extension, case insensitive */ streamFile->get_name(streamFile,filename,sizeof(filename)); if (strcasecmp("xma",filename_extension(filename)) - && strcasecmp("xma2",filename_extension(filename)) ) /* Skullgirls */ + && strcasecmp("xma2",filename_extension(filename)) /* Skullgirls */ + && strcasecmp("past",filename_extension(filename)) /* SoulCalibur II HD */ + ) goto fail; /* check header */ @@ -74,7 +78,7 @@ VGMSTREAM * init_vgmstream_xma(STREAMFILE *streamFile) { fake_riff_size = create_riff_header(fake_riff, FAKE_RIFF_BUFFER_SIZE, &xma, streamFile); if (fake_riff_size <= 0) goto fail; - data = init_ffmpeg_header_offset(streamFile, fake_riff, (uint64_t)fake_riff_size, xma.data_offset+4+4, xma.data_size); + data = init_ffmpeg_header_offset(streamFile, fake_riff, (uint64_t)fake_riff_size, xma.data_offset, xma.data_size); if (!data) goto fail; } else { /* no change */ @@ -130,20 +134,28 @@ static int parse_header(xma_header_data * xma, STREAMFILE *streamFile) { int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL; int16_t (*read_16bit)(off_t,STREAMFILE*) = NULL; uint32_t id; - enum { RIFF } header_type; int big_endian = 0; + enum { + id_RIFF = UINT32_C(0x52494646), /* "RIFF" */ + id_RIFX = UINT32_C(0x52494658), /* "RIFX" */ + id_NXMA = UINT32_C(0x786D6100), /* "xma\0" */ + id_PASX = UINT32_C(0x50415358), /* "PASX" */ + }; /* check header */ id = read_32bitBE(0x00,streamFile); - if (id == 0x52494646 || id == 0x52494658) { /* "RIFF" / "RIFX" */ - big_endian = id == 0x52494658; - header_type = RIFF; + switch (id) { + case id_RIFF: + break; + case id_RIFX: + case id_NXMA: + case id_PASX: + big_endian = 1; + break; + default: + goto fail; } - else { - goto fail; - } - memset(xma,0,sizeof(xma_header_data)); xma->big_endian = big_endian; @@ -159,7 +171,7 @@ static int parse_header(xma_header_data * xma, STREAMFILE *streamFile) { xma->file_size = streamFile->get_size(streamFile); /* find offsets */ - if (header_type == RIFF) { /* regular RIFF header */ + if (id == id_RIFF || id == id_RIFX) { /* regular RIFF header */ off_t current_chunk = 0xc; off_t fmt_offset = 0, xma2_offset = 0; size_t riff_size = 0, fmt_size = 0, xma2_size = 0; @@ -184,7 +196,7 @@ static int parse_header(xma_header_data * xma, STREAMFILE *streamFile) { case 0x64617461: /* "data" */ if (xma->data_offset) goto fail; - xma->data_offset = current_chunk; + xma->data_offset = current_chunk + 4 + 4; xma->data_size = chunk_size; break; case 0x584D4132: /* "XMA2" */ @@ -210,10 +222,42 @@ static int parse_header(xma_header_data * xma, STREAMFILE *streamFile) { xma->chunk_offset = fmt_offset; xma->chunk_size = fmt_size; xma->fmt_codec = read_16bit(xma->chunk_offset,streamFile); + xma->force_little_endian = xma->big_endian; } else { goto fail; } - } else { + } + else if (id == id_NXMA) { /* Namco (Tekken 6, Galaga Legions DX) */ + /* custom header with a "XMA2" or "fmt " data chunk inside, most other values are unknown */ + uint32_t chunk_type = read_32bit(0xC,streamFile); + xma->data_offset = 0x100; + xma->data_size = read_32bit(0x14,streamFile); + xma->chunk_offset = 0xBC; + xma->chunk_size = read_32bit(0x24,streamFile); + if (chunk_type == 0x4) { /* "XMA2" */ + xma->xma2_version = read_8bit(xma->chunk_offset,streamFile); + } else if (chunk_type == 0x8) { /* "fmt " */ + xma->fmt_codec = read_16bit(xma->chunk_offset,streamFile); + xma->force_little_endian = 1; + } else { + goto fail; + } + xma->needs_header = 1; + + if (xma->data_size + xma->data_offset > xma->file_size) goto fail; + } + else if (id == id_PASX) { /* SoulCalibur II HD */ + /* custom header with a "fmt " data chunk inside */ + xma->chunk_size = read_32bit(0x08,streamFile); + xma->data_size = read_32bit(0x0c,streamFile); + xma->chunk_offset = read_32bit(0x10,streamFile); + /* 0x14: chunk offset end */ + xma->data_offset = read_32bit(0x18,streamFile); + xma->fmt_codec = read_16bit(xma->chunk_offset,streamFile); + xma->needs_header = 1; + xma->force_little_endian = 1; + } + else { goto fail; } @@ -237,8 +281,8 @@ static int parse_header(xma_header_data * xma, STREAMFILE *streamFile) { xma->loop_flag = (uint8_t)read_8bit(xma->chunk_offset+0x30,streamFile) > 0 /* never set in practice */ || xma->loop_end_sample; /* not needed but may affect looping? (sometimes these don't match loop/total samples) */ - /* int32_t play_begin_sample = read_32bit(xma->fmt_offset+0x28,streamFile); */ - /* int32_t play_end_sample = play_begin_sample + read_32bit(xma->fmt_offset+0x24,streamFile); */ + /* int32_t play_begin_sample = read_32bit(xma->chunk_offset+0x20,streamFile); */ + /* int32_t play_end_sample = play_begin_sample + read_32bit(xma->chunk_offset+0x24,streamFile); */ } else if (xma->fmt_codec == 0x165) { /* pure XMA1 */ xma->loop_flag = (uint8_t)read_8bit(xma->chunk_offset+0xA,streamFile) > 0; @@ -248,7 +292,7 @@ static int parse_header(xma_header_data * xma, STREAMFILE *streamFile) { /* find samples count + loop samples since they are not in the header */ parse_xma1_sample_data(xma, streamFile); } - else { /* RIFF with no XMA data or unknown version */ + else { /* unknown chunk */ goto fail; } @@ -272,9 +316,9 @@ static void parse_xma1_sample_data(xma_header_data * xma, STREAMFILE *streamFile uint32_t size; uint32_t packet_size = XMA_BYTES_PER_PACKET; - uint32_t offset = xma->data_offset + 4 + 4; + uint32_t offset = xma->data_offset; uint32_t offset_b = 0; - uint32_t stream_offset_b = (xma->data_offset + 4 + 4) * 8; + uint32_t stream_offset_b = xma->data_offset * 8; size = offset + xma->data_size; packet_size_b = packet_size*8; @@ -333,7 +377,9 @@ static int create_riff_header(uint8_t * buf, size_t buf_size, xma_header_data * uint8_t internal[FAKE_RIFF_BUFFER_SIZE]; size_t head_size, file_size, internal_size; - if (xma->big_endian) { + int use_be = xma->big_endian && !xma->force_little_endian; + + if (use_be) { put_32bit = put_32bitBE; } else { put_32bit = put_32bitLE; @@ -347,7 +393,7 @@ static int create_riff_header(uint8_t * buf, size_t buf_size, xma_header_data * if (xma->xma2_version == 3) { /* old XMA2 v3: change to v4 (extra 8 bytes in the middle) */ internal_size = 4+4+xma->chunk_size + 8; - memcpy(internal + 0x0, "XMA2", 4); /* "XMA2" chunk (interal data is BE) */ + memcpy(internal + 0x0, "XMA2", 4); /* "XMA2" chunk (internal data is BE) */ put_32bit(internal + 0x4, xma->chunk_size + 8); /* v3 > v4 size*/ put_8bit(internal + 0x8, 4); /* v4 */ memcpy(internal + 0x9, chunk+1, 15); /* first v3 part (fixed) */ @@ -358,6 +404,11 @@ static int create_riff_header(uint8_t * buf, size_t buf_size, xma_header_data * else { /* direct copy (old XMA2 v4 ignoring "fmt", pure XMA1/2) */ internal_size = 4+4+xma->chunk_size; + if (xma->force_little_endian ) { + if ( !fmt_chunk_swap_endian(chunk, xma->fmt_codec) ) + goto fail; + } + memcpy(internal + 0x0, xma->xma2_version ? "XMA2" : "fmt ", 4); put_32bit(internal + 0x4, xma->chunk_size); memcpy(internal + 0x8, chunk, xma->chunk_size); @@ -368,7 +419,7 @@ static int create_riff_header(uint8_t * buf, size_t buf_size, xma_header_data * file_size = head_size-4-4 + xma->data_size; if (head_size > buf_size) goto fail; - memcpy(buf + 0x0, xma->big_endian ? "RIFX" : "RIFF", 4); + memcpy(buf + 0x0, use_be ? "RIFX" : "RIFF", 4); put_32bit(buf + 0x4, file_size); memcpy(buf + 0x8, "WAVE", 4); memcpy(buf + 0xc, internal, internal_size); @@ -382,6 +433,41 @@ fail: } +/** + * Swaps endianness + * + * returns 0 on error + */ +static int fmt_chunk_swap_endian(uint8_t * chunk, uint16_t codec) { + if (codec != 0x166) + goto fail; + + put_16bitLE(chunk + 0x00, get_16bitBE(chunk + 0x00));/*wFormatTag*/ + put_16bitLE(chunk + 0x02, get_16bitBE(chunk + 0x02));/*nChannels*/ + put_32bitLE(chunk + 0x04, get_32bitBE(chunk + 0x04));/*nSamplesPerSec*/ + put_32bitLE(chunk + 0x08, get_32bitBE(chunk + 0x08));/*nAvgBytesPerSec*/ + put_16bitLE(chunk + 0x0c, get_16bitBE(chunk + 0x0c));/*nBlockAlign*/ + put_16bitLE(chunk + 0x0e, get_16bitBE(chunk + 0x0e));/*wBitsPerSample*/ + put_16bitLE(chunk + 0x10, get_16bitBE(chunk + 0x10));/*cbSize*/ + put_16bitLE(chunk + 0x12, get_16bitBE(chunk + 0x12));/*NumStreams*/ + put_32bitLE(chunk + 0x14, get_32bitBE(chunk + 0x14));/*ChannelMask*/ + put_32bitLE(chunk + 0x18, get_32bitBE(chunk + 0x18));/*SamplesEncoded*/ + put_32bitLE(chunk + 0x1c, get_32bitBE(chunk + 0x1c));/*BytesPerBlock*/ + put_32bitLE(chunk + 0x20, get_32bitBE(chunk + 0x20));/*PlayBegin*/ + put_32bitLE(chunk + 0x24, get_32bitBE(chunk + 0x24));/*PlayLength*/ + put_32bitLE(chunk + 0x28, get_32bitBE(chunk + 0x28));/*LoopBegin*/ + put_32bitLE(chunk + 0x2c, get_32bitBE(chunk + 0x2c));/*LoopLength*/ + /* put_8bit(chunk + 0x30, get_8bit(chunk + 0x30));*//*LoopCount*/ + /* put_8bit(chunk + 0x31, get_8bit(chunk + 0x31));*//*EncoderVersion*/ + put_16bitLE(chunk + 0x32, get_16bitBE(chunk + 0x32));/*BlockCount*/ + + return 1; + +fail: + return 0; +} + + #if ADJUST_SAMPLE_RATE /** * Get real XMA sample rate (from Microsoft docs, apparently info only and not correct for playback). From 46d7fcfe80058c9e1afe7566bd04b10e8b0a82ea Mon Sep 17 00:00:00 2001 From: bnnm Date: Mon, 26 Dec 2016 13:30:43 +0100 Subject: [PATCH 08/15] Added Killzone VAGp (.vag); code cleanup --- src/meta/ps2_vag.c | 254 ++++++++++++++++++++++++--------------------- 1 file changed, 137 insertions(+), 117 deletions(-) diff --git a/src/meta/ps2_vag.c b/src/meta/ps2_vag.c index 98586dc3..7b9d3820 100644 --- a/src/meta/ps2_vag.c +++ b/src/meta/ps2_vag.c @@ -2,28 +2,29 @@ #include "../util.h" -static int vag_find_loop_points(STREAMFILE *streamFile, off_t * loop_start, off_t * loop_end, off_t offset); +static int vag_find_loop_offsets(STREAMFILE *streamFile, off_t start_offset, off_t * loop_start, off_t * loop_end); -/* PS2 VAG format, found in many Sony games - - 2008-05-17 - Fastelbja : First version ... +/** + * VAGp - SDK format, created by Sony's tools (like AIFF2VAG) */ VGMSTREAM * init_vgmstream_ps2_vag(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; char filename[PATH_LIMIT]; - off_t loopStart = 0; - off_t loopEnd = 0; + off_t loopStart = 0; + off_t loopEnd = 0; - uint8_t vagID; - off_t start_offset; + uint8_t vagID; + uint32_t version = 0; - size_t interleave; - + size_t filesize = 0, datasize = 0; + size_t interleave; + off_t start_offset; + int loop_flag=0; + int loop_samples_found = 0; int channel_count=0; int i; - int loop_samples_found = 0; /* check extension, case insensitive */ streamFile->get_name(streamFile,filename,sizeof(filename)); @@ -31,149 +32,167 @@ VGMSTREAM * init_vgmstream_ps2_vag(STREAMFILE *streamFile) { /* check VAG Header */ if (((read_32bitBE(0x00,streamFile) & 0xFFFFFF00) != 0x56414700) && /* "VAG\0" */ - ((read_32bitLE(0x00,streamFile) & 0xFFFFFF00) != 0x56414700)) + ((read_32bitLE(0x00,streamFile) & 0xFFFFFF00) != 0x56414700)) goto fail; - /* Check for correct channel count and loop flag */ - vagID=read_8bit(0x03,streamFile); + filesize = get_streamfile_size(streamFile); - switch(vagID) { - case '1': /* "VAG1" (1 channel) */ + /* version used to create the file + * ex: 00000000 = v1.8 PC, 00000002 = v1.3 Mac, 00000003 = v1.6+ Mac, 00000020 = v2.0+ PC */ + version = read_32bitBE(0x04,streamFile); + /* 0x08-0c: reserved */ + datasize = read_32bitBE(0x0c,streamFile); + /* 0x14-20 reserved */ + /* 0x20-30: name (optional) */ + /* 0x30: data start (first 0x10 usually 0s to init SPU) */ + + /* Check for correct channel count and loop flag */ + vagID=read_8bit(0x03,streamFile); + switch(vagID) { + case '1': /* "VAG1" (1 channel) [Metal Gear Solid 3] */ channel_count=1; break; - case '2': /* "VAG2" (2 channels) */ + case '2': /* "VAG2" (2 channels) [Metal Gear Solid 3] */ channel_count=2; break; - case 'i': /* "VAGi" (interleaved) */ - channel_count=2; - break; - case 'V': /* pGAV (little endian header) */ - if(read_32bitBE(0x20,streamFile)==0x53746572) /* "Ster" */ - channel_count=2; - else - channel_count=1; - break; - case 'p': /* "VAGp" (extended) */ - if((read_32bitBE(0x04,streamFile)<=0x00000004) && (read_32bitBE(0x0c,streamFile)<(get_streamfile_size(streamFile)/2))) { - loop_flag=(read_32bitBE(0x14,streamFile)!=0); - channel_count=2; - } - else if (read_32bitBE(0x04,streamFile) == 0x00020001) { /* HEVAG */ - loop_flag = vag_find_loop_points(streamFile, &loopStart, &loopEnd, 0x30); - channel_count = read_8bit(0x1e,streamFile); + case 'i': /* "VAGi" (interleaved) */ + channel_count=2; + break; + case 'V': /* pGAV (little endian / stereo) [Jak 3, Jak X] */ + if (read_32bitBE(0x20,streamFile)==0x53746572) /* "Ster" */ + channel_count=2; + else + channel_count=1; + break; + case 'p': /* "VAGp" (extended) [most common, ex Ratchet & Clank] */ + + if ((version <= 0x00000004) && (datasize < filesize / 2)) { + loop_flag=(read_32bitBE(0x14,streamFile)!=0); + channel_count=2; } - else { - loop_flag = vag_find_loop_points(streamFile, &loopStart, &loopEnd, 0x30); - channel_count = 1; - } - break; + else if (version == 0x00020001) { /* HEVAG */ + loop_flag = vag_find_loop_offsets(streamFile, 0x30, &loopStart, &loopEnd); + channel_count = read_8bit(0x1e,streamFile); + if (channel_count == 0) + channel_count = 1; /* ex. Lumines */ + } + else { + loop_flag = vag_find_loop_offsets(streamFile, 0x30, &loopStart, &loopEnd); + channel_count = 1; + } + break; default: goto fail; - } + } - /* build the VGMSTREAM */ + /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; /* fill in the vital statistics */ - vgmstream->channels = channel_count; vgmstream->coding_type = coding_PSX; + vgmstream->sample_rate = read_32bitBE(0x10,streamFile); switch(vagID) { case '1': // VAG1 - vgmstream->layout_type=layout_none; - vgmstream->sample_rate = read_32bitBE(0x10,streamFile); - vgmstream->num_samples = read_32bitBE(0x0C,streamFile)/16*28; - interleave = read_32bitLE(0x08,streamFile); + vgmstream->layout_type=layout_none; + vgmstream->num_samples = datasize / 16 * 28; + interleave = read_32bitLE(0x08,streamFile); if (interleave != 0) goto fail; - vgmstream->meta_type=meta_PS2_VAG1; - start_offset=0x40; + vgmstream->meta_type=meta_PS2_VAG1; + start_offset=0x40; /* 0x30 is extra data in VAG1 */ break; case '2': // VAG2 - vgmstream->layout_type=layout_interleave; - vgmstream->sample_rate = read_32bitBE(0x10,streamFile); - vgmstream->num_samples = read_32bitBE(0x0C,streamFile)/16*28; - interleave = 0x800; - vgmstream->meta_type=meta_PS2_VAG2; - start_offset=0x40; + vgmstream->layout_type=layout_interleave; + vgmstream->num_samples = datasize / 16 * 28; /* datasize is for 1 channel only in VAG2 */ + interleave = 0x800; + vgmstream->meta_type=meta_PS2_VAG2; + start_offset=0x40; /* 0x30 is extra data in VAG2 */ break; - case 'i': // VAGi - vgmstream->layout_type=layout_interleave; - vgmstream->sample_rate = read_32bitBE(0x10,streamFile); - vgmstream->num_samples = read_32bitBE(0x0C,streamFile)/16*28; - interleave = read_32bitLE(0x08,streamFile); - vgmstream->meta_type=meta_PS2_VAGi; - start_offset=0x800; - break; - case 'p': // VAGp - vgmstream->sample_rate = read_32bitBE(0x10,streamFile); - interleave=0x10; + case 'i': // VAGi + vgmstream->layout_type=layout_interleave; + vgmstream->num_samples = datasize / 16 * 28; + interleave = read_32bitLE(0x08,streamFile); + vgmstream->meta_type=meta_PS2_VAGi; + start_offset=0x800; + break; + case 'p': // VAGp + interleave=0x10; - if((read_32bitBE(0x04,streamFile)==0x00000004) && (read_32bitBE(0x0c,streamFile)<(get_streamfile_size(streamFile)/2))) { - vgmstream->channels=2; - vgmstream->num_samples = read_32bitBE(0x0C,streamFile); + if ((version == 0x00000004) && (datasize < filesize / 2)) { + vgmstream->channels=2; + vgmstream->num_samples = datasize; /* todo test if datasize/16*28? */ - if(loop_flag) { - vgmstream->loop_start_sample=read_32bitBE(0x14,streamFile); - vgmstream->loop_end_sample =read_32bitBE(0x18,streamFile); - loop_samples_found = 1; - } + if(loop_flag) { + vgmstream->loop_start_sample=read_32bitBE(0x14,streamFile); + vgmstream->loop_end_sample =read_32bitBE(0x18,streamFile); + loop_samples_found = 1; + } - start_offset=0x80; - vgmstream->layout_type=layout_interleave; - vgmstream->meta_type=meta_PS2_VAGs; + start_offset=0x80; + vgmstream->layout_type=layout_interleave; + vgmstream->meta_type=meta_PS2_VAGs; - // Double VAG Header @ 0x0000 & 0x1000 - if(read_32bitBE(0,streamFile)==read_32bitBE(0x1000,streamFile)) { - vgmstream->num_samples = read_32bitBE(0x0C,streamFile)/16*28; - interleave=0x1000; - start_offset=0; - } + // Double VAG Header @ 0x0000 & 0x1000 + if(read_32bitBE(0,streamFile)==read_32bitBE(0x1000,streamFile)) { + vgmstream->num_samples = datasize / 16 * 28; + interleave=0x1000; + start_offset=0; + } } - else if (read_32bitBE(0x04,streamFile) == 0x00020001) { /* HEVAG */ - vgmstream->meta_type = meta_PS2_VAGs; + else if (version == 0x40000000) { /* Guerilla VAG (little endian) */ + datasize = read_32bitLE(0x0c,streamFile); + vgmstream->sample_rate = read_32bitLE(0x10,streamFile); + vgmstream->layout_type=layout_none; + vgmstream->meta_type=meta_PS2_VAGp; + + vgmstream->num_samples = datasize / channel_count / 16 * 28; + start_offset = 0x30; + } + else if (version == 0x00020001) { /* HEVAG */ vgmstream->coding_type = coding_HEVAG_ADPCM; vgmstream->layout_type = layout_interleave; + vgmstream->meta_type = meta_PS2_VAGs; - vgmstream->num_samples = read_32bitBE(0x0C,streamFile) / channel_count / 16 * 28; + vgmstream->num_samples = datasize / channel_count / 16 * 28; + start_offset = 0x30; + } + else { /* VAGp, usually separate L/R files */ + vgmstream->layout_type=layout_none; + vgmstream->meta_type=meta_PS2_VAGp; + + vgmstream->num_samples = datasize / channel_count / 16 * 28; start_offset=0x30; - } - else { - vgmstream->layout_type=layout_none; - vgmstream->num_samples = read_32bitBE(0x0C,streamFile)/16*28; - vgmstream->meta_type=meta_PS2_VAGp; - start_offset=0x30; - } - break; + } + break; case 'V': // pGAV vgmstream->layout_type=layout_interleave; - interleave=0x2000; + interleave=0x2000; /* Jak 3 interleave, includes header */ - // Jak X hack ... - if(read_32bitLE(0x1000,streamFile)==0x56414770) - interleave=0x1000; + if(read_32bitLE(0x1000,streamFile)==0x56414770) /* "pGAV" */ + interleave=0x1000; /* Jak X interleave, includes header */ - vgmstream->sample_rate = read_32bitLE(0x10,streamFile); - vgmstream->num_samples = read_32bitLE(0x0C,streamFile)/16*14; - vgmstream->meta_type=meta_PS2_pGAV; - start_offset=0; - break; + vgmstream->sample_rate = read_32bitLE(0x10,streamFile); + vgmstream->num_samples = read_32bitLE(0x0C,streamFile)/16*14; + vgmstream->meta_type=meta_PS2_pGAV; + start_offset=0; + break; default: goto fail; - } + } - vgmstream->interleave_block_size=interleave; - - /* Don't add the header size to loop calc points */ - if(loop_flag && !loop_samples_found) { - loopStart-=start_offset; - loopEnd-=start_offset; + vgmstream->interleave_block_size=interleave; + + /* Don't add the header size to loop calc points */ + if(loop_flag && !loop_samples_found) { + loopStart-=start_offset; + loopEnd-=start_offset; vgmstream->loop_start_sample = (int32_t)((loopStart/(interleave*channel_count))*interleave)/16*28; vgmstream->loop_start_sample += (int32_t)(loopStart%(interleave*channel_count))/16*28; vgmstream->loop_end_sample = (int32_t)((loopEnd/(interleave*channel_count))*interleave)/16*28; vgmstream->loop_end_sample += (int32_t)(loopEnd%(interleave*channel_count))/16*28; - } + } /* open the file for reading by each channel */ @@ -203,15 +222,16 @@ fail: /** - * Finds loop points in VAG data using flag markers and updates loop_start and loop_end with the offsets. + * Finds loop points in VAG data using flag markers and updates loop_start and loop_end with the global offsets. * * returns 0 if not found */ -static int vag_find_loop_points(STREAMFILE *streamFile, off_t * loop_start, off_t * loop_end, off_t offset) { +static int vag_find_loop_offsets(STREAMFILE *streamFile, off_t start_offset, off_t * loop_start, off_t * loop_end) { off_t loopStart = 0; off_t loopEnd = 0; - /* used for loop points ... */ + /* used for loop points (todo: variations: 0x0c0700..00, 0x070077..77 ) */ + /* 'used to prevent unnecessary SPU interrupts' (optional if no IRQ or no looping) */ uint8_t eofVAG[16]={0x00,0x07,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77}; uint8_t eofVAG2[16]={0x00,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; uint8_t readbuf[16]; @@ -221,7 +241,7 @@ static int vag_find_loop_points(STREAMFILE *streamFile, off_t * loop_start, off_ size_t fileLength = get_streamfile_size(streamFile); - off_t readOffset = offset - 0x10; + off_t readOffset = start_offset - 0x10; do { readOffset+=0x10; @@ -246,8 +266,8 @@ static int vag_find_loop_points(STREAMFILE *streamFile, off_t * loop_start, off_ // if so we don't loop, if not present, we loop from end to start ... int read = read_streamfile(readbuf,readOffset+0x10,0x10,streamFile); if(read > 0 && readbuf[0]!=0x00 && readbuf[0]!=0x0c) { /* is there valid data after flag 0x1? */ - if(memcmp(readbuf,eofVAG,0x10) && (memcmp(readbuf,eofVAG2,0x10))) { /* probably could just check flag 0x7 */ - loopStart = offset + 0x10; + if(memcmp(readbuf,eofVAG,0x10) && (memcmp(readbuf,eofVAG2,0x10))) { /* probably could just check flag 0x7*/ + loopStart = start_offset + 0x10; /* todo proper start */ loopEnd = readOffset; break; } @@ -257,7 +277,7 @@ static int vag_find_loop_points(STREAMFILE *streamFile, off_t * loop_start, off_ } while (streamFile->get_offset(streamFile)<(off_t)fileLength); - if (loopEnd) { + if (loopStart && loopEnd) { *loop_start = loopStart; *loop_end = loopEnd; return 1; From 767444f58c16ae0e7e8a58e068de11b7e871ae82 Mon Sep 17 00:00:00 2001 From: bnnm Date: Mon, 26 Dec 2016 13:33:03 +0100 Subject: [PATCH 09/15] Removed old, unused STREAMFILE test --- test/filetest.c | 38 -------------------------------------- 1 file changed, 38 deletions(-) delete mode 100644 test/filetest.c diff --git a/test/filetest.c b/test/filetest.c deleted file mode 100644 index a55ec60b..00000000 --- a/test/filetest.c +++ /dev/null @@ -1,38 +0,0 @@ -#include -#include "streamfile.h" - -char buf[0x1002]; - -int main(void) { - STREAMFILE * infile; - FILE * outfile; - size_t filesize,i; - - infile = open_streamfile("bob.bin"); - if (!infile) { - printf("failed to open\n"); - return 1; - } - - outfile = fopen("fred.bin","wb"); - - filesize = get_streamfile_size(infile); - - for (i=0;i Date: Mon, 26 Dec 2016 13:46:13 +0100 Subject: [PATCH 10/15] Added a simple regression testing script Meant to be launched in a dir with stream files; compares the output of two test.exe versions and prints out if there are differences. Can be configured with external parameters. --- test/vrts.bat | 178 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 178 insertions(+) create mode 100644 test/vrts.bat diff --git a/test/vrts.bat b/test/vrts.bat new file mode 100644 index 00000000..d8f2fb75 --- /dev/null +++ b/test/vrts.bat @@ -0,0 +1,178 @@ +@echo off +chcp 65001 +REM #------------------------------------------------------------------------- +REM # VGMSTREAM REGRESSION TESTING SCRIPT +REM # +REM # Searches for files in a directory (or optionally subdirs) and compares +REM # the output of two test.exe versions, both wav and stdout, for regression +REM # testing. This creates and deletes temp files, trying to process all +REM # extensions found unless specified (except a few). +REM # +REM # Options: see below. +REM #------------------------------------------------------------------------- +REM #TODO: escape & ! % in file/folder names + +setlocal enableDelayedExpansion + + +REM #options +REM # -vo -vn : path to old/new exe +set OP_CMD_OLD=test_old.exe +set OP_CMD_NEW=test.exe +REM # -f : search wildcard (ex. -f "*.adx") +set OP_SEARCH="*.*" +REM # -r: recursive subfolders +set OP_RECURSIVE= +REM # -nd: don't delete compared files +set OP_NODELETE= +REM # -nc: don't report correct files +set OP_NOCORRECT= + + +REM # parse options +:set_options +if "%~1"=="" goto end_options +if "%~1"=="-vo" set OP_CMD_OLD=%2 +if "%~1"=="-vn" set OP_CMD_NEW=%2 +if "%~1"=="-f" set OP_SEARCH=%2 +if "%~1"=="-r" set OP_RECURSIVE=/s +if "%~1"=="-nd" set OP_NODELETE=true +if "%~1"=="-nc" set OP_NOCORRECT=true +shift +goto set_options +:end_options + +REM # output color defs +set C_W=0e +set C_E=0c +set C_O=0f + + +REM # check exe +set CMD_CHECK=where "%OP_CMD_OLD%" "%OP_CMD_NEW%" +%CMD_CHECK% > nul +if %ERRORLEVEL% NEQ 0 ( + echo Old/new exe not found + goto error +) +if %OP_SEARCH%=="" ( + echo Search wildcard not specified + goto error +) + +REM # process start +echo VRTS: start @%time% + +REM # search for files +set CMD_DIR=dir /a:-d /b %OP_RECURSIVE% %OP_SEARCH% +set CMD_FIND=findstr /i /v "\.exe$ \.dll$ \.zip$ \.7z$ \.rar$ \.bat$ \.sh$ \.txt$ \.lnk$ \.wav$" + +REM # process files +for /f "delims=" %%x in ('%CMD_DIR% ^| %CMD_FIND%') do ( + set CMD_FILE=%%x + call :process_file "!CMD_FILE!" +) + +REM # process end (ok) +goto done + + +REM # test a single file +:process_file outer + REM # ignore files starting with dot (no filename) + set CMD_SHORTNAME=%~n1 + if "%CMD_SHORTNAME%" == "" goto continue + + REM # get file + set CMD_FILE=%1 + set CMD_FILE=%CMD_FILE:"=% + REM echo VTRS: file %CMD_FILE% + + REM # old/new temp output + set WAV_OLD=%CMD_FILE%.old.wav + set TXT_OLD=%CMD_FILE%.old.txt + set CMD_VGM_OLD="%OP_CMD_OLD%" -o "%WAV_OLD%" "%CMD_FILE%" + %CMD_VGM_OLD% 1> "%TXT_OLD%" 2>&1 & REM || goto error + + set WAV_NEW=%CMD_FILE%.new.wav + set TXT_NEW=%CMD_FILE%.new.txt + set CMD_VGM_NEW="%OP_CMD_NEW%" -o "%WAV_NEW%" "%CMD_FILE%" + %CMD_VGM_NEW% 1> "%TXT_NEW%" 2>&1 & REM || goto error + + REM # ignore if no files are created (unsupported formats) + if not exist "%WAV_NEW%" ( + if not exist "%WAV_OLD%" ( + REM echo VRTS: nothing created for file %CMD_FILE% + if exist "%TXT_NEW%" del /a:a "%TXT_NEW%" + if exist "%TXT_OLD%" del /a:a "%TXT_OLD%" + goto continue + ) + ) + + REM # compare files (doesn't use /b for speedup, somehow) + set CMP_WAV=fc /a /lb1 "%WAV_OLD%" "%WAV_NEW%" + set CMP_TXT=fc /a /lb1 "%TXT_OLD%" "%TXT_NEW%" + + %CMP_WAV% 1> nul 2>&1 + set CMP_WAV_ERROR=0 + if %ERRORLEVEL% NEQ 0 set CMP_WAV_ERROR=1 + + %CMP_TXT% 1> nul 2>&1 + set CMP_TXT_ERROR=0 + if %ERRORLEVEL% NEQ 0 set CMP_TXT_ERROR=1 + + REM # print output + if %CMP_WAV_ERROR% EQU 1 ( + if %CMP_TXT_ERROR% EQU 1 ( + call :echo_color %C_E% "%CMD_FILE%" "wav and txt diffs" + ) else ( + call :echo_color %C_E% "%CMD_FILE%" "wav diffs" + ) + ) else ( + if %CMP_TXT_ERROR% EQU 1 ( + call :echo_color %C_W% "%CMD_FILE%" "txt diffs" + ) else ( + if "%OP_NOCORRECT%" == "" ( + call :echo_color %C_O% "%CMD_FILE%" "no diffs" + ) + ) + ) + + REM # delete temp files + if "%OP_NODELETE%" == "" ( + if exist "%WAV_OLD%" del /a:a "%WAV_OLD%" + if exist "%TXT_OLD%" del /a:a "%TXT_OLD%" + if exist "%WAV_NEW%" del /a:a "%WAV_NEW%" + if exist "%TXT_NEW%" del /a:a "%TXT_NEW%" + ) + +:continue +exit /B +REM :process_file end, continue from last call + + +REM # hack to get colored output in Windows CMD using findstr + temp file +:echo_color +set TEMP_FILE=%2-result +set TEMP_FILE=%TEMP_FILE:"=% +set TEMP_TEXT=%3 +set TEMP_TEXT=%TEMP_TEXT:"=% +echo %TEMP_TEXT% > "%TEMP_FILE%" +REM # show colored filename + any text in temp file +findstr /v /a:%1 /r "^$" "%TEMP_FILE%" nul +del "%TEMP_FILE%" +exit /B +REM :echo_color end, continue from last call + + +:done +echo VRTS: done @%time% +goto exit + + +:error +echo VRTS: error @%time% +goto exit + + +:exit From 3807ebebb85e1e070ff65f70b21bcf61c1a4889c Mon Sep 17 00:00:00 2001 From: bnnm Date: Mon, 26 Dec 2016 13:54:49 +0100 Subject: [PATCH 11/15] Include ext_libs in zip file --- Makefile | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 84ab3725..92cded39 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,8 @@ -.PHONY: buildrelease mingw_test mingw_winamp sourceball mingwbin +.PHONY: buildfullrelease buildrelease mingw_test mingw_winamp sourceball mingwbin -buildrelease: clean sourceball mingwbin +buildfullrelease: clean sourceball mingwbin + +buildrelease: clean mingwbin sourceball: rm -rf vgmstream-`./version.sh` @@ -12,7 +14,7 @@ sourceball: rm -rf vgmstream-`./version.sh` mingwbin: mingw_test mingw_winamp - zip -j "vgmstream-`./version.sh`-test.zip" readme.txt COPYING test/test.exe winamp/in_vgmstream.dll + zip -FS -j "vgmstream-`./version.sh`-test.zip" COPYING readme.txt test/test.exe winamp/in_vgmstream.dll ext_libs/*.dll mingw_test: $(MAKE) -C test -f Makefile.mingw test.exe From 709d6aae604c5af0d377e9014bf3cb19b9a498a9 Mon Sep 17 00:00:00 2001 From: bnnm Date: Mon, 26 Dec 2016 13:57:15 +0100 Subject: [PATCH 12/15] Enable FFmpeg by default and use internal FFmpeg DLLs --- ext_libs/Makefile.mingw | 11 ++++++++++- test/Makefile.mingw | 23 ++++++++++++++++------- winamp/Makefile | 21 +++++++++++++++------ 3 files changed, 41 insertions(+), 14 deletions(-) diff --git a/ext_libs/Makefile.mingw b/ext_libs/Makefile.mingw index 231a066b..ac03eadd 100644 --- a/ext_libs/Makefile.mingw +++ b/ext_libs/Makefile.mingw @@ -17,5 +17,14 @@ libg719_decode.a: libg719_decode.def libat3plusdecoder.a: at3plusdecoder.def $(DLLTOOL) -d at3plusdecoder.def -l libat3plusdecoder.a +libavcodec.a: avcodec-vgmstream-57.dll avcodec-vgmstream-57.def + $(DLLTOOL) -D avcodec-vgmstream-57.dll -d avcodec-vgmstream-57.def -l libavcodec.a + +libavformat.a: avformat-vgmstream-57.dll avformat-vgmstream-57.def + $(DLLTOOL) -D avformat-vgmstream-57.dll -d avformat-vgmstream-57.def -l libavformat.a + +libavutil.a: avutil-vgmstream-55.dll avutil-vgmstream-55.def + $(DLLTOOL) -D avutil-vgmstream-55.dll -d avutil-vgmstream-55.def -l libavutil.a + clean: - rm -f libvorbis.a libmpg123-0.a libg7221_decode.a libg719_decode.a libat3plusdecoder.a + rm -f libvorbis.a libmpg123-0.a libg7221_decode.a libg719_decode.a libat3plusdecoder.a libavcodec.a libavformat.a libavutil.a diff --git a/test/Makefile.mingw b/test/Makefile.mingw index dbcebc5d..94bde73d 100644 --- a/test/Makefile.mingw +++ b/test/Makefile.mingw @@ -1,11 +1,11 @@ # optional parts -VGM_ENABLE_FFMPEG=0 +VGM_ENABLE_FFMPEG=1 ifeq ($(VGM_ENABLE_FFMPEG),1) -FFMPEG_CC=-DVGM_USE_FFMPEG -DVGM_USE_FFMPEG_ACCURATE_LOOPING -I../../vgmstream-ffmpeg/include -FFMPEG_LD=-L../../vgmstream-ffmpeg/lib -lavcodec -lavformat -lavutil +FFMPEG_CC=-DVGM_USE_FFMPEG -DVGM_USE_FFMPEG_ACCURATE_LOOPING +FFMPEG_LD=-lavcodec -lavformat -lavutil endif -VGM_ENABLE_MAIATRAC3PLUS=1 +VGM_ENABLE_MAIATRAC3PLUS=0 ifeq ($(VGM_ENABLE_MAIATRAC3PLUS),1) MAT3P_CC=-DVGM_USE_MAIATRAC3PLUS MAT3P_LD=-lat3plusdecoder @@ -31,9 +31,9 @@ export CC=i686-w64-mingw32-gcc export AR=i686-w64-mingw32-ar export STRIP=i686-w64-mingw32-strip -.PHONY: libvgmstream.a libvorbis.a libmpg123-0.a libg7221_decode.a libg719_decode.a libat3plusdecoder.a +.PHONY: libvgmstream.a libvorbis.a libmpg123-0.a libg7221_decode.a libg719_decode.a libat3plusdecoder.a libavcodec.a libavformat.a libavutil.a -test.exe: libvgmstream.a libvorbis.a libmpg123-0.a libg7221_decode.a libg719_decode.a libat3plusdecoder.a +test.exe: libvgmstream.a libvorbis.a libmpg123-0.a libg7221_decode.a libg719_decode.a libat3plusdecoder.a libavcodec.a libavformat.a libavutil.a $(CC) $(CFLAGS) "-DVERSION=\"`../version.sh`\"" test.c $(LDFLAGS) -o test.exe $(STRIP) test.exe @@ -54,6 +54,15 @@ libg719_decode.a: libat3plusdecoder.a: $(MAKE) -C ../ext_libs -f Makefile.mingw $@ - + +libavcodec.a: + $(MAKE) -C ../ext_libs -f Makefile.mingw $@ + +libavformat.a: + $(MAKE) -C ../ext_libs -f Makefile.mingw $@ + +libavutil.a: + $(MAKE) -C ../ext_libs -f Makefile.mingw $@ + clean: rm -f test.exe diff --git a/winamp/Makefile b/winamp/Makefile index ef4c00e1..76c67195 100644 --- a/winamp/Makefile +++ b/winamp/Makefile @@ -1,11 +1,11 @@ # optional parts -VGM_ENABLE_FFMPEG=0 +VGM_ENABLE_FFMPEG=1 ifeq ($(VGM_ENABLE_FFMPEG),1) -FFMPEG_CC=-DVGM_USE_FFMPEG -DVGM_USE_FFMPEG_ACCURATE_LOOPING -I../../vgmstream-ffmpeg/include -FFMPEG_LD=-L../../vgmstream-ffmpeg/lib -lavcodec -lavformat -lavutil +FFMPEG_CC=-DVGM_USE_FFMPEG -DVGM_USE_FFMPEG_ACCURATE_LOOPING +FFMPEG_LD=-lavcodec -lavformat -lavutil endif -VGM_ENABLE_MAIATRAC3PLUS=1 +VGM_ENABLE_MAIATRAC3PLUS=0 ifeq ($(VGM_ENABLE_MAIATRAC3PLUS),1) MAT3P_CC=-DVGM_USE_MAIATRAC3PLUS MAT3P_LD=-lat3plusdecoder @@ -33,9 +33,9 @@ export WINDRES=i586-mingw32msvc-windres #export STRIP=i686-w64-mingw32-strip #export WINDRES=i686-w64-mingw32-windres -.PHONY: libvgmstream.a libvorbis.a libmpg123-0.a libg7221_decode.a libg719_decode.a at3plusdecoder.a +.PHONY: libvgmstream.a libvorbis.a libmpg123-0.a libg7221_decode.a libg719_decode.a libat3plusdecoder.a libavcodec.a libavformat.a libavutil.a -in_vgmstream.dll: libvgmstream.a libvorbis.a libmpg123-0.a libg7221_decode.a libg719_decode.a libat3plusdecoder.a in_vgmstream.c resource.o +in_vgmstream.dll: libvgmstream.a libvorbis.a libmpg123-0.a libg7221_decode.a libg719_decode.a libat3plusdecoder.a libavcodec.a libavformat.a libavutil.a resource.o $(CC) -shared -static-libgcc $(CFLAGS) "-DVERSION=\"`../version.sh`\"" in_vgmstream.c resource.o $(LDFLAGS) -o in_vgmstream.dll $(STRIP) in_vgmstream.dll @@ -60,5 +60,14 @@ libg719_decode.a: libat3plusdecoder.a: $(MAKE) -C ../ext_libs -f Makefile.mingw $@ +libavcodec.a: + $(MAKE) -C ../ext_libs -f Makefile.mingw $@ + +libavformat.a: + $(MAKE) -C ../ext_libs -f Makefile.mingw $@ + +libavutil.a: + $(MAKE) -C ../ext_libs -f Makefile.mingw $@ + clean: rm -f in_vgmstream.dll resource.o From aee2aa78f3c69aae7703804b8c1e21c680fc5c80 Mon Sep 17 00:00:00 2001 From: bnnm Date: Mon, 26 Dec 2016 14:04:44 +0100 Subject: [PATCH 13/15] Removed .vgms (not .vgmstream), as I feel one hack-extension is enough --- fb2k/in_vgmstream.cpp | 2 -- winamp/in_vgmstream.c | 1 - xmp-vgmstream/DllMain.c | 2 +- 3 files changed, 1 insertion(+), 4 deletions(-) diff --git a/fb2k/in_vgmstream.cpp b/fb2k/in_vgmstream.cpp index 28883081..0cfb67fd 100644 --- a/fb2k/in_vgmstream.cpp +++ b/fb2k/in_vgmstream.cpp @@ -626,7 +626,6 @@ bool input_vgmstream::g_is_our_path(const char * p_path,const char * p_extension if(!stricmp_utf8(p_extension,"zwdsp")) return 1; if(!stricmp_utf8(p_extension,"vgmstream")) return 1; - if(!stricmp_utf8(p_extension,"vgms")) return 1; return 0; } @@ -965,4 +964,3 @@ DECLARE_MULTIPLE_FILE_TYPE("ZSD Audio File (*.ZSD)", zsd); DECLARE_MULTIPLE_FILE_TYPE("ZWDSP Audio File (*.ZWDSP)", zwdsp); DECLARE_MULTIPLE_FILE_TYPE("vgmstream Audio File (*.VGMSTREAM)", vgmstream); -DECLARE_MULTIPLE_FILE_TYPE("vgmstream Audio File (*.VGMS)", vgms); diff --git a/winamp/in_vgmstream.c b/winamp/in_vgmstream.c index e0c59015..79841e4e 100644 --- a/winamp/in_vgmstream.c +++ b/winamp/in_vgmstream.c @@ -371,7 +371,6 @@ char * extension_list[] = { "zwdsp\0ZWDSP Audio File (*.ZWDSP)\0", "vgmstream\0vgmstream Audio File (*.VGMSTREAM)\0", - "vgms\00vgmstream Audio File (*.VGMS)\0", }; void about(HWND hwndParent) { diff --git a/xmp-vgmstream/DllMain.c b/xmp-vgmstream/DllMain.c index c8a19dee..1dfd4ed2 100644 --- a/xmp-vgmstream/DllMain.c +++ b/xmp-vgmstream/DllMain.c @@ -332,7 +332,7 @@ void __stdcall GetAdditionalFields(char* blerp) { XMPIN vgmstream_intf = { XMPIN_FLAG_CANSTREAM, "vgmstream for XMPlay", - "vgmstream files\0""2dx9/aaap/aax/acm/adp/adpcm/ads/adx/afc/agsc/ahx/aifc/aiff/aix/amts/as4/asd/asf/asr/ass/ast/aud/aus/baf/baka/bar/bcstm/bcwav/bfstm/bfwav/bfwavnsmbu/bg00/bgw/bh2pcm/bmdx/bns/bnsf/bo2/brstm/caf/capdsp/ccc/cfn/cnk/dcs/dcsw/ddsp/de2/dmsg/dsp/dvi/dxh/eam/emff/enth/fag/filp/fsb/fwav/gca/gcm/gcsw/gcw/genh/gms/gsp/hca/hgc1/his/hps/hwas/idsp/idvi/ikm/ild/int/isd/ish/ivaud/ivb/joe/kces/kcey/khv/kraw/leg/logg/lps/lsf/lwav/matx/mcg/mi4/mib/mic/mihb/mpdsp/msa/mss/msvp/mus/musc/musx/mwv/myspd/ndp/npsf/nus3bank/nwa/omu/otm/p3d/pcm/pdt/pnb/pos/psh/psw/raw/rkv/rnd/rrds/rsd/rsf/rstm/rwar/rwav/rws/rwsd/rwx/rxw/s14/sab/sad/sap/sc/scd/sd9/sdt/seg/sfl/sfs/sl3/sli/smp/smpl/snd/sng/sns/spd/sps/spsd/spt/spw/ss2/ss7/ssm/sss/ster/sth/stm/stma/str/strm/sts/stx/svag/svs/swav/swd/tec/thp/tk5/tydsp/um3/vag/vas/vgs/vig/vjdsp/voi/vpk/vs/vsf/waa/wac/wad/wam/was/wavm/wb/wii/wp2/wsd/wsi/wvs/xa/xa2/xa30/xma/xma2/xmu/xss/xvas/xwav/xwb/ydsp/ymf/zsd/zwdsp/vgmstream/vgms", + "vgmstream files\0""2dx9/aaap/aax/acm/adp/adpcm/ads/adx/afc/agsc/ahx/aifc/aiff/aix/amts/as4/asd/asf/asr/ass/ast/aud/aus/baf/baka/bar/bcstm/bcwav/bfstm/bfwav/bfwavnsmbu/bg00/bgw/bh2pcm/bmdx/bns/bnsf/bo2/brstm/caf/capdsp/ccc/cfn/cnk/dcs/dcsw/ddsp/de2/dmsg/dsp/dvi/dxh/eam/emff/enth/fag/filp/fsb/fwav/gca/gcm/gcsw/gcw/genh/gms/gsp/hca/hgc1/his/hps/hwas/idsp/idvi/ikm/ild/int/isd/ish/ivaud/ivb/joe/kces/kcey/khv/kraw/leg/logg/lps/lsf/lwav/matx/mcg/mi4/mib/mic/mihb/mpdsp/mca/msa/mss/msvp/mus/musc/musx/mwv/myspd/ndp/npsf/nus3bank/nwa/omu/otm/p3d/pcm/pdt/pnb/pos/psh/psw/raw/rkv/rnd/rrds/rsd/rsf/rstm/rwar/rwav/rws/rwsd/rwx/rxw/s14/sab/sad/sap/sc/scd/sd9/sdt/seg/sfl/sfs/sl3/sli/smp/smpl/snd/sng/sns/spd/sps/spsd/spt/spw/ss2/ss7/ssm/sss/ster/sth/stm/stma/str/strm/sts/stx/svag/svs/swav/swd/tec/thp/tk5/tydsp/um3/vag/vas/vgs/vig/vjdsp/voi/vpk/vs/vsf/waa/wac/wad/wam/was/wavm/wb/wii/wp2/wsd/wsi/wvs/xa/xa2/xa30/xma/xma2/xmu/xss/xvas/xwav/xwb/ydsp/ymf/zsd/zwdsp/vgmstream", XMPAbout, NULL, XMP_CheckFile, From 8daafeea90b4df37d214c36926942b579bfcc36e Mon Sep 17 00:00:00 2001 From: bnnm Date: Mon, 26 Dec 2016 16:09:16 +0100 Subject: [PATCH 14/15] Adjusted loop detection for some dual stereo files (Ecco the Dolphin) It was detecting one channel as looping and other as non-looping. --- src/meta/ps2_vag.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/meta/ps2_vag.c b/src/meta/ps2_vag.c index 7b9d3820..7ca91519 100644 --- a/src/meta/ps2_vag.c +++ b/src/meta/ps2_vag.c @@ -260,13 +260,20 @@ static int vag_find_loop_offsets(STREAMFILE *streamFile, off_t start_offset, off break; } - // Loop from end to beginning ... + /* hack for some games that don't have loop points but play the same track on repeat + * (sometimes this will loop non-looping tracks incorrectly) + * if there is a "partial" 0x07 end flag pretend it wants to loop */ if (flag == 0x01) { - // Check if we have the eof tag after the loop point ... + // Check if we have a full eof tag after the loop point ... // if so we don't loop, if not present, we loop from end to start ... int read = read_streamfile(readbuf,readOffset+0x10,0x10,streamFile); - if(read > 0 && readbuf[0]!=0x00 && readbuf[0]!=0x0c) { /* is there valid data after flag 0x1? */ - if(memcmp(readbuf,eofVAG,0x10) && (memcmp(readbuf,eofVAG2,0x10))) { /* probably could just check flag 0x7*/ + /* is there valid data after flag 0x1? */ + if (read > 0 + && readbuf[0] != 0x00 + && readbuf[0] != 0x0c + && readbuf[0] != 0x3c /* Ecco the Dolphin, Ratchet & Clank 2 */ + ) { + if (memcmp(readbuf,eofVAG,0x10) && (memcmp(readbuf,eofVAG2,0x10))) { /* full end flags */ loopStart = start_offset + 0x10; /* todo proper start */ loopEnd = readOffset; break; From 1fea06610c5fb8944a32e2b200d220dd3bccb965 Mon Sep 17 00:00:00 2001 From: bnnm Date: Mon, 26 Dec 2016 19:48:45 +0100 Subject: [PATCH 15/15] Updated some types --- readme.txt | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/readme.txt b/readme.txt index 4c0f5ce2..372271bd 100644 --- a/readme.txt +++ b/readme.txt @@ -117,7 +117,7 @@ PS2/PSX ADPCM: - .xa2 - .xa30 -GC/Wii DSP ADPCM: +GC/Wii/3DS DSP ADPCM: - .aaap - .agsc - .amts @@ -140,6 +140,7 @@ GC/Wii DSP ADPCM: - .idsp - .ish+.isd - .lps +- .mca - .mpdsp - .mss - .mus (not quite right) @@ -260,6 +261,7 @@ etc: - .kcey (EACS IMA ADPCM) - .lsf (LSF ADPCM) - .mwv (Level-5 0x555 ADPCM) +- .mtaf (Konami ADPCM) - .ogg, .logg (Ogg Vorbis) - .p3d (Radical ADPCM) - .rsf (CCITT G.721 ADPCM) @@ -275,17 +277,18 @@ etc: - .stx (GC AFC ADPCM) - .um3 (Ogg Vorbis) - .xa (CD-ROM XA audio) +- .xma (MS WMA Pro) loop assists: - .mus (playlist for .acm) -- .pos (loop info for .wav) +- .pos (loop info for .wav: 32 bit LE loop start sample + loop end sample) - .sli (loop info for .ogg) - .sfl (loop info for .ogg) other: - .adxkey (decryption key for .adx, in start/mult/add format) - .hcakey (decryption key for .hca, in HCA Decoder format) -- .vgmstream/.vgms + .pos (to force FFmpeg formats + loop assist) +- .vgmstream + .pos (FFmpeg formats + loop assist) Enjoy! -hcs