diff --git a/doc/TXTP.md b/doc/TXTP.md index eab6171f..6c67beb3 100644 --- a/doc/TXTP.md +++ b/doc/TXTP.md @@ -87,8 +87,8 @@ mode = layers ``` -### Channel mapping -TXTP can swap channels for custom channel mappings. Note that channel masking applies after mappings. Format is: +### Channel swapping/mapping +TXTP can swap channels for custom channel mappings. It does "swapping" rather than simpler "mapping" since vgmstream can't read a format's mappings or guess which channel is which. Format is: ``` #ch1 = first file1.ext#m2-3 # "FL BL FR BR" to "FL FR BL BR" @@ -96,6 +96,7 @@ file1.ext#m2-3 # "FL BL FR BR" to "FL FR BL BR" #do note the order specified affects swapping file2.ext#m2-3,4-5,4-6 # ogg "FL CN FR BL BR SB" to wav "FL FR CN SB BL BR" ``` +Note that channel masking applies after mappings. ### Custom play settings diff --git a/src/coding/ea_xas_decoder.c b/src/coding/ea_xas_decoder.c index aa982249..148bfff8 100644 --- a/src/coding/ea_xas_decoder.c +++ b/src/coding/ea_xas_decoder.c @@ -9,8 +9,9 @@ static const int EA_XA_TABLE[20] = { 0, -1, -3, -4 }; -/* EA-XAS, evolution of EA-XA and cousin of MTA2. Layout: blocks of 0x4c per channel (128 samples), - * divided into 4 headers + 4 vertical groups of 15 bytes (for parallelism?). +/* EA-XAS, evolution of EA-XA and cousin of MTA2. From FFmpeg (general info) + MTA2 (layout) + EA-XA (decoding) + * + * Layout: blocks of 0x4c per channel (128 samples), divided into 4 headers + 4 vertical groups of 15 bytes (for parallelism?). * To simplify, always decodes the block and discards unneeded samples, so doesn't use external hist. */ void decode_ea_xas(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { int group, row, i; diff --git a/src/coding/hca_decoder.c b/src/coding/hca_decoder.c index c8c0ba9c..88a3e647 100644 --- a/src/coding/hca_decoder.c +++ b/src/coding/hca_decoder.c @@ -151,15 +151,22 @@ void free_hca(hca_codec_data * data) { } -#define HCA_KEY_MAX_BLANK_FRAMES 15 /* ignored up to N blank frames (not uncommon to have ~10, if more something is off) */ -#define HCA_KEY_MAX_TEST_FRAMES 10 /* 5~15 should be enough, but mostly silent or badly mastered files may need more */ -#define HCA_KEY_MAX_ACCEPTABLE_SCORE 150 /* more is unlikely to work correctly, 10~30 isn't uncommon */ +/* arbitrary scale to simplify score comparisons */ +#define HCA_KEY_SCORE_SCALE 10 +/* ignores beginning frames (~10 is not uncommon, Dragalia Lost vocal layers have lots) */ +#define HCA_KEY_MAX_SKIP_BLANKS 1200 +/* 5~15 should be enough, but almost silent or badly mastered files may need tweaks */ +#define HCA_KEY_MIN_TEST_FRAMES 5 +#define HCA_KEY_MAX_TEST_FRAMES 10 +/* score of 10~30 isn't uncommon in a single frame, too many frames over that is unlikely */ +#define HCA_KEY_MAX_FRAME_SCORE 150 +#define HCA_KEY_MAX_TOTAL_SCORE (HCA_KEY_MAX_TEST_FRAMES * 50*HCA_KEY_SCORE_SCALE) /* Test a number of frames if key decrypts correctly. * Returns score: <0: error/wrong, 0: unknown/silent file, >0: good (the closest to 1 the better). */ int test_hca_key(hca_codec_data * data, unsigned long long keycode) { - size_t test_frame = 0, current_frame = 0, blank_frames = 0; - int total_score = 0; + size_t test_frames = 0, current_frame = 0, blank_frames = 0; + int total_score = 0, found_regular_frame = 0; const unsigned int blockSize = data->info.blockSize; /* Due to the potentially large number of keys this must be tuned for speed. @@ -168,43 +175,56 @@ int test_hca_key(hca_codec_data * data, unsigned long long keycode) { clHCA_SetKey(data->handle, keycode); - while (test_frame < HCA_KEY_MAX_TEST_FRAMES && current_frame < data->info.blockCount) { + /* Test up to N non-blank frames or until total frames. */ + /* A final score of 0 (=silent) is only possible for short files with all blank frames */ + + while (test_frames < HCA_KEY_MAX_TEST_FRAMES && current_frame < data->info.blockCount) { off_t offset = data->info.headerSize + current_frame * blockSize; int score; size_t bytes; - /* read frame */ + /* read and test frame */ bytes = read_streamfile(data->data_buffer, offset, blockSize, data->streamfile); if (bytes != blockSize) { total_score = -1; break; } - /* test frame */ score = clHCA_TestBlock(data->handle, (void*)(data->data_buffer), blockSize); - if (score < 0) { + if (score < 0 || score > HCA_KEY_MAX_FRAME_SCORE) { total_score = -1; break; } current_frame++; - /* skip blank block at the beginning */ - if (score == 0 && blank_frames < HCA_KEY_MAX_BLANK_FRAMES) { + /* ignore silent frames at the beginning, up to a point */ + if (score == 0 && blank_frames < HCA_KEY_MAX_SKIP_BLANKS && !found_regular_frame) { blank_frames++; continue; } - test_frame++; + found_regular_frame = 1; + test_frames++; + + /* scale values to make scores of perfect frames more detectable */ + switch(score) { + case 1: score = 1; break; + case 0: score = 3*HCA_KEY_SCORE_SCALE; break; /* blanks after non-blacks aren't very trustable */ + default: score = score*HCA_KEY_SCORE_SCALE; + } + total_score += score; - /* too far, don't bother checking more frames */ - if (total_score > HCA_KEY_MAX_ACCEPTABLE_SCORE) + + /* don't bother checking more frames, other keys will get better scores */ + if (total_score > HCA_KEY_MAX_TOTAL_SCORE) break; } + //;VGM_LOG("HCA KEY: blanks=%i, tests=%i, score=%i\n", blank_frames, test_frames, total_score); - /* signal best possible score */ - if (total_score > 0 && total_score <= HCA_KEY_MAX_TEST_FRAMES) { + /* signal best possible score (many perfect frames and few blank frames) */ + if (test_frames > HCA_KEY_MIN_TEST_FRAMES && total_score > 0 && total_score <= test_frames) { total_score = 1; } diff --git a/src/coding/mta2_decoder.c b/src/coding/mta2_decoder.c index 09617da2..2ecbb327 100644 --- a/src/coding/mta2_decoder.c +++ b/src/coding/mta2_decoder.c @@ -113,20 +113,28 @@ void decode_mta2(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, frame_size = read_16bitBE(stream->offset+0x06,stream->streamfile); /* not including this header */ /* 0x08(8): null */ - /* EOF: 0-fill buffer (or, as track_channels = 0 > divs by 0) */ - if (num_track < 0) { + + VGM_ASSERT(frame_size == 0, "MTA2: empty frame at %x\n", (uint32_t)stream->offset); + /* frame_size 0 means silent/empty frame (rarely found near EOF for one track but not others) + * negative track only happens for truncated files (EOF) */ + if (frame_size == 0 || num_track < 0) { for (i = 0; i < samples_to_do; i++) outbuf[i * channelspacing] = 0; + stream->offset += 0x10; return; } - track_channels = 0; for (i = 0; i < 8; i++) { if ((channel_layout >> i) & 0x01) track_channels++; } + if (track_channels == 0) { /* bad data, avoid div by 0 */ + VGM_LOG("track_channels 0 at %x\n", (uint32_t)stream->offset); + return; + } + /* assumes tracks channels are divided evenly in all tracks (ex. not 2ch + 1ch + 1ch) */ if (channel / track_channels == num_track) break; /* channel belongs to this track */ diff --git a/src/formats.c b/src/formats.c index 38d7c442..4eb018c5 100644 --- a/src/formats.c +++ b/src/formats.c @@ -129,7 +129,7 @@ static const char* extension_list[] = { "e4x", "eam", "eda", //txth/reserved [Project Eden (PS2)] - "emff", + "emff", //fake extension for .mul (to be removed) "enm", "eno", "ens", @@ -175,6 +175,7 @@ static const char* extension_list[] = { "idx", "ikm", "ild", + "imc", "int", "isd", "isws", @@ -249,6 +250,7 @@ static const char* extension_list[] = { "msvp", "mta2", "mtaf", + "mul", "mus", "musc", "musx", @@ -681,9 +683,8 @@ static const layout_info layout_info_list[] = { {layout_blocked_ws_aud, "blocked (Westwood Studios .aud)"}, {layout_blocked_matx, "blocked (Matrix .matx)"}, {layout_blocked_dec, "blocked (DEC)"}, - {layout_blocked_vs, "blocked (vs)"}, - {layout_blocked_emff_ps2, "blocked (EMFF PS2)"}, - {layout_blocked_emff_ngc, "blocked (EMFF NGC)"}, + {layout_blocked_vs, "blocked (Melbourne House VS)"}, + {layout_blocked_mul, "blocked (MUL)"}, {layout_blocked_gsb, "blocked (GSB)"}, {layout_blocked_thp, "blocked (THP Movie Audio)"}, {layout_blocked_filp, "blocked (FILP)"}, @@ -692,7 +693,7 @@ static const layout_info layout_info_list[] = { {layout_blocked_bdsp, "blocked (BDSP)"}, {layout_blocked_ivaud, "blocked (IVAUD)"}, {layout_blocked_ps2_iab, "blocked (IAB)"}, - {layout_blocked_ps2_strlr, "blocked (The Bouncer STR)"}, + {layout_blocked_vs_str, "blocked (STR VS)"}, {layout_blocked_rws, "blocked (RWS)"}, {layout_blocked_hwas, "blocked (HWAS)"}, {layout_blocked_tra, "blocked (TRA)"}, @@ -706,7 +707,7 @@ static const layout_info layout_info_list[] = { {layout_blocked_sthd, "blocked (STHD)"}, {layout_blocked_h4m, "blocked (H4M)"}, {layout_blocked_xa_aiff, "blocked (XA AIFF)"}, - {layout_blocked_vs_ffx, "blocked (Square VS)"}, + {layout_blocked_vs_square, "blocked (Square VS)"}, }; static const meta_info meta_info_list[] = { @@ -832,7 +833,7 @@ static const meta_info meta_info_list[] = { {meta_NGC_WVS, "Metal Arms WVS Header (GameCube)"}, {meta_XBOX_MATX, "assumed Matrix file by .matx extension"}, {meta_DEC, "Falcom DEC RIFF header"}, - {meta_VS, "Men in Black VS Header"}, + {meta_VS, "Melbourne House .VS header"}, {meta_DC_STR, "Sega Stream Asset Builder header"}, {meta_DC_STR_V2, "variant of Sega Stream Asset Builder header"}, {meta_XBOX_XMU, "XMU header"}, @@ -888,8 +889,7 @@ static const meta_info meta_info_list[] = { {meta_VGS, "Guitar Hero VGS Header"}, {meta_DC_DCSW_DCS, "Evil Twin DCS file with helper"}, {meta_WII_SMP, "SMP DSP Header"}, - {meta_EMFF_PS2, "Eidos Music File Format Header"}, - {meta_EMFF_NGC, "Eidos Music File Format Header"}, + {meta_MUL, "Crystal Dynamics .MUL header"}, {meta_THP, "THP Movie File Format Header"}, {meta_STS_WII, "Shikigami no Shiro (WII) Header"}, {meta_PS2_P2BT, "Pop'n'Music 7 Header"}, @@ -908,7 +908,7 @@ static const meta_info meta_info_list[] = { {meta_ADS, "dhSS Header"}, {meta_PS2_MCG, "Gunvari MCG Header"}, {meta_ZSD, "ZSD Header"}, - {meta_RedSpark, "RedSpark Header"}, + {meta_REDSPARK, "RedSpark Header"}, {meta_IVAUD, "Rockstar .ivaud header"}, {meta_DSP_WII_WSD, ".WSD header"}, {meta_WII_NDP, "Icon Games NDP header"}, @@ -986,7 +986,7 @@ static const meta_info meta_info_list[] = { {meta_X360_TRA, "Terminal Reality .TRA raw header"}, {meta_PS2_VGS, "Princess Soft VGS header"}, {meta_PS2_IAB, "Runtime .IAB header"}, - {meta_PS2_STRLR, "The Bouncer STR header"}, + {meta_VS_STR, "Square .VS STR* header"}, {meta_LSF_N1NJ4N, ".lsf !n1nj4n header"}, {meta_VAWX, "feelplus VAWX header"}, {meta_PC_SNDS, "assumed Heavy Iron IMA by .snds extension"}, @@ -1126,7 +1126,7 @@ static const meta_info meta_info_list[] = { {meta_XWMA, "Microsoft XWMA RIFF header"}, {meta_VA3, "Konami VA3 header" }, {meta_XOPUS, "Exient XOPUS header"}, - {meta_VS_FFX, "Square VS header"}, + {meta_VS_SQUARE, "Square VS header"}, {meta_NWAV, "Chunsoft NWAV header"}, {meta_XPCM, "Circus XPCM header"}, {meta_MSF_TAMASOFT, "Tama-Soft MSF header"}, @@ -1134,6 +1134,7 @@ static const meta_info meta_info_list[] = { {meta_ZSND, "Vicarious Visions ZSND header"}, {meta_DSP_ADPCMX, "AQUASTYLE ADPY header"}, {meta_OGG_OPUS, "Ogg Opus header"}, + {meta_IMC, "iNiS .IMC header"}, }; diff --git a/src/layout/blocked.c b/src/layout/blocked.c index 2e6e4836..c6eea228 100644 --- a/src/layout/blocked.c +++ b/src/layout/blocked.c @@ -127,11 +127,8 @@ void block_update(off_t block_offset, VGMSTREAM * vgmstream) { case layout_blocked_dec: block_update_dec(block_offset,vgmstream); break; - case layout_blocked_emff_ps2: - block_update_emff_ps2(block_offset,vgmstream); - break; - case layout_blocked_emff_ngc: - block_update_emff_ngc(block_offset,vgmstream); + case layout_blocked_mul: + block_update_mul(block_offset,vgmstream); break; case layout_blocked_gsb: block_update_gsb(block_offset,vgmstream); @@ -166,8 +163,8 @@ void block_update(off_t block_offset, VGMSTREAM * vgmstream) { case layout_blocked_ps2_iab: block_update_ps2_iab(block_offset,vgmstream); break; - case layout_blocked_ps2_strlr: - block_update_ps2_strlr(block_offset,vgmstream); + case layout_blocked_vs_str: + block_update_vs_str(block_offset,vgmstream); break; case layout_blocked_rws: block_update_rws(block_offset,vgmstream); @@ -205,8 +202,8 @@ void block_update(off_t block_offset, VGMSTREAM * vgmstream) { case layout_blocked_xa_aiff: block_update_xa_aiff(block_offset,vgmstream); break; - case layout_blocked_vs_ffx: - block_update_vs_ffx(block_offset,vgmstream); + case layout_blocked_vs_square: + block_update_vs_square(block_offset,vgmstream); break; default: /* not a blocked layout */ break; diff --git a/src/layout/blocked_emff.c b/src/layout/blocked_emff.c deleted file mode 100644 index 41ad197f..00000000 --- a/src/layout/blocked_emff.c +++ /dev/null @@ -1,34 +0,0 @@ -#include "layout.h" -#include "../vgmstream.h" - -/* set up for the block at the given offset */ -void block_update_emff_ps2(off_t block_offset, VGMSTREAM * vgmstream) { - int i; - - vgmstream->current_block_offset = block_offset; - vgmstream->current_block_size = read_32bitLE( - vgmstream->current_block_offset+0x10, - vgmstream->ch[0].streamfile); - vgmstream->next_block_offset = vgmstream->current_block_offset + vgmstream->current_block_size+0x20; - vgmstream->current_block_size/=vgmstream->channels; - - for (i=0;ichannels;i++) { - vgmstream->ch[i].offset = vgmstream->current_block_offset+0x20+(vgmstream->current_block_size*i); - } -} - -void block_update_emff_ngc(off_t block_offset, VGMSTREAM * vgmstream) { - int i; - - vgmstream->current_block_offset = block_offset; - vgmstream->current_block_size = read_32bitBE( - vgmstream->current_block_offset+0x20, - vgmstream->ch[0].streamfile); - vgmstream->next_block_offset = vgmstream->current_block_offset + vgmstream->current_block_size+0x40; - vgmstream->current_block_size/=vgmstream->channels; - - for (i=0;ichannels;i++) { - vgmstream->ch[i].offset = vgmstream->current_block_offset+0x40+(vgmstream->current_block_size*i); - } -} - diff --git a/src/layout/blocked_mul.c b/src/layout/blocked_mul.c new file mode 100644 index 00000000..716e03c6 --- /dev/null +++ b/src/layout/blocked_mul.c @@ -0,0 +1,50 @@ +#include "layout.h" +#include "../vgmstream.h" + +/* process headered blocks with sub-headers */ +void block_update_mul(off_t block_offset, VGMSTREAM * vgmstream) { + STREAMFILE* streamFile = vgmstream->ch[0].streamfile; + int i, block_type; + size_t block_size, block_header, data_size, data_header; + int32_t (*read_32bit)(off_t,STREAMFILE*) = vgmstream->codec_endian ? read_32bitBE : read_32bitLE; + + block_type = read_32bit(block_offset + 0x00,streamFile); + block_size = read_32bit(block_offset + 0x04,streamFile); /* not including main header */ + + switch(vgmstream->coding_type) { + case coding_NGC_DSP: + block_header = 0x20; + data_header = 0x20; + break; + default: + block_header = 0x10; + data_header = 0x10; + break; + } + + if (block_type == 0x00 && block_size == 0) { + /* oddity in some vid+audio files? bad extraction? */ + block_header = 0x10; + data_header = 0x00; + data_size = 0; + } + if (block_type == 0x00 && block_size != 0) { + /* read audio sub-header */ + data_size = read_32bit(block_offset + block_header + 0x00,streamFile); + } + else { + /* non-audio or empty audio block */ + data_header = 0x00; + data_size = 0; + } + + vgmstream->current_block_offset = block_offset; + vgmstream->current_block_size = data_size / vgmstream->channels; + vgmstream->next_block_offset = block_offset + block_header + block_size; + + for (i = 0; i < vgmstream->channels; i++) { + vgmstream->ch[i].offset = block_offset + block_header + data_header + vgmstream->current_block_size*i; + //VGM_LOG("ch%i of=%lx\n", i, vgmstream->ch[i].offset); + } + //getchar(); +} diff --git a/src/layout/blocked_vs.c b/src/layout/blocked_vs.c index 4284642d..d53732d6 100644 --- a/src/layout/blocked_vs.c +++ b/src/layout/blocked_vs.c @@ -1,17 +1,16 @@ #include "layout.h" #include "../vgmstream.h" -/* set up for the block at the given offset */ +/* mini-blocks of size + data */ void block_update_vs(off_t block_offset, VGMSTREAM * vgmstream) { + STREAMFILE* streamFile = vgmstream->ch[0].streamfile; int i; - for (i=0;ichannels;i++) { - vgmstream->current_block_offset = block_offset; - vgmstream->current_block_size = read_32bitLE( - vgmstream->current_block_offset, - vgmstream->ch[0].streamfile); - vgmstream->next_block_offset = vgmstream->current_block_offset + vgmstream->current_block_size + 0x4; - vgmstream->ch[i].offset = vgmstream->current_block_offset + 0x4; - if(i==0) block_offset=vgmstream->next_block_offset; + for (i = 0; i < vgmstream->channels; i++) { + vgmstream->current_block_offset = block_offset; + vgmstream->current_block_size = read_32bitLE(vgmstream->current_block_offset,streamFile); + vgmstream->next_block_offset = vgmstream->current_block_offset + vgmstream->current_block_size + 0x04; + vgmstream->ch[i].offset = vgmstream->current_block_offset + 0x04; + if (i == 0) block_offset=vgmstream->next_block_offset; } } diff --git a/src/layout/blocked_vs_ffx.c b/src/layout/blocked_vs_square.c similarity index 72% rename from src/layout/blocked_vs_ffx.c rename to src/layout/blocked_vs_square.c index d7a924a1..403d6140 100644 --- a/src/layout/blocked_vs_ffx.c +++ b/src/layout/blocked_vs_square.c @@ -2,13 +2,13 @@ #include "../vgmstream.h" /* Square "VS" headered blocks */ -void block_update_vs_ffx(off_t block_offset, VGMSTREAM * vgmstream) { +void block_update_vs_square(off_t block_offset, VGMSTREAM * vgmstream) { int i; size_t block_size = 0x800; vgmstream->current_block_offset = block_offset; vgmstream->current_block_size = block_size - 0x20; - vgmstream->next_block_offset = block_offset + block_size; + vgmstream->next_block_offset = block_offset + block_size*vgmstream->channels; /* 0x08: number of remaning blocks, 0x0c: blocks left */ for (i = 0; i < vgmstream->channels; i++) { diff --git a/src/layout/blocked_ps2_strlr.c b/src/layout/blocked_vs_str.c similarity index 89% rename from src/layout/blocked_ps2_strlr.c rename to src/layout/blocked_vs_str.c index 17f982bb..023f38bf 100644 --- a/src/layout/blocked_ps2_strlr.c +++ b/src/layout/blocked_vs_str.c @@ -2,7 +2,7 @@ #include "../vgmstream.h" /* The Bouncer STRx blocks, one block per channel when stereo */ -void block_update_ps2_strlr(off_t block_offset, VGMSTREAM * vgmstream) { +void block_update_vs_str(off_t block_offset, VGMSTREAM * vgmstream) { STREAMFILE* streamFile = vgmstream->ch[0].streamfile; int i; diff --git a/src/layout/layout.h b/src/layout/layout.h index a1431a17..6e3b6598 100644 --- a/src/layout/layout.h +++ b/src/layout/layout.h @@ -21,8 +21,7 @@ void block_update_ws_aud(off_t block_offset, VGMSTREAM * vgmstream); void block_update_matx(off_t block_offset, VGMSTREAM * vgmstream); void block_update_dec(off_t block_offset, VGMSTREAM * vgmstream); void block_update_vs(off_t block_offset, VGMSTREAM * vgmstream); -void block_update_emff_ps2(off_t block_offset, VGMSTREAM * vgmstream); -void block_update_emff_ngc(off_t block_offset, VGMSTREAM * vgmstream); +void block_update_mul(off_t block_offset, VGMSTREAM * vgmstream); void block_update_gsb(off_t block_offset, VGMSTREAM * vgmstream); void block_update_xvas(off_t block_offset, VGMSTREAM * vgmstream); void block_update_thp(off_t block_offset, VGMSTREAM * vgmstream); @@ -33,7 +32,7 @@ void block_update_adm(off_t block_offset, VGMSTREAM * vgmstream); void block_update_bdsp(off_t block_offset, VGMSTREAM * vgmstream); void block_update_tra(off_t block_offset, VGMSTREAM * vgmstream); void block_update_ps2_iab(off_t block_offset, VGMSTREAM * vgmstream); -void block_update_ps2_strlr(off_t block_offset, VGMSTREAM * vgmstream); +void block_update_vs_str(off_t block_offset, VGMSTREAM * vgmstream); void block_update_rws(off_t block_offset, VGMSTREAM * vgmstream); void block_update_hwas(off_t block_offset, VGMSTREAM * vgmstream); void block_update_ea_sns(off_t block_offset, VGMSTREAM * vgmstream); @@ -46,7 +45,7 @@ void block_update_ea_wve_ad10(off_t block_offset, VGMSTREAM * vgmstream); void block_update_sthd(off_t block_offset, VGMSTREAM * vgmstream); void block_update_h4m(off_t block_offset, VGMSTREAM * vgmstream); void block_update_xa_aiff(off_t block_offset, VGMSTREAM * vgmstream); -void block_update_vs_ffx(off_t block_offset, VGMSTREAM * vgmstream); +void block_update_vs_square(off_t block_offset, VGMSTREAM * vgmstream); /* other layouts */ void render_vgmstream_interleave(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstream); diff --git a/src/libvgmstream.vcproj b/src/libvgmstream.vcproj index e43284f2..7e6b87fb 100644 --- a/src/libvgmstream.vcproj +++ b/src/libvgmstream.vcproj @@ -525,7 +525,7 @@ > + + - + @@ -146,6 +146,7 @@ + @@ -170,7 +171,7 @@ - + @@ -251,7 +252,7 @@ - + @@ -398,7 +399,7 @@ - + @@ -546,7 +547,7 @@ - + @@ -555,7 +556,7 @@ - + diff --git a/src/libvgmstream.vcxproj.filters b/src/libvgmstream.vcxproj.filters index f36e72ff..bc857d48 100644 --- a/src/libvgmstream.vcxproj.filters +++ b/src/libvgmstream.vcxproj.filters @@ -334,7 +334,7 @@ meta\Source Files - + meta\Source Files @@ -760,7 +760,7 @@ meta\Source Files - + meta\Source Files @@ -1192,7 +1192,7 @@ layout\Source Files - + layout\Source Files @@ -1213,7 +1213,7 @@ layout\Source Files - + layout\Source Files @@ -1303,7 +1303,7 @@ meta\Source Files - + meta\Source Files @@ -1315,7 +1315,7 @@ meta\Source Files - + layout\Source Files @@ -1360,6 +1360,9 @@ meta\Source Files + + meta\Source Files + meta\Source Files diff --git a/src/meta/emff.c b/src/meta/emff.c deleted file mode 100644 index 79928e66..00000000 --- a/src/meta/emff.c +++ /dev/null @@ -1,179 +0,0 @@ -#include "meta.h" -#include "../layout/layout.h" -#include "../util.h" - -/* EMFF - Eidos Music File Format (PS2), -Legacy of Kain - Defiance, possibly more... */ -VGMSTREAM * init_vgmstream_emff_ps2(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; - off_t start_offset; - int loop_flag = 0; - int channel_count; - int frequency; - int i; - - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("emff",filename_extension(filename))) goto fail; - - /* do some checks on the file, cause we have no magic words to check the header... - it seems if 0x800 and 0x804 = 0 then the file has only audio, if 0x800 = 1 - it has a text section, if both are 1 it's video with a text section included... */ - if (read_32bitBE(0x800,streamFile) == 0x01000000 || /* "0x01000000" */ - read_32bitBE(0x804,streamFile) == 0x01000000) /* "0x01000000" */ - goto fail; - - frequency = read_32bitLE(0x0,streamFile); - channel_count = read_32bitLE(0xC,streamFile); - - if (frequency > 48000 || - channel_count > 8) { - goto fail; - } - - loop_flag = (read_32bitLE(0x4,streamFile) != 0xFFFFFFFF); - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); - if (!vgmstream) goto fail; - - /* fill in the vital statistics */ - start_offset = 0x800; - vgmstream->sample_rate = frequency; - vgmstream->channels = channel_count; - vgmstream->coding_type = coding_PSX; - - vgmstream->layout_type = layout_blocked_emff_ps2; - vgmstream->interleave_block_size = 0x10; - vgmstream->meta_type = meta_EMFF_PS2; - - /* open the file for reading */ - { - STREAMFILE * file; - file = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); - if (!file) goto fail; - for (i=0;ich[i].streamfile = file; - } - } - - /* Calc num_samples */ - block_update_emff_ps2(start_offset,vgmstream); - vgmstream->num_samples = read_32bitLE(0x8,streamFile); - if (loop_flag) { - vgmstream->loop_start_sample = (read_32bitLE(0x28,streamFile)-start_offset)*28/16/channel_count; - vgmstream->loop_end_sample = read_32bitLE(0x8,streamFile); - } - - return vgmstream; - -/* clean up anything we may have opened */ -fail: - if (vgmstream) close_vgmstream(vgmstream); - return NULL; -} - - -/* EMFF - Eidos Music File Format (NGC/WII), -found in Tomb Raider Legend/Anniversary/Underworld, possibly more... */ -VGMSTREAM * init_vgmstream_emff_ngc(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; - off_t start_offset; - int loop_flag = 0; - int channel_count; - int frequency; - int i; - int j; - - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("emff",filename_extension(filename))) goto fail; - - /* do some checks on the file, cause we have no magic words to check the header... - it seems if 0x800 and 0x804 = 0 then the file has only audio, if 0x800 = 1 - it has a text section, if both are 1 it's video with a text section included... */ - if (read_32bitBE(0x800,streamFile) == 0x00000001 || /* "0x00000001" */ - read_32bitBE(0x804,streamFile) == 0x00000001) /* "0x00000001" */ - goto fail; - - frequency = read_32bitBE(0x0,streamFile); - channel_count = read_32bitBE(0xC,streamFile); - - if (frequency > 48000 || - channel_count > 8) { - goto fail; - } - - loop_flag = (read_32bitBE(0x4,streamFile) != 0xFFFFFFFF); - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); - if (!vgmstream) goto fail; - - /* fill in the vital statistics */ - start_offset = 0x800; - vgmstream->sample_rate = frequency; - vgmstream->channels = channel_count; - vgmstream->coding_type = coding_NGC_DSP; - - /* Retrieving coefs and loops, depending on the file layout... */ - /* Found in Tomb Raider - Legend for GameCube */ - if (read_32bitBE(0xC8,streamFile) > 0x0) { - off_t coef_table[8] = {0xC8,0xF6,0x124,0x152,0x180,0x1AE,0x1DC,0x20A}; - for (j=0;jchannels;j++) { - for (i=0;i<16;i++) { - vgmstream->ch[j].adpcm_coef[i] = read_16bitBE(coef_table[j]+i*2,streamFile); - } - } - /* Found in Tomb Raider - Anniversary for WII */ - } else if (read_32bitBE(0xCC,streamFile) > 0x0) { - off_t coef_table[8] = {0xCC,0xFA,0x128,0x156,0x184,0x1B2,0x1E0,0x20E}; - for (j=0;jchannels;j++) { - for (i=0;i<16;i++) { - vgmstream->ch[j].adpcm_coef[i] = read_16bitBE(coef_table[j]+i*2,streamFile); - } -} - /* Found in Tomb Raider - Underworld for WII */ - } else if (read_32bitBE(0x2D0,streamFile) > 0x0) { - off_t coef_table[8] = {0x2D0,0x2FE,0x32C,0x35A,0x388,0x3B6,0x3E4,0x412}; - for (j=0;jchannels;j++) { - for (i=0;i<16;i++) { - vgmstream->ch[j].adpcm_coef[i] = read_16bitBE(coef_table[j]+i*2,streamFile); - } -} - - } else { - goto fail; - } - - vgmstream->layout_type = layout_blocked_emff_ngc; - vgmstream->interleave_block_size = 0x10; - vgmstream->meta_type = meta_EMFF_NGC; - - /* open the file for reading */ - { - STREAMFILE * file; - file = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); - if (!file) goto fail; - for (i=0;ich[i].streamfile = file; - } - } - - /* Calc num_samples */ - block_update_emff_ngc(start_offset,vgmstream); - vgmstream->num_samples = read_32bitBE(0x8,streamFile);; - if (loop_flag) { - vgmstream->loop_start_sample = (read_32bitBE(0x28,streamFile))*14/8/channel_count; - vgmstream->loop_end_sample = read_32bitBE(0x8,streamFile); - } - - return vgmstream; - -/* clean up anything we may have opened */ -fail: - if (vgmstream) close_vgmstream(vgmstream); - return NULL; -} diff --git a/src/meta/hca.c b/src/meta/hca.c index ee59c183..7fe5f68c 100644 --- a/src/meta/hca.c +++ b/src/meta/hca.c @@ -88,8 +88,8 @@ static inline void test_key(hca_codec_data * hca_data, uint64_t key, uint16_t su if (score < 0) return; - /* score 0 is not trustable, update too if something better is found */ - if (*best_score < 0 || (score < *best_score && score > 0) || (*best_score == 0 && score == 1)) { + /* update if something better is found */ + if (*best_score <= 0 || (score < *best_score && score > 0)) { *best_score = score; *best_keycode = key; } diff --git a/src/meta/hca_keys.h b/src/meta/hca_keys.h index 462d3d97..4acbbefb 100644 --- a/src/meta/hca_keys.h +++ b/src/meta/hca_keys.h @@ -228,7 +228,7 @@ static const hcakey_info hcakey_list[] = { // Taga Tame no Alchemist (iOS/Android) {5047159794308}, // 00000497222AAA84 - // Shin Tennis no Ouji-sama: Rising Beat (iOS/Android) + // Shin Tennis no Ouji-sama: Rising Beat (iOS/Android) voices? {4902201417679}, // 0000047561F95FCF // Kai-ri-Sei Million Arthur (Vita) diff --git a/src/meta/imc.c b/src/meta/imc.c new file mode 100644 index 00000000..ffdca931 --- /dev/null +++ b/src/meta/imc.c @@ -0,0 +1,130 @@ +#include "meta.h" +#include "../coding/coding.h" + +/* .IMC - from iNiS Gitaroo Man (PS2) */ +VGMSTREAM * init_vgmstream_imc(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + off_t start_offset; + int loop_flag, channel_count, sample_rate, interleave, blocks; + size_t file_size, data_size; + + + /* checks */ + /* .imc: extension from the main container */ + if (!check_extensions(streamFile, "imc")) + goto fail; + + channel_count = read_32bitLE(0x00, streamFile); + sample_rate = read_32bitLE(0x04, streamFile); + interleave = read_32bitLE(0x08, streamFile) * 0x10; /* number of frames in a block */ + blocks = read_32bitLE(0x0c, streamFile); /* number of interleave blocks (even in mono) */ + + file_size = get_streamfile_size(streamFile); + loop_flag = 0; + start_offset = 0x10; +VGM_LOG("3\n"); + /* extra checks since the header is so simple */ + if (channel_count < 1 || channel_count > 8 || sample_rate < 22000 || sample_rate > 48000) + goto fail; +VGM_LOG("4\n"); + if (interleave*blocks + start_offset != file_size) + goto fail; +VGM_LOG("5\n"); + + /* remove padding (important to play gapless subsongs, happens even for mono) */ + { + off_t min_offset = file_size - interleave; + off_t offset = file_size - 0x10; + + data_size = file_size - start_offset; + while (offset > min_offset) { + if (read_32bitLE(offset, streamFile) != 0) + break; + data_size -= 0x10*channel_count; + offset -= 0x10; + } + } + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count, loop_flag); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_IMC; + vgmstream->sample_rate = sample_rate; + vgmstream->num_samples = ps_bytes_to_samples(data_size, channel_count); + vgmstream->coding_type = coding_PSX; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = interleave; + + if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + goto fail; + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} + +/* ****************************************************************************** */ + +/* .IMC in containers */ +VGMSTREAM * init_vgmstream_imc_container(STREAMFILE *streamFile) { + VGMSTREAM *vgmstream = NULL; + STREAMFILE *temp_streamFile = NULL; + off_t header_offset, subfile_offset, next_offset, name_offset; + uint32_t flags1, flags2; + + size_t subfile_size; + int total_subsongs, target_subsong = streamFile->stream_index; + + /* checks */ + if (!check_extensions(streamFile, "imc")) + goto fail; + + total_subsongs = read_32bitLE(0x00, streamFile); + if (target_subsong == 0) target_subsong = 1; + if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail; + + + header_offset = 0x04 + 0x20*(target_subsong-1); + + name_offset = header_offset + 0x00; + //flags1 = (uint32_t)read_32bitLE(header_offset + 0x08, streamFile); + /* 0x0c: same for all songs in single .imc but varies between .imc */ + subfile_offset = read_32bitLE(header_offset + 0x10,streamFile); + //flags2 = (uint32_t)read_32bitLE(header_offset + 0x14, streamFile); + /* 0x18: same for all songs in single .imc but varies between .imc */ + /* 0x1c: flags? (0 or 2) */ +//VGM_LOG("1: %x, %x\n", flags1, flags2); +// if (!(flags1 == 0x77DE2A70 || flags1 == 0x77DE2A00 || flags1 == 0x00000020 || flags1 == 0x00000000)) +// goto fail; +// if (!(flags2 == 0x0000F095 || flags2 == 0x0012FA3C)) +// goto fail; +//VGM_LOG("2\n"); + + if (target_subsong == total_subsongs) { + next_offset = get_streamfile_size(streamFile); + } + else { + next_offset = read_32bitLE(header_offset + 0x20 + 0x10,streamFile); + } + subfile_size = next_offset - subfile_offset; + + + temp_streamFile = setup_subfile_streamfile(streamFile, subfile_offset,subfile_size, NULL); + if (!temp_streamFile) goto fail; + + vgmstream = init_vgmstream_imc(temp_streamFile); + if (!vgmstream) goto fail; + + close_streamfile(temp_streamFile); + vgmstream->num_streams = total_subsongs; + read_string(vgmstream->stream_name,0x08+1, name_offset,streamFile); + + return vgmstream; + +fail: + close_streamfile(temp_streamFile); + close_vgmstream(vgmstream); + return NULL; +} diff --git a/src/meta/meta.h b/src/meta/meta.h index 1c38f08e..9b392cea 100644 --- a/src/meta/meta.h +++ b/src/meta/meta.h @@ -361,8 +361,7 @@ VGMSTREAM * init_vgmstream_dc_dcsw_dcs(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_wii_smp(STREAMFILE * streamFile); -VGMSTREAM * init_vgmstream_emff_ps2(STREAMFILE * streamFile); -VGMSTREAM * init_vgmstream_emff_ngc(STREAMFILE * streamFile); +VGMSTREAM * init_vgmstream_mul(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_thp(STREAMFILE *streamFile); @@ -401,7 +400,7 @@ VGMSTREAM * init_vgmstream_zsd(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_ps2_vgs(STREAMFILE *streamFile); -VGMSTREAM * init_vgmstream_RedSpark(STREAMFILE *streamFile); +VGMSTREAM * init_vgmstream_redspark(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_ivaud(STREAMFILE *streamFile); @@ -534,7 +533,7 @@ VGMSTREAM * init_vgmstream_x360_tra(STREAMFILE* streamFile); VGMSTREAM * init_vgmstream_ps2_iab(STREAMFILE* streamFile); -VGMSTREAM * init_vgmstream_ps2_strlr(STREAMFILE* streamFile); +VGMSTREAM * init_vgmstream_vs_str(STREAMFILE* streamFile); VGMSTREAM * init_vgmstream_lsf_n1nj4n(STREAMFILE* streamFile); @@ -802,7 +801,7 @@ VGMSTREAM * init_vgmstream_xwma(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_xopus(STREAMFILE * streamFile); -VGMSTREAM * init_vgmstream_vs_ffx(STREAMFILE * streamFile); +VGMSTREAM * init_vgmstream_vs_square(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_msf_banpresto_wmsf(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_msf_banpresto_2msf(STREAMFILE * streamFile); @@ -822,4 +821,7 @@ VGMSTREAM * init_vgmstream_ogg_opus(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_nus3audio(STREAMFILE * streamFile); +VGMSTREAM * init_vgmstream_imc(STREAMFILE * streamFile); +VGMSTREAM * init_vgmstream_imc_container(STREAMFILE * streamFile); + #endif /*_META_H*/ diff --git a/src/meta/mul.c b/src/meta/mul.c new file mode 100644 index 00000000..cf262160 --- /dev/null +++ b/src/meta/mul.c @@ -0,0 +1,164 @@ +#include "meta.h" +#include "../layout/layout.h" +#include "../coding/coding.h" + +typedef enum { NONE, PSX, DSP, XBOX } mul_codec; + +/* .MUL - from Crystal Dynamics games [Legacy of Kain: Defiance (PS2), Tomb Raider Underworld (multi)] */ +VGMSTREAM * init_vgmstream_mul(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + off_t start_offset, coefs_offset = 0; + int loop_flag, channel_count, sample_rate, num_samples, loop_start; + int big_endian; + mul_codec codec = NONE; + int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL; + + + /* checks */ + /* .mul: found in the exe, used by the bigfile extractor (Gibbed.TombRaider) + * (some files have companion .mus/sam files but seem to be sequences/control stuff) + * .(extensionless): filenames as found in the bigfile + * .emff: fake extension ('Eidos Music File Format') */ + if (!check_extensions(streamFile, "mul,,emff")) + goto fail; + if (read_32bitBE(0x10,streamFile) != 0 || + read_32bitBE(0x14,streamFile) != 0 || + read_32bitBE(0x18,streamFile) != 0 || + read_32bitBE(0x1c,streamFile) != 0) + goto fail; + + big_endian = guess_endianness32bit(0x00, streamFile); + read_32bit = big_endian ? read_32bitBE : read_32bitLE; + + sample_rate = read_32bit(0x00,streamFile); + loop_start = read_32bit(0x04,streamFile); + num_samples = read_32bit(0x08,streamFile); + channel_count = read_32bit(0x0C,streamFile); + if (sample_rate < 8000 || sample_rate > 48000 || channel_count > 8) + goto fail; + /* 0x20: flag when file has non-audio blocks (ignored by the layout) */ + /* 0x24: same? */ + /* 0x28: loop offset within audio data (not file offset) */ + /* 0x2c: some value related to loop? */ + /* 0x34: id? */ + /* 0x38+: channel config until ~0x100? (multiple 0x3F800000 depending on the number of channels) */ + + /* test known versions (later versions start from 0x24 instead of 0x20) */ + if (!(read_32bit(0x38,streamFile) == 0x3F800000 || + read_32bit(0x3c,streamFile) == 0x3F800000)) /* Tomb Raider Underworld */ + goto fail; + + loop_flag = (loop_start >= 0); /* 0xFFFFFFFF when not looping */ + start_offset = 0x800; + + /* format is pretty limited so we need to guess codec */ + if (big_endian) { + /* test DSP (GC/Wii): check known coef locations */ + if (read_32bitBE(0xC8,streamFile) != 0) { /* Tomb Raider Legend (GC) */ + codec = DSP; + coefs_offset = 0xC8; + } + else if (read_32bitBE(0xCC,streamFile) != 0) { /* Tomb Raider Anniversary (Wii) */ + codec = DSP; + coefs_offset = 0xCC; + } + else if (read_32bitBE(0x2D0,streamFile) != 0) { /* Tomb Raider Underworld (Wii) */ + codec = DSP; + coefs_offset = 0x2D0; + } + + // todo test XMA1 (X360): mono streams, each block has 1 sub-blocks of 0x800 packet per channel + + // todo test ? (PS3) + } + else { + int i; + off_t offset = start_offset; + size_t file_size = get_streamfile_size(streamFile); + size_t frame_size; + + /* check first audio frame */ + while (offset < file_size) { + uint32_t block_type = read_32bit(offset+0x00, streamFile); + uint32_t block_size = read_32bit(offset+0x04, streamFile); + uint32_t data_size = read_32bit(offset+0x10, streamFile); + + if (block_type != 0x00) { + offset += 0x10 + block_size; + continue; /* not audio */ + } + + /* test PS-ADPCM (PS2/PSP): flag is always 2 in .mul */ + frame_size = 0x10; + for (i = 0; i < data_size / frame_size; i++) { + if (read_8bit(offset + 0x20 + frame_size*i + 0x01, streamFile) != 0x02) + break; + } + if (i == data_size / frame_size) { + codec = PSX; + break; + } + + /* test XBOX-IMA (PC/Xbox): reserved frame header value is always 0 */ + frame_size = 0x24; + for (i = 0; i < data_size / frame_size; i++) { + if (read_8bit(offset + 0x20 + frame_size*i + 0x03, streamFile) != 0x00) + break; + } + if (i == data_size / frame_size) { + codec = XBOX; + break; + } + + break; + } + } + + if (codec == NONE) { + VGM_LOG("MUL: unknown codec\n"); + goto fail; + } + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count,loop_flag); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_MUL; + vgmstream->sample_rate = sample_rate; + vgmstream->num_samples = num_samples; + vgmstream->loop_start_sample = loop_start; + vgmstream->loop_end_sample = num_samples; + vgmstream->codec_endian = big_endian; + + switch(codec) { + case PSX: + vgmstream->coding_type = coding_PSX; + vgmstream->layout_type = layout_blocked_mul; + break; + + case DSP: + vgmstream->coding_type = coding_NGC_DSP; + vgmstream->layout_type = layout_blocked_mul; + + dsp_read_coefs_be(vgmstream,streamFile,coefs_offset+0x00,0x2e); + dsp_read_hist_be (vgmstream,streamFile,coefs_offset+0x24,0x2e); + break; + + case XBOX: + vgmstream->coding_type = coding_XBOX_IMA_int; + vgmstream->layout_type = layout_blocked_mul; + break; + + default: + goto fail; + } + + if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + goto fail; + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} diff --git a/src/meta/redspark.c b/src/meta/redspark.c index aee8f5e3..fe8f0b93 100644 --- a/src/meta/redspark.c +++ b/src/meta/redspark.c @@ -13,7 +13,7 @@ static uint32_t find_key(uint32_t firstword) { /* RSD - RedSpark (MadWorld) RS3D - RedSpark (Mario & Luigi: Dream Team I fi*/ -VGMSTREAM * init_vgmstream_RedSpark(STREAMFILE *streamFile) { +VGMSTREAM * init_vgmstream_redspark(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; char filename[PATH_LIMIT]; off_t start_offset; @@ -105,7 +105,7 @@ VGMSTREAM * init_vgmstream_RedSpark(STREAMFILE *streamFile) { } else { vgmstream->layout_type = layout_none; } - vgmstream->meta_type = meta_RedSpark; + vgmstream->meta_type = meta_REDSPARK; { diff --git a/src/meta/vs.c b/src/meta/vs.c index d36aee6f..57589cff 100644 --- a/src/meta/vs.c +++ b/src/meta/vs.c @@ -1,75 +1,52 @@ #include "meta.h" #include "../layout/layout.h" -#include "../util.h" +#include "../coding/coding.h" -/* VS (from Men in Black) */ +/* .VS - from Melbourne House games [Men in Black II (PS2), Grand Prix Challenge (PS2) */ VGMSTREAM * init_vgmstream_vs(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; - - char filename[PATH_LIMIT]; off_t start_offset; - int loop_flag = 0; - int channel_count; - int i; + int loop_flag, channel_count; - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("vs",filename_extension(filename))) goto fail; - - /* check header */ - if (read_32bitBE(0x00,streamFile) != 0xC8000000) /* "0xC8000000" */ + /* checks */ + if (!check_extensions(streamFile, "vs")) + goto fail; + if (read_32bitBE(0x00,streamFile) != 0xC8000000) goto fail; loop_flag = 0; channel_count = 2; - - /* build the VGMSTREAM */ + start_offset = 0x08; + + + /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; - /* fill in the vital statistics */ - start_offset = 0x08; - vgmstream->channels = channel_count; - vgmstream->interleave_block_size=0x10; + vgmstream->meta_type = meta_VS; vgmstream->sample_rate = read_32bitLE(0x04,streamFile); vgmstream->coding_type = coding_PSX; + vgmstream->layout_type = layout_blocked_vs; -#if 0 - if (loop_flag) { - vgmstream->loop_start_sample = 0; - vgmstream->loop_end_sample = (read_32bitLE(0x0c,streamFile)-start_offset); - } -#endif + if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + goto fail; - vgmstream->layout_type = layout_blocked_vs; - vgmstream->meta_type = meta_VS; - - - /* open the file for reading */ - { - for (i=0;ich[i].streamfile = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); - if (!vgmstream->ch[i].streamfile) goto fail; + /* calc num_samples */ + { + vgmstream->next_block_offset = start_offset; + do { + block_update(vgmstream->next_block_offset,vgmstream); + vgmstream->num_samples += ps_bytes_to_samples(vgmstream->current_block_size, 1); } + while (vgmstream->next_block_offset < get_streamfile_size(streamFile)); + block_update(start_offset, vgmstream); + } - - /* Calc num_samples */ - block_update_vs(start_offset,vgmstream); - vgmstream->num_samples=0; - - do { - vgmstream->num_samples += vgmstream->current_block_size*28/16; - block_update_vs(vgmstream->next_block_offset,vgmstream); - } while (vgmstream->next_block_offsetmeta_type = meta_VS_FFX; + vgmstream->meta_type = meta_VS_SQUARE; vgmstream->sample_rate = round10((48000 * pitch) / 4096); /* needed for rare files */ vgmstream->coding_type = coding_PSX; - vgmstream->layout_type = layout_blocked_vs_ffx; + vgmstream->layout_type = layout_blocked_vs_square; if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) goto fail; diff --git a/src/meta/ps2_strlr.c b/src/meta/vs_str.c similarity index 87% rename from src/meta/ps2_strlr.c rename to src/meta/vs_str.c index 83770fc4..a6002b36 100644 --- a/src/meta/ps2_strlr.c +++ b/src/meta/vs_str.c @@ -3,7 +3,7 @@ #include "../coding/coding.h" /* .vs/STRx - from The Bouncer (PS2) */ -VGMSTREAM * init_vgmstream_ps2_strlr(STREAMFILE *streamFile) { +VGMSTREAM * init_vgmstream_vs_str(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; int channel_count, loop_flag; off_t start_offset; @@ -29,10 +29,10 @@ VGMSTREAM * init_vgmstream_ps2_strlr(STREAMFILE *streamFile) { vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; - vgmstream->meta_type = meta_PS2_STRLR; + vgmstream->meta_type = meta_VS_STR; vgmstream->sample_rate = 44100; vgmstream->coding_type = coding_PSX; - vgmstream->layout_type = layout_blocked_ps2_strlr; + vgmstream->layout_type = layout_blocked_vs_str; if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) goto fail; diff --git a/src/meta/zsnd.c b/src/meta/zsnd.c index 4d055a9a..63d1e9b7 100644 --- a/src/meta/zsnd.c +++ b/src/meta/zsnd.c @@ -108,9 +108,11 @@ VGMSTREAM * init_vgmstream_zsnd(STREAMFILE *streamFile) { name_size = 0; switch(sample_rate) { case 0x0800: sample_rate = 22050; break; + case 0x0687: sample_rate = 18000; break; + case 0x05ce: sample_rate = 16000; break; case 0x0400: sample_rate = 11025; break; default: - VGM_LOG("ZSND: unknown sample_rate\n"); + VGM_LOG("ZSND: unknown sample_rate %x at %x\n", sample_rate, (uint32_t)header2_offset); goto fail; } break; diff --git a/src/vgmstream.c b/src/vgmstream.c index fe8f9191..fc7cb3cb 100644 --- a/src/vgmstream.c +++ b/src/vgmstream.c @@ -192,8 +192,7 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_vgs, init_vgmstream_dc_dcsw_dcs, init_vgmstream_wii_smp, - init_vgmstream_emff_ps2, - init_vgmstream_emff_ngc, + init_vgmstream_mul, init_vgmstream_thp, init_vgmstream_wii_sts, init_vgmstream_ps2_p2bt, @@ -213,7 +212,7 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_ps2_mcg, init_vgmstream_zsd, init_vgmstream_ps2_vgs, - init_vgmstream_RedSpark, + init_vgmstream_redspark, init_vgmstream_ivaud, init_vgmstream_wii_wsd, init_vgmstream_wii_ndp, @@ -290,7 +289,7 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_ps2_spm, init_vgmstream_x360_tra, init_vgmstream_ps2_iab, - init_vgmstream_ps2_strlr, + init_vgmstream_vs_str, init_vgmstream_lsf_n1nj4n, init_vgmstream_vawx, init_vgmstream_ps2_wmus, @@ -445,7 +444,7 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_ue4opus, init_vgmstream_xwma, init_vgmstream_xopus, - init_vgmstream_vs_ffx, + init_vgmstream_vs_square, init_vgmstream_msf_banpresto_wmsf, init_vgmstream_msf_banpresto_2msf, init_vgmstream_nwav, @@ -458,6 +457,8 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_dsp_adpcmx, init_vgmstream_ogg_opus, init_vgmstream_nus3audio, + init_vgmstream_imc, + init_vgmstream_imc_container, /* lowest priority metas (should go after all metas, and TXTH should go before raw formats) */ init_vgmstream_txth, /* proper parsers should supersede TXTH, once added */ @@ -1006,8 +1007,7 @@ void render_vgmstream(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstre case layout_blocked_matx: case layout_blocked_dec: case layout_blocked_vs: - case layout_blocked_emff_ps2: - case layout_blocked_emff_ngc: + case layout_blocked_mul: case layout_blocked_gsb: case layout_blocked_xvas: case layout_blocked_thp: @@ -1018,7 +1018,7 @@ void render_vgmstream(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstre case layout_blocked_bdsp: case layout_blocked_tra: case layout_blocked_ps2_iab: - case layout_blocked_ps2_strlr: + case layout_blocked_vs_str: case layout_blocked_rws: case layout_blocked_hwas: case layout_blocked_ea_sns: @@ -1031,7 +1031,7 @@ void render_vgmstream(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstre case layout_blocked_sthd: case layout_blocked_h4m: case layout_blocked_xa_aiff: - case layout_blocked_vs_ffx: + case layout_blocked_vs_square: render_vgmstream_blocked(buffer,sample_count,vgmstream); break; case layout_aix: diff --git a/src/vgmstream.h b/src/vgmstream.h index 1e169121..0770669b 100644 --- a/src/vgmstream.h +++ b/src/vgmstream.h @@ -229,8 +229,7 @@ typedef enum { layout_blocked_dec, layout_blocked_xvas, layout_blocked_vs, - layout_blocked_emff_ps2, - layout_blocked_emff_ngc, + layout_blocked_mul, layout_blocked_gsb, layout_blocked_thp, layout_blocked_filp, @@ -241,7 +240,7 @@ typedef enum { layout_blocked_ivaud, /* GTA IV .ivaud blocks */ layout_blocked_tra, /* DefJam Rapstar .tra blocks */ layout_blocked_ps2_iab, - layout_blocked_ps2_strlr, + layout_blocked_vs_str, layout_blocked_rws, layout_blocked_hwas, layout_blocked_ea_sns, /* newest Electronic Arts blocks, found in SNS/SNU/SPS/etc formats */ @@ -254,7 +253,7 @@ typedef enum { layout_blocked_sthd, /* Dream Factory STHD */ layout_blocked_h4m, /* H4M video */ layout_blocked_xa_aiff, /* XA in AIFF files [Crusader: No Remorse (SAT), Road Rash (3DO)] */ - layout_blocked_vs_ffx, + layout_blocked_vs_square, /* otherwise odd */ layout_aix, /* CRI AIX's wheels within wheels */ @@ -441,8 +440,7 @@ typedef enum { meta_DC_DCSW_DCS, /* Evil Twin - Cypriens Chronicles (DC) */ meta_WII_SMP, /* Mushroom Men - The Spore Wars */ meta_WII_SNG, /* Excite Trucks */ - meta_EMFF_PS2, /* Eidos Music File Format for PS2*/ - meta_EMFF_NGC, /* Eidos Music File Format for NGC/WII */ + meta_MUL, meta_SAT_BAKA, /* Crypt Killer */ meta_PS2_VSF, /* Musashi: Samurai Legend */ meta_PS2_VSF_TTA, /* Tiny Toon Adventures: Defenders of the Universe */ @@ -502,8 +500,8 @@ typedef enum { meta_PS2_TK5, /* Tekken 5 Stream Files */ meta_PS2_MCG, /* Gunvari MCG Files (was name .GCM on disk) */ meta_ZSD, /* Dragon Booster ZSD */ - meta_RedSpark, /* "RedSpark" RSD (MadWorld) */ - meta_IVAUD, /* .ivaud GTA IV */ + meta_REDSPARK, /* "RedSpark" RSD (MadWorld) */ + meta_IVAUD, /* .ivaud GTA IV */ meta_NDS_HWAS, /* Spider-Man 3, Tony Hawk's Downhill Jam, possibly more... */ meta_NGC_LPS, /* Rave Master (Groove Adventure Rave)(GC) */ meta_NAOMI_ADPCM, /* NAOMI/NAOMI2 ARcade games */ @@ -567,7 +565,7 @@ typedef enum { meta_X360_TRA, /* Def Jam Rapstar */ meta_PS2_VGS, /* Princess Soft PS2 games */ meta_PS2_IAB, /* Ueki no Housoku - Taosu ze Robert Juudan!! (PS2) */ - meta_PS2_STRLR, /* The Bouncer */ + meta_VS_STR, /* The Bouncer */ meta_LSF_N1NJ4N, /* .lsf n1nj4n Fastlane Street Racing (iPhone) */ meta_VAWX, /* feelplus: No More Heroes Heroes Paradise, Moon Diver */ meta_PC_SNDS, /* Incredibles PC .snds */ @@ -702,14 +700,15 @@ typedef enum { meta_XWMA, meta_VA3, /* DDR Supernova 2 AC */ meta_XOPUS, - meta_VS_FFX, + meta_VS_SQUARE, meta_NWAV, meta_XPCM, meta_MSF_TAMASOFT, meta_XPS_DAT, meta_ZSND, meta_DSP_ADPCMX, - meta_OGG_OPUS + meta_OGG_OPUS, + meta_IMC, } meta_t;