diff --git a/Frameworks/vgmstream/libvgmstream.xcodeproj/project.pbxproj b/Frameworks/vgmstream/libvgmstream.xcodeproj/project.pbxproj index 869455955..51da50c01 100644 --- a/Frameworks/vgmstream/libvgmstream.xcodeproj/project.pbxproj +++ b/Frameworks/vgmstream/libvgmstream.xcodeproj/project.pbxproj @@ -136,6 +136,9 @@ 832BF82B21E0514B006F50F1 /* xopus.c in Sources */ = {isa = PBXBuildFile; fileRef = 832BF81921E0514A006F50F1 /* xopus.c */; }; 832BF82C21E0514B006F50F1 /* hca_keys_awb.h in Headers */ = {isa = PBXBuildFile; fileRef = 832BF81A21E0514A006F50F1 /* hca_keys_awb.h */; }; 832BF82D21E0514B006F50F1 /* nus3audio.c in Sources */ = {isa = PBXBuildFile; fileRef = 832BF81B21E0514B006F50F1 /* nus3audio.c */; }; + 832FC36C278FA4CB0056A860 /* ubi_ckd_cwav_streamfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 832FC367278FA4CB0056A860 /* ubi_ckd_cwav_streamfile.h */; }; + 832FC36D278FA4CB0056A860 /* ubi_ckd_cwav.c in Sources */ = {isa = PBXBuildFile; fileRef = 832FC36B278FA4CB0056A860 /* ubi_ckd_cwav.c */; }; + 832FC36F278FAE3E0056A860 /* encrypted_mc161_streamfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 832FC36E278FAE3E0056A860 /* encrypted_mc161_streamfile.h */; }; 83345A521F8AEB2800B2EAA4 /* xvag.c in Sources */ = {isa = PBXBuildFile; fileRef = 83345A4E1F8AEB2800B2EAA4 /* xvag.c */; }; 83349719275DD2AC00302E21 /* wbk.c in Sources */ = {isa = PBXBuildFile; fileRef = 83349715275DD2AC00302E21 /* wbk.c */; }; 833A7A2E1ED11961003EC53E /* xau.c in Sources */ = {isa = PBXBuildFile; fileRef = 833A7A2D1ED11961003EC53E /* xau.c */; }; @@ -322,7 +325,6 @@ 836F6F9A18BDC2190095E648 /* meta.h in Headers */ = {isa = PBXBuildFile; fileRef = 836F6E5E18BDC2180095E648 /* meta.h */; }; 836F6F9B18BDC2190095E648 /* mn_str.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E5F18BDC2180095E648 /* mn_str.c */; }; 836F6F9C18BDC2190095E648 /* mp4.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E6018BDC2180095E648 /* mp4.c */; }; - 836F6F9D18BDC2190095E648 /* msvp.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E6118BDC2180095E648 /* msvp.c */; }; 836F6F9E18BDC2190095E648 /* mus_acm.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E6218BDC2180095E648 /* mus_acm.c */; }; 836F6F9F18BDC2190095E648 /* musc.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E6318BDC2180095E648 /* musc.c */; }; 836F6FA018BDC2190095E648 /* musx.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E6418BDC2180095E648 /* musx.c */; }; @@ -953,6 +955,9 @@ 832BF81921E0514A006F50F1 /* xopus.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xopus.c; sourceTree = ""; }; 832BF81A21E0514A006F50F1 /* hca_keys_awb.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = hca_keys_awb.h; sourceTree = ""; }; 832BF81B21E0514B006F50F1 /* nus3audio.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = nus3audio.c; sourceTree = ""; }; + 832FC367278FA4CB0056A860 /* ubi_ckd_cwav_streamfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ubi_ckd_cwav_streamfile.h; sourceTree = ""; }; + 832FC36B278FA4CB0056A860 /* ubi_ckd_cwav.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ubi_ckd_cwav.c; sourceTree = ""; }; + 832FC36E278FAE3E0056A860 /* encrypted_mc161_streamfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = encrypted_mc161_streamfile.h; sourceTree = ""; }; 83345A4E1F8AEB2800B2EAA4 /* xvag.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xvag.c; sourceTree = ""; }; 83349715275DD2AC00302E21 /* wbk.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = wbk.c; sourceTree = ""; }; 833A7A2D1ED11961003EC53E /* xau.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xau.c; sourceTree = ""; }; @@ -1139,7 +1144,6 @@ 836F6E5E18BDC2180095E648 /* meta.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = meta.h; sourceTree = ""; }; 836F6E5F18BDC2180095E648 /* mn_str.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mn_str.c; sourceTree = ""; }; 836F6E6018BDC2180095E648 /* mp4.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mp4.c; sourceTree = ""; }; - 836F6E6118BDC2180095E648 /* msvp.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = msvp.c; sourceTree = ""; }; 836F6E6218BDC2180095E648 /* mus_acm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mus_acm.c; sourceTree = ""; }; 836F6E6318BDC2180095E648 /* musc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = musc.c; sourceTree = ""; }; 836F6E6418BDC2180095E648 /* musx.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = musx.c; sourceTree = ""; }; @@ -1904,6 +1908,7 @@ 8306B0C42098458D000302D4 /* ea_wve_ad10.c */, 8306B0BF2098458C000302D4 /* ea_wve_au00.c */, 83AF2CC826226BA500538240 /* encrypted_bgm_streamfile.h */, + 832FC36E278FAE3E0056A860 /* encrypted_mc161_streamfile.h */, 83031ECE243C50DE00C3F3E0 /* encrypted.c */, 836F6E4918BDC2180095E648 /* exakt_sc.c */, 836F6E4A18BDC2180095E648 /* excitebots.c */, @@ -1990,7 +1995,6 @@ 83C7280A22BC893C00678B4A /* msf.c */, 83709E011ECBC1A4005C03D3 /* mss.c */, 834FE0E7215C79EC000A5D3D /* msv.c */, - 836F6E6118BDC2180095E648 /* msvp.c */, 83C7280E22BC893D00678B4A /* mta2_streamfile.h */, 83C727FF22BC893900678B4A /* mta2.c */, 83C7280322BC893A00678B4A /* mtaf.c */, @@ -2182,6 +2186,8 @@ 8306B0D22098458F000302D4 /* txtp.c */, 8351F32B2212B57000A606E4 /* ubi_bao_streamfile.h */, 8306B0D420984590000302D4 /* ubi_bao.c */, + 832FC367278FA4CB0056A860 /* ubi_ckd_cwav_streamfile.h */, + 832FC36B278FA4CB0056A860 /* ubi_ckd_cwav.c */, 836F6EFC18BDC2190095E648 /* ubi_ckd.c */, 837CEAE023487F2A00E62A4A /* ubi_hx.c */, 8306B0D720984590000302D4 /* ubi_jade.c */, @@ -2341,6 +2347,7 @@ 83AA7F802519C042004C5298 /* sab_streamfile.h in Headers */, 83A21F87201D8981000F04B9 /* fsb_keys.h in Headers */, 83E7FD6325EF2B0C00683FD2 /* tac_decoder_lib_data.h in Headers */, + 832FC36C278FA4CB0056A860 /* ubi_ckd_cwav_streamfile.h in Headers */, 83E7FD6125EF2B0C00683FD2 /* tac_decoder_lib.h in Headers */, 834FE0EC215C79ED000A5D3D /* kma9_streamfile.h in Headers */, 83E7FD6225EF2B0C00683FD2 /* tac_decoder_lib_ops.h in Headers */, @@ -2376,6 +2383,7 @@ 83C7280F22BC893D00678B4A /* xwb_xsb.h in Headers */, 8315868B26F586F900803A3A /* m2_psb.h in Headers */, 83AA7F732519BFEA004C5298 /* mpeg_bitreader.h in Headers */, + 832FC36F278FAE3E0056A860 /* encrypted_mc161_streamfile.h in Headers */, 83C7281622BC893D00678B4A /* xwma_konami_streamfile.h in Headers */, 839E21E81F2EDAF100EE54D7 /* mpeg_decoder.h in Headers */, 839E21E51F2EDAF100EE54D7 /* vorbis_custom_decoder.h in Headers */, @@ -2679,6 +2687,7 @@ 83299FD01E7660C7003A3242 /* bik.c in Sources */, 8346D97C25BF838C00D1A8B0 /* mjb_mjh.c in Sources */, 839C3D27270D49FF00E13653 /* lpcm_fb.c in Sources */, + 832FC36D278FA4CB0056A860 /* ubi_ckd_cwav.c in Sources */, 8373341823F60C7B00DE14DC /* tgcadpcm_decoder.c in Sources */, 83349719275DD2AC00302E21 /* wbk.c in Sources */, 83FC417326D3304D009A2022 /* xsh_xsd_xss.c in Sources */, @@ -3126,7 +3135,6 @@ 8349A9151FE6258200E26435 /* ps2_xa2_rrp.c in Sources */, 836F703518BDC2190095E648 /* svs.c in Sources */, 836F6FA318BDC2190095E648 /* naomi_spsd.c in Sources */, - 836F6F9D18BDC2190095E648 /* msvp.c in Sources */, 8306B0B920984552000302D4 /* blocked_matx.c in Sources */, 83A21F7B201D895B000F04B9 /* blocked_xvag.c in Sources */, 836F6FBA18BDC2190095E648 /* ngc_ymf.c in Sources */, diff --git a/Frameworks/vgmstream/vgmstream/src/formats.c b/Frameworks/vgmstream/vgmstream/src/formats.c index e762af24e..a599021ce 100644 --- a/Frameworks/vgmstream/vgmstream/src/formats.c +++ b/Frameworks/vgmstream/vgmstream/src/formats.c @@ -339,7 +339,7 @@ static const char* extension_list[] = { "msf", "mss", "msv", - "msvp", + "msvp", //fake extension/header id for .msv "mta2", "mtaf", "mul", @@ -1081,7 +1081,6 @@ static const meta_info meta_info_list[] = { {meta_ISH_ISD, "ISH+ISD DSP Header"}, {meta_GSP_GSB, "Tecmo GSP+GSB Header"}, {meta_YDSP, "Yuke's DSP (YDSP) Header"}, - {meta_MSVP, "MSVP Header"}, {meta_NGC_SSM, "SSM DSP Header"}, {meta_PS2_JOE, "Asobo Studio .JOE header"}, {meta_VGS, "Guitar Hero VGS Header"}, diff --git a/Frameworks/vgmstream/vgmstream/src/meta/encrypted.c b/Frameworks/vgmstream/vgmstream/src/meta/encrypted.c index 123c6d680..62d06d8b1 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/encrypted.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/encrypted.c @@ -2,6 +2,7 @@ #include "../coding/coding.h" #include "ogg_vorbis_streamfile.h" #include "encrypted_bgm_streamfile.h" +#include "encrypted_mc161_streamfile.h" //todo fuse ogg encryptions and use generic names @@ -158,7 +159,7 @@ static VGMSTREAM* init_vgmstream_encrypted_rpgmvo_riff(STREAMFILE* sf) { e.new_sf = setup_subfile_streamfile(e.temp_sf, 0x10, riff_size, "wav"); if (!e.new_sf) goto fail; -dump_streamfile(e.new_sf, 0); + e.vgmstream = init_vgmstream_riff(e.new_sf); close_streamfile(e.temp_sf); close_streamfile(e.new_sf); @@ -171,6 +172,26 @@ fail: } +/* Minecraft (PC) before v1.6.1 (Java version) */ +static VGMSTREAM* init_vgmstream_encrypted_mc161(STREAMFILE* sf) { + encrypted_t e = {0}; + + if (!check_extensions(sf,"mus")) + goto fail; + + /* all files use a different key so just fail on meta init */ + + e.temp_sf = setup_mc161_streamfile(sf); + if (!e.temp_sf) goto fail; + + e.vgmstream = init_vgmstream_ogg_vorbis(e.temp_sf); + close_streamfile(e.temp_sf); + return e.vgmstream; +fail: + return NULL; +} + + /* parser for various encrypted games */ VGMSTREAM* init_vgmstream_encrypted(STREAMFILE* sf) { VGMSTREAM* v = NULL; @@ -187,5 +208,8 @@ VGMSTREAM* init_vgmstream_encrypted(STREAMFILE* sf) { v = init_vgmstream_encrypted_rpgmvo_riff(sf); if (v) return v; + v = init_vgmstream_encrypted_mc161(sf); + if (v) return v; + return NULL; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/encrypted_mc161_streamfile.h b/Frameworks/vgmstream/vgmstream/src/meta/encrypted_mc161_streamfile.h new file mode 100644 index 000000000..e8a1fde6a --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/encrypted_mc161_streamfile.h @@ -0,0 +1,101 @@ +#ifndef _MC161_STREAMFILE_H_ +#define _MC161_STREAMFILE_H_ +#include "../streamfile.h" + + +typedef struct { + int32_t base_key; + int32_t curr_key; + uint32_t curr_offset; +} mc161_io_data; + + +static void decrypt_chunk(uint8_t* buf, int buf_size, mc161_io_data* data) { + int i; + int32_t hash = data->curr_key; + + + for (i = 0; i < buf_size; i++) { + buf[i] = (uint8_t)(buf[i] ^ ((hash >> 8) & 0xFF)); + hash = (int32_t)(hash * 498729871) + (85731 * (int8_t)buf[i]); /* signed */ + } + + data->curr_key = hash; + data->curr_offset += buf_size; +} + +static void update_key(STREAMFILE* sf, off_t offset, mc161_io_data* data) { + uint8_t buf[0x800]; + size_t bytes; + size_t to_skip; + + if (offset < data->curr_offset || offset == 0x00) { + data->curr_key = data->base_key; + data->curr_offset = 0x00; + to_skip = offset; + } + else { + to_skip = offset - data->curr_offset; + } + + /* update key by reading and decrypt all data between current offset + last known key to requested offset */ + while (to_skip > 0) { + size_t read_size = sizeof(buf); + if (read_size > to_skip) + read_size = to_skip; + + bytes = read_streamfile(buf, data->curr_offset, read_size, sf); + if (!bytes) /* ??? */ + break; + + decrypt_chunk(buf, bytes, data); /* updates curr_offset and key */ + to_skip -= bytes; + } +} + +/* XOR depends on decrypted data, meanings having to decrypt linearly to reach some offset. */ +static size_t mc161_io_read(STREAMFILE* sf, uint8_t* dest, off_t offset, size_t length, mc161_io_data* data) { + size_t bytes; + + /* read and decrypt unneded data */ + update_key(sf, offset, data); + + /* read and decrypt current data */ + bytes = read_streamfile(dest, offset, length, sf); + decrypt_chunk(dest, bytes, data); + + return bytes; +} + +/* String.hashCode() should be equivalent to this on Windows (though implementation-defined), note Java has no unsigned */ +static int32_t mc161_get_java_hashcode(STREAMFILE* sf) { + char filename[1024]; + int i = 0; + int32_t hash = 0; + + get_streamfile_filename(sf, filename, sizeof(filename)); + + while (filename[i] != '\0') { + hash = 31 * hash + (uint8_t)filename[i]; + i++; + } + + return hash; +} + +/* decrypts Minecraft old streams (some info from: https://github.com/ata4/muscode) */ +static STREAMFILE* setup_mc161_streamfile(STREAMFILE* sf) { + STREAMFILE* new_sf = NULL; + mc161_io_data io_data = {0}; + + io_data.base_key = mc161_get_java_hashcode(sf); + io_data.curr_key = io_data.base_key; + io_data.curr_offset = 0x00; + + new_sf = open_wrap_streamfile(sf); + new_sf = open_io_streamfile_f(new_sf, &io_data, sizeof(mc161_io_data), mc161_io_read, NULL); + new_sf = open_fakename_streamfile_f(new_sf, NULL, "ogg"); + return new_sf; +} + +#endif /* _MC161_STREAMFILE_H_ */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/hca_keys.h b/Frameworks/vgmstream/vgmstream/src/meta/hca_keys.h index 1022d80e8..6f3d89aae 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/hca_keys.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/hca_keys.h @@ -874,8 +874,11 @@ static const hcakey_info hcakey_list[] = { // Super Robot Wars 30 (PC) {6734488621090458}, // 0017ECFB5201069A - // CHUNITHM NEW (AC) - {32931609366120192}, // 0074FF1FCE264700 + // CHUNITHM NEW (AC) + {32931609366120192}, // 0074FF1FCE264700 + + // Shaman King: Funbari Chronicle (Android) + {1620612098671}, // 0000017954022A6F }; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/meta.h b/Frameworks/vgmstream/vgmstream/src/meta/meta.h index 0b22dc921..51375bd6e 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/meta.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/meta.h @@ -345,8 +345,6 @@ VGMSTREAM * init_vgmstream_ydsp(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_gsp_gsb(STREAMFILE * streamFile); -VGMSTREAM * init_vgmstream_msvp(STREAMFILE * streamFile); - VGMSTREAM * init_vgmstream_ngc_ssm(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_ps2_joe(STREAMFILE * streamFile); @@ -971,4 +969,6 @@ VGMSTREAM* init_vgmstream_lpcm_fb(STREAMFILE* sf); VGMSTREAM* init_vgmstream_wbk(STREAMFILE* sf); VGMSTREAM* init_vgmstream_wbk_nslb(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_ubi_ckd_cwav(STREAMFILE* sf); + #endif /*_META_H*/ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/msv.c b/Frameworks/vgmstream/vgmstream/src/meta/msv.c index ff844b817..d03ceb8a6 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/msv.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/msv.c @@ -1,43 +1,47 @@ -#include "meta.h" -#include "../coding/coding.h" - -/* MSV - from Sony MultiStream format [Fight Club (PS2)] */ -VGMSTREAM * init_vgmstream_msv(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - off_t start_offset; - size_t channel_size; - int loop_flag, channel_count; - - - /* checks */ - if ( !check_extensions(streamFile,"msv") ) - goto fail; - if (read_32bitBE(0x00,streamFile) != 0x4D535670) /* "MSVp" */ - goto fail; - - start_offset = 0x30; - channel_count = 1; - channel_size = read_32bitBE(0x0c,streamFile); - loop_flag = 0; /* no looping and last 16 bytes (end frame) are removed, from Sony's docs */ - - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); - if (!vgmstream) goto fail; - - vgmstream->meta_type = meta_MSV; - vgmstream->sample_rate = read_32bitBE(0x10,streamFile); - vgmstream->num_samples = ps_bytes_to_samples(channel_size,1); - - vgmstream->coding_type = coding_PSX; - vgmstream->layout_type = layout_none; - read_string(vgmstream->stream_name,0x10+1, 0x20,streamFile); - - if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) ) - goto fail; - return vgmstream; - -fail: - close_vgmstream(vgmstream); - return NULL; -} +#include "meta.h" +#include "../coding/coding.h" + + +/* MSV - from Sony MultiStream format [Fight Club (PS2), PoPcap Hits Vol. 1 (PS2)] */ +VGMSTREAM* init_vgmstream_msv(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + off_t start_offset; + size_t channel_size; + int loop_flag, channels; + + + if (!is_id32be(0x00,sf, "MSVp")) + goto fail; + + /* checks */ + /* .msv: actual extension + * .msvp: header ID */ + if (!check_extensions(sf,"msv,msvp")) + goto fail; + + channels = 1; + channel_size = read_u32be(0x0c,sf); + loop_flag = 0; /* no looping and last 16 bytes (end frame) are removed, from Sony's docs */ + start_offset = 0x30; + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channels, loop_flag); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_MSV; + vgmstream->sample_rate = read_u32be(0x10,sf); + vgmstream->num_samples = ps_bytes_to_samples(channel_size, 1); + + vgmstream->coding_type = coding_PSX; + vgmstream->layout_type = layout_none; + read_string(vgmstream->stream_name,0x10+1, 0x20,sf); + + if (!vgmstream_open_stream(vgmstream, sf, start_offset)) + goto fail; + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/msvp.c b/Frameworks/vgmstream/vgmstream/src/meta/msvp.c deleted file mode 100644 index 687344e89..000000000 --- a/Frameworks/vgmstream/vgmstream/src/meta/msvp.c +++ /dev/null @@ -1,70 +0,0 @@ -#include "meta.h" -#include "../util.h" - -/* MSVP (from PoPcap Hits Vol. 1) */ -VGMSTREAM * init_vgmstream_msvp(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; - off_t start_offset; - int loop_flag = 0; - int channel_count; - - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("msvp",filename_extension(filename))) goto fail; - - /* check header */ - if (read_32bitBE(0x00,streamFile) != 0x4D535670) /* "MSVp" */ - goto fail; - - loop_flag = 0; - channel_count = 1; - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); - if (!vgmstream) goto fail; - - /* fill in the vital statistics */ - start_offset = 0x30; - vgmstream->channels = channel_count; - vgmstream->sample_rate = read_32bitBE(0x10,streamFile); - vgmstream->coding_type = coding_PSX; - vgmstream->num_samples = (get_streamfile_size(streamFile)-start_offset)*28/16/channel_count; - if (loop_flag) { - vgmstream->loop_start_sample = loop_flag; - vgmstream->loop_end_sample = (read_32bitBE(0x0C,streamFile))*28/16/channel_count; - } - -/* Just to be sure that there comes a 2 channel file */ - if (channel_count == 1) { - vgmstream->layout_type = layout_none; - } else if (channel_count == 2) { - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = 0x10; /* Unknown for now */ - } - - vgmstream->meta_type = meta_MSVP; - - /* open the file for reading */ - { - int i; - STREAMFILE * file; - file = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); - if (!file) goto fail; - for (i=0;ich[i].streamfile = file; - - vgmstream->ch[i].channel_start_offset= - vgmstream->ch[i].offset=start_offset+ - vgmstream->interleave_block_size*i; - - } - } - - return vgmstream; - - /* clean up anything we may have opened */ -fail: - if (vgmstream) close_vgmstream(vgmstream); - return NULL; -} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ngc_adpdtk.c b/Frameworks/vgmstream/vgmstream/src/meta/ngc_adpdtk.c index 982cd45aa..d57b30a3e 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ngc_adpdtk.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ngc_adpdtk.c @@ -18,7 +18,7 @@ VGMSTREAM* init_vgmstream_dtk(STREAMFILE* sf) { /* check valid frames as files have no header, and .adp/wav are common */ { - int i; + int i, blanks = 0; for (i = 0; i < 10; i++) { /* try a bunch of frames */ /* header 0x00/01 are repeated in 0x02/03 (for error correction?), * could also test header values (upper nibble should be 0..3, and lower nibble 0..C) */ @@ -26,10 +26,13 @@ VGMSTREAM* init_vgmstream_dtk(STREAMFILE* sf) { read_u8(0x01 + i*0x20,sf) != read_u8(0x03 + i*0x20,sf)) goto fail; - /* frame headers for silent frames are 0x0C, never null */ - if (read_u8(0x00 + i*0x20,sf) == 0x00) - goto fail; + /* silent frame headers are almost always 0x0C, save a few uncommon tracks [Wave Race Blue Storm (GC), 1080 Silver Storm (GC)] */ + if (read_u16be(0x00 + i*0x20,sf) == 0x00) + blanks++; } + + if (blanks > 3) + goto fail; } /* DTK (Disc Track) are DVD hardware-decoded streams, always stereo and no loop. diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ngc_dsp_std.c b/Frameworks/vgmstream/vgmstream/src/meta/ngc_dsp_std.c index ddd50a397..3b5619281 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ngc_dsp_std.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ngc_dsp_std.c @@ -1364,7 +1364,7 @@ fail: } -/* WIIADPCM - Exient wrapper [Need for Speed: Hot Pursuit (Wii)] */ +/* WIIADPCM - Exient wrapper [Need for Speed: Hot Pursuit (Wii), Angry Birds: Star Wars (WiiU)] */ VGMSTREAM* init_vgmstream_dsp_wiiadpcm(STREAMFILE* sf) { dsp_meta dspm = {0}; @@ -1375,18 +1375,28 @@ VGMSTREAM* init_vgmstream_dsp_wiiadpcm(STREAMFILE* sf) { goto fail; dspm.interleave = read_u32be(0x08,sf); /* interleave offset */ - if (dspm.interleave) { - dspm.interleave -= 0x10; - } - /* 0x0c: 0 when RAM (2 DSP headers), interleave size when stream (2 WIIADPCM headers) */ + /* 0x0c: NFS = 0 when RAM (2 DSP headers), interleave size when stream (2 WIIADPCM headers) + * AB = 0 (2 WIIADPCM headers) */ dspm.channels = (dspm.interleave ? 2 : 1); dspm.max_channels = 2; - dspm.header_offset = 0x10; + if (read_u32be(0x10,sf) != 0) + dspm.header_offset = 0x10; /* NFSHP */ + else + dspm.header_offset = 0x20; /* ABSW */ + + if (dspm.interleave) + dspm.interleave -= dspm.header_offset; + dspm.interleave_first_skip = 0x60 + dspm.header_offset; + dspm.interleave_first = dspm.interleave - dspm.interleave_first_skip; + dspm.header_spacing = dspm.interleave; dspm.start_offset = dspm.header_offset + 0x60; + // +VGM_LOG("%lx, %x\n", dspm.header_offset, dspm.interleave); +VGM_LOG("%x, %x\n", dspm.interleave_first_skip, dspm.interleave_first); dspm.meta_type = meta_DSP_WIIADPCM; return init_vgmstream_dsp_common(sf, &dspm); fail: diff --git a/Frameworks/vgmstream/vgmstream/src/meta/riff.c b/Frameworks/vgmstream/vgmstream/src/meta/riff.c index 453c4b8fc..97fa44349 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/riff.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/riff.c @@ -103,21 +103,21 @@ static int read_fmt(int big_endian, STREAMFILE* sf, off_t offset, riff_fmt_chunk fmt->size = read_u32(offset+0x04,sf); /* WAVEFORMAT */ - fmt->codec = read_u16(offset+0x08,sf); - fmt->channels = read_u16(offset+0x0a,sf); - fmt->sample_rate = read_u32(offset+0x0c,sf); - //fmt->avg_bps = read_u32(offset+0x10,sf); - fmt->block_size = read_u16(offset+0x14,sf); - fmt->bps = read_u16(offset+0x16,sf); + fmt->codec = read_u16(offset+0x08+0x00,sf); + fmt->channels = read_u16(offset+0x08+0x02,sf); + fmt->sample_rate = read_u32(offset+0x08+0x04,sf); + //fmt->avg_bps = read_u32(offset+0x08+0x08,sf); + fmt->block_size = read_u16(offset+0x08+0x0c,sf); + fmt->bps = read_u16(offset+0x08+0x0e,sf); /* WAVEFORMATEX */ if (fmt->size >= 0x10) { - fmt->extra_size = read_u16(offset+0x18,sf); + fmt->extra_size = read_u16(offset+0x08+0x10,sf); /* 0x1a+ depends on codec (ex. coef table for MSADPCM, samples_per_frame in MS-IMA, etc) */ } /* WAVEFORMATEXTENSIBLE */ if (fmt->codec == 0xFFFE && fmt->extra_size >= 0x16) { - //fmt->extra_samples = read_u16(offset+0x1a,sf); /* valid_bits_per_sample or samples_per_block */ - fmt->channel_layout = read_u32(offset+0x1c,sf); + //fmt->extra_samples = read_u16(offset+0x08+0x12,sf); /* valid_bits_per_sample or samples_per_block */ + fmt->channel_layout = read_u32(offset+0x08+0x14,sf); /* 0x10 guid at 0x20 */ /* happens in various .at3/at9, may be a bug in their encoder b/c MS's defs set mono as FC */ @@ -135,7 +135,7 @@ static int read_fmt(int big_endian, STREAMFILE* sf, off_t offset, riff_fmt_chunk goto fail; switch (fmt->codec) { - case 0x00: /* Yamaha AICA ADPCM [Headhunter (DC), Bomber hehhe (DC), Rayman 2 (DC)] (unofficial) */ + case 0x0000: /* Yamaha AICA ADPCM [Headhunter (DC), Bomber hehhe (DC), Rayman 2 (DC)] (unofficial) */ if (fmt->bps != 4) goto fail; if (fmt->block_size != 0x02*fmt->channels && fmt->block_size != 0x01*fmt->channels) goto fail; @@ -143,7 +143,7 @@ static int read_fmt(int big_endian, STREAMFILE* sf, off_t offset, riff_fmt_chunk fmt->interleave = 0x01; break; - case 0x01: /* PCM */ + case 0x0001: /* PCM */ switch (fmt->bps) { case 24: /* Omori (PC) */ fmt->coding_type = coding_PCM24LE; @@ -160,7 +160,7 @@ static int read_fmt(int big_endian, STREAMFILE* sf, off_t offset, riff_fmt_chunk fmt->interleave = fmt->block_size / fmt->channels; break; - case 0x02: /* MSADPCM */ + case 0x0002: /* MSADPCM */ if (fmt->bps == 4) { fmt->coding_type = coding_MSADPCM; if (!msadpcm_check_coefs(sf, fmt->offset + 0x08 + 0x14)) @@ -174,19 +174,29 @@ static int read_fmt(int big_endian, STREAMFILE* sf, off_t offset, riff_fmt_chunk } break; - case 0x11: /* MS-IMA ADPCM [Layton Brothers: Mystery Room (iOS/Android)] */ + case 0x0011: /* MS-IMA ADPCM [Layton Brothers: Mystery Room (iOS/Android)] */ if (fmt->bps != 4) goto fail; fmt->coding_type = coding_MS_IMA; break; - case 0x20: /* Yamaha AICA ADPCM [Takuyo/Dynamix/etc DC games] */ + case 0x0020: /* Yamaha AICA ADPCM [Takuyo/Dynamix/etc DC games] (official-ish) */ if (fmt->bps != 4) goto fail; fmt->coding_type = coding_AICA; /* official RIFF spec has 0x20 as 'Yamaha ADPCM', but data is probably not pure AICA * (maybe with headered frames and would need extra detection?) */ break; - case 0x69: /* XBOX IMA ADPCM [Dynasty Warriors 5 (Xbox)] */ +#ifdef VGM_USE_MPEG + case 0x0055: /* MP3 [Bear in the Big Blue House: Bear's Imagine That! (PC)] (official) */ + fmt->coding_type = coding_MPEG_custom; + /* some oddities, unsure if part of standard: + * - block size is 1 (in mono) + * - bps is 16 + * - extra size 0x0c, has channels? and (possibly) approx frame size */ + break; +#endif + + case 0x0069: /* XBOX IMA ADPCM [Dynasty Warriors 5 (Xbox)] */ if (fmt->bps != 4) goto fail; fmt->coding_type = coding_XBOX_IMA; break; @@ -355,13 +365,14 @@ VGMSTREAM* init_vgmstream_riff(STREAMFILE* sf) { * .rws: Climax ATRAC3 [Silent Hill Origins (PSP), Oblivion (PSP)] * .aud: EA Replay ATRAC3 * .at9: standard ATRAC9 + * .ckd: renamed ATRAC9 [Rayman Origins (Vita)] * .saf: Whacked! (Xbox) * .mwv: Level-5 games [Dragon Quest VIII (PS2), Rogue Galaxy (PS2)] * .ima: Baja: Edge of Control (PS3/X360) * .nsa: Studio Ring games that uses NScripter [Hajimete no Otetsudai (PC)] * .pcm: Silent Hill Arcade (PC) */ - if ( check_extensions(sf, "wav,lwav,xwav,da,dax,cd,med,snd,adx,adp,xss,xsew,adpcm,adw,wd,,sbv,wvx,str,at3,rws,aud,at9,saf,ima,nsa,pcm") ) { + if ( check_extensions(sf, "wav,lwav,xwav,da,dax,cd,med,snd,adx,adp,xss,xsew,adpcm,adw,wd,,sbv,wvx,str,at3,rws,aud,at9,ckd,saf,ima,nsa,pcm") ) { ; } else if ( check_extensions(sf, "mwv") ) { @@ -590,6 +601,11 @@ VGMSTREAM* init_vgmstream_riff(STREAMFILE* sf) { JunkFound = 1; break; + + case 0x64737068: /* "dsph" */ + case 0x63776176: /* "cwav" */ + goto fail; /* parse elsewhere */ + default: /* ignorance is bliss */ break; @@ -625,6 +641,10 @@ VGMSTREAM* init_vgmstream_riff(STREAMFILE* sf) { read_u32be(start_offset+0x3c, sf) == 0xFFFFFFFF) goto fail; + ///* MSADPCM .ckd are parsed elsewhere, though they are valid so no big deal if parsed here (just that loops should be ignored) */ + if (!fmt.is_at9 && check_extensions(sf, "ckd")) + goto fail; + /* ignore Gitaroo Man Live! (PSP) multi-RIFF (to allow chunked TXTH) */ if (fmt.is_at3 && get_streamfile_size(sf) > 0x2800 && read_32bitBE(0x2800, sf) == 0x52494646) { /* "RIFF" */ goto fail; @@ -661,7 +681,11 @@ VGMSTREAM* init_vgmstream_riff(STREAMFILE* sf) { vgmstream->layout_type = layout_none; vgmstream->interleave_block_size = fmt.block_size; break; - +#ifdef VGM_USE_MPEG + case coding_MPEG_custom: + vgmstream->layout_type = layout_none; + break; +#endif case coding_MSADPCM: vgmstream->layout_type = layout_none; vgmstream->frame_size = fmt.block_size; @@ -804,6 +828,21 @@ VGMSTREAM* init_vgmstream_riff(STREAMFILE* sf) { } #endif +#ifdef VGM_USE_MPEG + case coding_MPEG_custom: { + mpeg_custom_config cfg = {0}; + + vgmstream->codec_data = init_mpeg_custom(sf, start_offset, &vgmstream->coding_type, fmt.channels, MPEG_STANDARD, &cfg); + if (!vgmstream->codec_data) goto fail; + + /* should provide "fact" but it's optional (some game files don't include it) */ + if (!fact_sample_count) + fact_sample_count = mpeg_get_samples(sf, start_offset, data_size); + vgmstream->num_samples = fact_sample_count; + } + break; +#endif + default: goto fail; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/rws.c b/Frameworks/vgmstream/vgmstream/src/meta/rws.c index ba67b565e..1c7082748 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/rws.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/rws.c @@ -1,17 +1,20 @@ #include "meta.h" #include "../coding/coding.h" #include "../layout/layout.h" +#include "../util/endianness.h" -static off_t get_rws_string_size(off_t offset, STREAMFILE *streamFile); +static off_t get_rws_string_size(off_t offset, STREAMFILE* sf); typedef struct { int big_endian; uint32_t codec; - int channel_count; + int channels; int sample_rate; + int interleave; + int frame_size; off_t file_name_offset; @@ -43,50 +46,52 @@ typedef struct { /* RWS - RenderWare Stream (from games using RenderWare Audio middleware) */ -VGMSTREAM * init_vgmstream_rws(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; +VGMSTREAM* init_vgmstream_rws(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; off_t start_offset, offset; size_t stream_size; int loop_flag; int i; - int total_subsongs, target_subsong = streamFile->stream_index; + int total_subsongs, target_subsong = sf->stream_index; rws_header rws = {0}; - int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL; + read_u32_t read_u32; + read_u16_t read_u16; + if (read_u32le(0x00,sf) != 0x0000080d) /* audio file id */ + goto fail; + rws.file_size = read_u32le(0x04, sf); /* audio file size */ + if (rws.file_size + 0x0c != get_streamfile_size(sf)) + goto fail; + /* checks */ - if (!check_extensions(streamFile,"rws")) + if (!check_extensions(sf,"rws")) goto fail; /* Audio .RWS is made of file + header + data chunks (non-audio .RWS with other chunks exist). - * Chunk format (LE): id, size, RW version, data of size (version is repeated but same for all chunks). + * Chunk format (LE): id, size, RW version, then data (version is repeated but same for all chunks). * Version is 16b main + 16b build (possibly shifted), no known differences between versions, * and can vary between files of a game. ex: 0c02, 1003, 1400 = 3.5, 1803 = 3.6, 1C02 = 3.7. */ /* parse audio chunks */ - if (read_32bitLE(0x00,streamFile) != 0x0000080d) /* audio file id */ + if (read_u32le(0x0c,sf) != 0x0000080e) /* header id */ goto fail; - rws.file_size = read_32bitLE(0x04, streamFile); /* audio file size */ - if (rws.file_size + 0x0c != get_streamfile_size(streamFile)) - goto fail; - - if (read_32bitLE(0x0c,streamFile) != 0x0000080e) /* header id */ - goto fail; - rws.header_size = read_32bitLE(0x10, streamFile); /* header size */ + rws.header_size = read_u32le(0x10, sf); /* header size */ rws.data_offset = 0x0c + 0x0c + rws.header_size; /* usually 0x800 but not always */ - if (read_32bitLE(rws.data_offset + 0x00, streamFile) != 0x0000080f) /* data chunk id */ + if (read_u32le(rws.data_offset + 0x00, sf) != 0x0000080f) /* data chunk id */ goto fail; - rws.data_size = read_32bitLE(rws.data_offset + 0x04, streamFile); /* data chunk size */ - if (rws.data_size+0x0c + rws.data_offset != get_streamfile_size(streamFile)) + rws.data_size = read_u32le(rws.data_offset + 0x04, sf); /* data chunk size */ + if (rws.data_size+0x0c + rws.data_offset != get_streamfile_size(sf)) goto fail; /* inside header chunk (many unknown fields are probably IDs/config/garbage, * as two files of the same size vary a lot) */ offset = 0x0c + 0x0c; - rws.big_endian = guess_endianness32bit(offset + 0x00, streamFile); /* GC/Wii/X360 */ - read_32bit = rws.big_endian ? read_32bitBE : read_32bitLE; + rws.big_endian = guess_endian32(offset + 0x00, sf); /* GC/Wii/X360 */ + read_u32 = rws.big_endian ? read_u32be : read_u32le; + read_u16 = rws.big_endian ? read_u16be : read_u16le; /* base header */ { @@ -94,9 +99,9 @@ VGMSTREAM * init_vgmstream_rws(STREAMFILE *streamFile) { /* 0x04/08/10: sizes of various sections? */ /* 0x14/18: config? */ /* 0x1c: null? */ - rws.total_segments = read_32bit(offset + 0x20, streamFile); + rws.total_segments = read_u32(offset + 0x20, sf); /* 0x24: config? */ - rws.total_layers = read_32bit(offset + 0x28, streamFile); + rws.total_layers = read_u32(offset + 0x28, sf); /* 0x2c: config? */ /* 0x30: 0x800? */ /* 0x34: block_layers_size? */ @@ -109,7 +114,7 @@ VGMSTREAM * init_vgmstream_rws(STREAMFILE *streamFile) { /* audio file name */ { rws.file_name_offset = offset; - offset += get_rws_string_size(offset, streamFile); + offset += get_rws_string_size(offset, sf); } /* RWS data can be divided in two ways: @@ -144,15 +149,15 @@ VGMSTREAM * init_vgmstream_rws(STREAMFILE *streamFile) { if (i+1 == rws.target_segment) { /* 0x00/04/0c: config? */ /* others: ? */ - rws.segment_layers_size = read_32bit(offset + 0x18, streamFile); /* sum of all including padding */ - rws.segment_offset = read_32bit(offset + 0x1c, streamFile); + rws.segment_layers_size = read_u32(offset + 0x18, sf); /* sum of all including padding */ + rws.segment_offset = read_u32(offset + 0x1c, sf); } offset += 0x20; } /* usable layer sizes per segment */ for (i = 0; i < (rws.total_segments * rws.total_layers); i++) { - size_t usable_size = read_32bit(offset, streamFile); /* without padding */ + size_t usable_size = read_u32(offset, sf); /* without padding */ /* size order: segment1 layer1 size, ..., segment1 layerN size, segment2 layer1 size, etc */ if (i+1 == target_subsong) { /* order matches our subsong order */ rws.usable_size = usable_size; @@ -170,7 +175,7 @@ VGMSTREAM * init_vgmstream_rws(STREAMFILE *streamFile) { if (i+1 == rws.target_segment) { rws.segment_name_offset = offset; } - offset += get_rws_string_size(offset, streamFile); + offset += get_rws_string_size(offset, sf); } /* layer info */ @@ -179,13 +184,15 @@ VGMSTREAM * init_vgmstream_rws(STREAMFILE *streamFile) { /* 0x00/04: config? */ /* 0x08: null? */ /* 0x0c: related to samples per frame? (XBOX-IMA=07, PSX=1C, DSP=0E, PCM=01) */ - //block_size_pad = read_32bit(offset + 0x10, streamFile); /* with padding, can be different per layer */ - /* 0x14/18: ? */ + //block_size_pad = read_u32(offset + 0x10, sf); /* with padding, can be different per layer */ + /* 0x14: config? */ + rws.interleave = read_u16(offset + 0x18, sf); /* wrong values in Burnout 2 Xbox, otherwise correct */ + rws.frame_size = read_u16(offset + 0x1a, sf); /* same */ /* 0x1c: codec related? */ - rws.block_size = read_32bit(offset + 0x20, streamFile); /* without padding */ - rws.layer_start = read_32bit(offset + 0x24, streamFile); /* skip data */ + rws.block_size = read_u32(offset + 0x20, sf); /* without padding */ + rws.layer_start = read_u32(offset + 0x24, sf); /* skip data */ } - rws.block_layers_size += read_32bit(offset + 0x10, streamFile); /* needed to skip during decode */ + rws.block_layers_size += read_u32(offset + 0x10, sf); /* needed to skip during decode */ offset += 0x28; } @@ -193,15 +200,17 @@ VGMSTREAM * init_vgmstream_rws(STREAMFILE *streamFile) { for (i = 0; i < rws.total_layers; i++) { uint32_t layer_codec = 0; if (i+1 == rws.target_layer) { - rws.sample_rate = read_32bit(offset + 0x00, streamFile); + rws.sample_rate = read_u32(offset + 0x00, sf); /* 0x04: config? */ - //rws.layer_size = read_32bit(offset + 0x08, streamFile); /* same or close to usable size */ + //rws.layer_size = read_u32(offset + 0x08, sf); /* same or close to usable size */ /* 0x0c: bits per sample */ - rws.channel_count = read_8bit(offset + 0x0d, streamFile); - /* others: ? */ - rws.codec = (uint32_t)read_32bit(offset + 0x1c, streamFile); /* 128b uuid (32b-16b-16b-8b*8) but first 32b is enough */ + rws.channels = read_u8(offset + 0x0d, sf); + /* 0x0e: null or some value ? */ + /* 0x10/0x14: null? */ + /* 0x18: null or some size? */ + rws.codec = read_u32(offset + 0x1c, sf); /* 128b uuid (32b-16b-16b-8b*8) but first 32b is enough */ } - layer_codec = (uint32_t)read_32bit(offset + 0x1c, streamFile); + layer_codec = read_u32(offset + 0x1c, sf); offset += 0x2c; /* DSP has an extra field per layer */ @@ -226,7 +235,7 @@ VGMSTREAM * init_vgmstream_rws(STREAMFILE *streamFile) { if (i+1 == rws.target_layer) { rws.layer_name_offset = offset; } - offset += get_rws_string_size(offset, streamFile); + offset += get_rws_string_size(offset, sf); } /* rest is padding/garbage until chunk end (may contain strings and uninitialized memory) */ @@ -249,11 +258,11 @@ VGMSTREAM * init_vgmstream_rws(STREAMFILE *streamFile) { { char base_name[STREAM_NAME_SIZE], file_name[STREAM_NAME_SIZE], segment_name[STREAM_NAME_SIZE], layer_name[STREAM_NAME_SIZE]; - get_streamfile_basename(streamFile, base_name, sizeof(base_name)); + get_streamfile_basename(sf, base_name, sizeof(base_name)); /* null terminated */ - read_string(file_name,STREAM_NAME_SIZE, rws.file_name_offset, streamFile); - read_string(segment_name,STREAM_NAME_SIZE, rws.segment_name_offset, streamFile); - read_string(layer_name,STREAM_NAME_SIZE, rws.layer_name_offset, streamFile); + read_string(file_name,STREAM_NAME_SIZE, rws.file_name_offset, sf); + read_string(segment_name,STREAM_NAME_SIZE, rws.segment_name_offset, sf); + read_string(layer_name,STREAM_NAME_SIZE, rws.layer_name_offset, sf); /* some internal names aren't very interesting and are stuff like "SubStream" */ if (strcmp(base_name, file_name) == 0) { @@ -275,7 +284,7 @@ VGMSTREAM * init_vgmstream_rws(STREAMFILE *streamFile) { /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(rws.channel_count,loop_flag); + vgmstream = allocate_vgmstream(rws.channels, loop_flag); if (!vgmstream) goto fail; vgmstream->meta_type = meta_RWS; @@ -289,41 +298,47 @@ VGMSTREAM * init_vgmstream_rws(STREAMFILE *streamFile) { vgmstream->full_block_size = rws.block_layers_size; switch(rws.codec) { - case 0xD01BD217: /* {D01BD217,3587,4EED,B9,D9,B8,E8,6E,A9,B9,95} PCM PC/X360 */ - /* ex. D.i.R.T.: Origin of the Species (PC), The Legend of Spyro (X360) */ - vgmstream->coding_type = coding_PCM16_int; - vgmstream->codec_endian = (rws.big_endian); - vgmstream->interleave_block_size = 0x02; /* only to setup channels */ + case 0xD01BD217: /* {D01BD217,3587,4EED,B9,D9,B8,E8,6E,A9,B9,95} PCM PC/X360/PS2 */ + /* D.i.R.T.: Origin of the Species (PC), The Legend of Spyro (X360), kill.switch (PS2) */ + if (rws.interleave == 0x02) { /* PC, X360 */ + vgmstream->coding_type = coding_PCM16_int; + vgmstream->codec_endian = (rws.big_endian); + vgmstream->interleave_block_size = rws.interleave; /* only to setup channels */ + } + else { /* PS2 */ + vgmstream->coding_type = rws.big_endian ? coding_PCM16BE : coding_PCM16LE; + vgmstream->interleave_block_size = rws.interleave; /* only to setup channels */ + } - vgmstream->num_samples = pcm_bytes_to_samples(stream_size, rws.channel_count, 16); + vgmstream->num_samples = pcm16_bytes_to_samples(stream_size, rws.channels); break; case 0xD9EA9798: /* {D9EA9798,BBBC,447B,96,B2,65,47,59,10,2E,16} PS-ADPCM PS2 */ - /* ex. Silent Hill Origins (PS2), Ghost Rider (PS2), Max Payne 2 (PS2), Nana (PS2) */ + /* Silent Hill Origins (PS2), Ghost Rider (PS2), Max Payne 2 (PS2), Nana (PS2) */ vgmstream->coding_type = coding_PSX; vgmstream->interleave_block_size = rws.block_size / 2; - vgmstream->num_samples = ps_bytes_to_samples(stream_size, rws.channel_count); + vgmstream->num_samples = ps_bytes_to_samples(stream_size, rws.channels); break; case 0xF86215B0: /* {F86215B0,31D5,4C29,BD,37,CD,BF,9B,D1,0C,53} DSP GC/Wii */ - /* ex. Burnout 2 (GC), Alice in Wonderland (Wii) */ + /* Burnout 2 (GC), Alice in Wonderland (Wii) */ vgmstream->coding_type = coding_NGC_DSP; vgmstream->interleave_block_size = rws.block_size / 2; /* get coefs (all channels share them; also seem fixed for all RWS) */ - dsp_read_coefs_be(vgmstream, streamFile, rws.coefs_offset, 0); + dsp_read_coefs_be(vgmstream, sf, rws.coefs_offset, 0); - vgmstream->num_samples = dsp_bytes_to_samples(stream_size, rws.channel_count); + vgmstream->num_samples = dsp_bytes_to_samples(stream_size, rws.channels); break; case 0xEF386593: /* {EF386593,B611,432D,95,7F,A7,1A,DE,44,22,7A} XBOX-IMA PC */ case 0x632FA22B: /* {632FA22B,11DD,458F,AA,27,A5,C3,46,E9,79,0E} XBOX-IMA Xbox */ - /* ex. Broken Sword 3 (PC), Jacked (PC/Xbox), Burnout 2 (Xbox) */ + /* Broken Sword 3 (PC), Jacked (PC/Xbox), Burnout 2 (Xbox) */ vgmstream->coding_type = coding_XBOX_IMA; /* same data though different uuid */ vgmstream->interleave_block_size = 0; - vgmstream->num_samples = xbox_ima_bytes_to_samples(stream_size, rws.channel_count); + vgmstream->num_samples = xbox_ima_bytes_to_samples(stream_size, rws.channels); break; default: @@ -332,7 +347,7 @@ VGMSTREAM * init_vgmstream_rws(STREAMFILE *streamFile) { } - if (!vgmstream_open_stream(vgmstream, streamFile, start_offset)) + if (!vgmstream_open_stream(vgmstream, sf, start_offset)) goto fail; return vgmstream; @@ -343,10 +358,10 @@ fail: /* rws-strings are null-terminated then padded to 0x10 (weirdly enough the padding contains garbage) */ -static off_t get_rws_string_size(off_t offset, STREAMFILE *streamFile) { +static off_t get_rws_string_size(off_t offset, STREAMFILE* sf) { int i; for (i = 0; i < 255; i++) { /* arbitrary max */ - if (read_8bit(offset+i, streamFile) == 0) { /* null terminator */ + if (read_u8(offset+i, sf) == 0) { /* null terminator */ return i + (0x10 - (i % 0x10)); /* size is padded */ } } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/rxws.c b/Frameworks/vgmstream/vgmstream/src/meta/rxws.c index fd7ac5abb..6294b5086 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/rxws.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/rxws.c @@ -2,54 +2,56 @@ #include "../util.h" #include "../coding/coding.h" -/* RXWS - from Sony SCEI PS2 games (Okage: Shadow King, Genji, Bokura no Kazoku) */ +/* RXWS - from Sony SCEI games [Okage: Shadow King (PS2), Genji (PS2), Bokura no Kazoku (PS2))] */ VGMSTREAM* init_vgmstream_rxws(STREAMFILE* sf) { VGMSTREAM* vgmstream = NULL; - STREAMFILE* sh = NULL; + STREAMFILE* sf_head = NULL; + STREAMFILE* sf_body = NULL; off_t start_offset, chunk_offset, name_offset = 0; size_t stream_size, chunk_size; - int loop_flag = 0, channels, is_separate = 0, type, sample_rate; + int loop_flag = 0, channels, is_xwh = 0, type, sample_rate; int32_t num_samples, loop_start; int total_subsongs, target_subsong = sf->stream_index; + + /* for plugins that start with .xwb */ + if (check_extensions(sf,"xwb")) { + /* extra check to reject Microsoft's XWB faster */ + if (is_id32be(0x00,sf,"WBND") || is_id32be(0x00,sf,"DNBW")) /* LE/BE */ + goto fail; + + sf_head = open_streamfile_by_ext(sf, "xwh"); + if (!sf_head) goto fail; + } + else { + sf_head = sf; + } + + if (!is_id32be(0x00,sf_head,"RXWS")) + goto fail; + /* checks */ /* .xws: header and data * .xwh+xwb: header + data (.bin+dat are also found in Wild Arms 4/5) */ if (!check_extensions(sf,"xws,xwb")) goto fail; - is_separate = check_extensions(sf,"xwb"); - - /* xwh+xwb: use xwh as header; otherwise use the current file */ - if (is_separate) { - /* extra check to reject Microsoft's XWB faster */ - if (is_id32be(0x00,sf,"WBND") || /* (LE) */ - is_id32be(0x00,sf,"DNBW")) /* (BE) */ - goto fail; - - sh = open_streamfile_by_ext(sf, "xwh"); - if (!sh) goto fail; - } else { - sh = sf; - } - if (!is_id32be(0x00,sh,"RXWS")) - goto fail; /* file size (just the .xwh/xws) */ - if (read_u32le(0x04,sh) + 0x10 != get_streamfile_size(sh)) + if (read_u32le(0x04,sf_head) + 0x10 != get_streamfile_size(sf_head)) goto fail; /* 0x08: version (0x100/0x200) * 0x0C: null */ /* typical chunks: FORM, FTXT, MARK, BODY (for .xws) */ - if (!is_id32be(0x10,sh,"FORM")) /* main header (always first) */ + if (!is_id32be(0x10,sf_head,"FORM")) /* main header (always first) */ goto fail; - chunk_size = read_u32le(0x10+0x04,sh); /* size - 0x10 */ + chunk_size = read_u32le(0x10+0x04,sf_head); /* size - 0x10 */ /* 0x08 version (0x100), 0x0c: null */ chunk_offset = 0x20; /* check multi-streams */ - total_subsongs = read_s32le(chunk_offset+0x00,sh); + total_subsongs = read_s32le(chunk_offset+0x00,sf_head); if (target_subsong == 0) target_subsong = 1; if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail; @@ -57,60 +59,72 @@ VGMSTREAM* init_vgmstream_rxws(STREAMFILE* sf) { /* read stream header */ { off_t header_offset = chunk_offset + 0x4 + 0x1c * (target_subsong-1); /* position in FORM */ - off_t stream_offset, next_stream_offset, data_offset = 0; + off_t stream_offset, next_stream_offset, body_offset; - type = read_u8(header_offset+0x00, sh); + type = read_u8(header_offset+0x00, sf_head); /* 0x01: unknown (always 0x1c) */ /* 0x02: flags? (usually 8002/0002, & 0x01 if looped) */ /* 0x04: vol/pan stuff? (0x00007F7F) */ /* 0x08: null? */ - channels = read_u8(header_offset+0x09, sh); + channels = read_u8(header_offset+0x09, sf_head); + sample_rate = read_u16le(header_offset+0x0a,sf_head); /* 0x0c: null? */ - sample_rate = read_u16le(header_offset+0x0a,sh); - stream_offset = read_u32le(header_offset+0x10,sh); - num_samples = read_s32le(header_offset+0x14,sh); - loop_start = read_s32le(header_offset+0x18,sh); + stream_offset = read_u32le(header_offset+0x10,sf_head); + num_samples = read_s32le(header_offset+0x14,sf_head); + loop_start = read_s32le(header_offset+0x18,sf_head); loop_flag = (loop_start >= 0); - /* find data start and size */ - if (is_separate) { - data_offset = 0x00; - } - else { - off_t current_chunk = 0x10; - /* note the extra 0x10 in chunk_size/offsets */ - while (current_chunk < get_streamfile_size(sf)) { - if (is_id32be(current_chunk,sf, "BODY")) { - data_offset = 0x10 + current_chunk; + /* find body start and size (needed for stream_size) */ + { + uint32_t current_chunk = 0x10; + + body_offset = 0x00; + while (current_chunk < get_streamfile_size(sf_head)) { + if (is_id32be(current_chunk,sf_head, "BODY")) { + body_offset = 0x10 + current_chunk; + is_xwh = 1; break; } - current_chunk += 0x10 + read_u32le(current_chunk+4,sf); + /* note the extra 0x10 in chunk_size/offsets */ + current_chunk += 0x10 + read_u32le(current_chunk + 0x04,sf_head); + } + + /* .xwh and .xws are only different in that the latter has BODY chunk (no flags/sizes) */ + is_xwh = !body_offset; + + /* for plugins that start with .xwh (and don't check extensions) */ + if (is_xwh && sf == sf_head) { + sf_body = open_streamfile_by_ext(sf, "xwb"); + if (!sf_body) goto fail; + } + else { + sf_body = sf; } - if (!data_offset) goto fail; } if (target_subsong == total_subsongs) { - next_stream_offset = get_streamfile_size(is_separate ? sf : sh) - data_offset; + uint32_t max_size = get_streamfile_size(sf_body); + next_stream_offset = max_size - body_offset; } else { off_t next_header_offset = chunk_offset + 0x4 + 0x1c * (target_subsong); - next_stream_offset = read_u32le(next_header_offset+0x10,sh); + next_stream_offset = read_u32le(next_header_offset+0x10, sf_head); } stream_size = next_stream_offset - stream_offset; - start_offset = data_offset + stream_offset; + start_offset = body_offset + stream_offset; } /* get stream name (always follows FORM) */ - if (is_id32be(0x10+0x10 + chunk_size,sh, "FTXT")) { + if (is_id32be(0x10+0x10 + chunk_size,sf_head, "FTXT")) { chunk_offset = 0x10+0x10 + chunk_size + 0x10; - if (read_s32le(chunk_offset+0x00,sh) == total_subsongs) { - name_offset = chunk_offset + read_u32le(chunk_offset+0x04 + (target_subsong-1)*0x04,sh); + if (read_s32le(chunk_offset+0x00,sf_head) == total_subsongs) { + name_offset = chunk_offset + read_u32le(chunk_offset+0x04 + (target_subsong-1)*0x04,sf_head); } } /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channels,loop_flag); + vgmstream = allocate_vgmstream(channels, loop_flag); if (!vgmstream) goto fail; vgmstream->meta_type = meta_RXWS; @@ -118,7 +132,7 @@ VGMSTREAM* init_vgmstream_rxws(STREAMFILE* sf) { vgmstream->num_streams = total_subsongs; vgmstream->stream_size = stream_size; if (name_offset) - read_string(vgmstream->stream_name,STREAM_NAME_SIZE, name_offset,sh); + read_string(vgmstream->stream_name,STREAM_NAME_SIZE, name_offset, sf_head); switch (type) { case 0x00: /* PS-ADPCM */ @@ -149,7 +163,7 @@ VGMSTREAM* init_vgmstream_rxws(STREAMFILE* sf) { encoder_delay = 1024 + 69*2; /* observed default */ vgmstream->num_samples = num_samples - encoder_delay; - vgmstream->codec_data = init_ffmpeg_atrac3_raw(sf, start_offset,stream_size, vgmstream->num_samples,vgmstream->channels,vgmstream->sample_rate, block_align, encoder_delay); + vgmstream->codec_data = init_ffmpeg_atrac3_raw(sf_body, start_offset,stream_size, vgmstream->num_samples,vgmstream->channels,vgmstream->sample_rate, block_align, encoder_delay); if (!vgmstream->codec_data) goto fail; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; @@ -164,14 +178,16 @@ VGMSTREAM* init_vgmstream_rxws(STREAMFILE* sf) { } /* open the file for reading */ - if (!vgmstream_open_stream(vgmstream, sf, start_offset)) + if (!vgmstream_open_stream(vgmstream, sf_body, start_offset)) goto fail; - if (is_separate && sh) close_streamfile(sh); + if (sf != sf_head) close_streamfile(sf_head); + if (sf != sf_body) close_streamfile(sf_body); return vgmstream; fail: - if (is_separate && sh) close_streamfile(sh); + if (sf != sf_head) close_streamfile(sf_head); + if (sf != sf_body) close_streamfile(sf_body); close_vgmstream(vgmstream); return NULL; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/sgxd.c b/Frameworks/vgmstream/vgmstream/src/meta/sgxd.c index 91af9aec7..0a3122c44 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/sgxd.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/sgxd.c @@ -6,93 +6,123 @@ VGMSTREAM* init_vgmstream_sgxd(STREAMFILE* sf) { VGMSTREAM* vgmstream = NULL; STREAMFILE* sf_head = NULL; + STREAMFILE* sf_body = NULL; off_t start_offset, data_offset, chunk_offset, name_offset = 0; size_t stream_size; + uint32_t base1_offset, base2_offset, base3_offset; - int is_sgx, is_sgb = 0; - int loop_flag, channels, codec; - int sample_rate, num_samples, loop_start_sample, loop_end_sample; + int is_sgx, is_sgd = 0; + int loop_flag, channels, codec, sample_rate; + int32_t num_samples, loop_start_sample, loop_end_sample; int total_subsongs, target_subsong = sf->stream_index; - /* check extension, case insensitive */ - /* .sgx: header+data (Genji), .sgd: header+data, .sgh/sgd: header/data */ - if (!check_extensions(sf,"sgx,sgd,sgb")) - goto fail; - is_sgx = check_extensions(sf,"sgx"); - is_sgb = check_extensions(sf,"sgb"); - - /* SGB+SGH: use SGH as header; otherwise use the current file as header */ - if (is_sgb) { + /* for plugins that start with .sgb */ + if (check_extensions(sf,"sgb")) { sf_head = open_streamfile_by_ext(sf, "sgh"); if (!sf_head) goto fail; - } else { + } + else { sf_head = sf; } - - /* SGXD base (size 0x10) */ - if (read_32bitBE(0x00,sf_head) != 0x53475844) /* "SGXD" */ + if (!is_id32be(0x00,sf_head, "SGXD")) goto fail; - /* 0x04 SGX: full header_size; SGD/SGH: unknown header_size (counting from 0x0/0x8/0x10, varies) */ - /* 0x08 SGX: first chunk offset? (0x10); SGD/SGH: full header_size */ - /* 0x0c SGX/SGH: full data size with padding; SGD: full data size + 0x80000000 with padding */ - if (is_sgb) { - data_offset = 0x00; - } else if ( is_sgx ) { - data_offset = read_32bitLE(0x04,sf_head); + + /* checks */ + /* .sgx: header+data (Genji) + * .sgd: header+data (common) + * .sgh+sgd: header+data */ + if (!check_extensions(sf,"sgx,sgd,sgb")) + goto fail; + + /* SGXD base (size 0x10), always LE even on PS3 */ + /* 0x04: SGX = full header size + SGD/SGH = bank name offset (part of NAME table, usually same as filename) */ + /* 0x08: SGX = first chunk offset? (0x10) + SGD/SGH = full header size */ + /* 0x0c: SGX/SGH = full data size with padding / + SGD = full data size ^ (1<<31) with padding */ + base1_offset = read_u32le(0x04, sf_head); + base2_offset = read_u32le(0x08, sf_head); + base3_offset = read_u32le(0x0c, sf_head); + + is_sgx = base2_offset == 0x10; /* fixed size */ + is_sgd = base3_offset & (1 << 31); /* flag */ + + /* Ogg SGXD don't have flag (probably due to codec hijack, or should be split), allow since it's not so obvious */ + if (!(is_sgx || is_sgd) && get_streamfile_size(sf_head) != base2_offset) /* sgh but wrong header size must be sgd */ + is_sgd = 1; + + /* for plugins that start with .sgh (and don't check extensions) */ + if (!(is_sgx || is_sgd) && sf == sf_head) { + sf_body = open_streamfile_by_ext(sf, "sgb"); + if (!sf_body) goto fail; + } + else { + sf_body = sf; + } + + + if (is_sgx) { + data_offset = base1_offset; + } else if (is_sgd) { + data_offset = base2_offset; } else { - data_offset = read_32bitLE(0x08,sf_head); + data_offset = 0x00; } /* typical chunks: WAVE, RGND, NAME (strings for WAVE or RGND), SEQD (related to SFX), WSUR, WMKR, BUSS */ /* WAVE chunk (size 0x10 + files * 0x38 + optional padding) */ if (is_sgx) { /* position after chunk+size */ - if (read_32bitBE(0x10,sf_head) != 0x57415645) goto fail; /* "WAVE" */ + if (!is_id32be(0x10,sf_head, "WAVE")) + goto fail; chunk_offset = 0x18; } else { - if (!find_chunk_le(sf_head, 0x57415645,0x10,0, &chunk_offset,NULL)) goto fail; /* "WAVE" */ + if (!find_chunk_le(sf_head, get_id32be("WAVE"),0x10,0, &chunk_offset, NULL)) + goto fail; } /* 0x04 SGX: unknown; SGD/SGH: chunk length, 0x08 null */ /* check multi-streams (usually only SE containers; Puppeteer) */ - total_subsongs = read_32bitLE(chunk_offset+0x04,sf_head); + total_subsongs = read_s32le(chunk_offset+0x04,sf_head); if (target_subsong == 0) target_subsong = 1; if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail; /* read stream header */ { - off_t stream_offset; + uint32_t stream_offset; chunk_offset += 0x08 + 0x38 * (target_subsong-1); /* position in target header*/ /* 0x00 ? (00/01/02) */ if (!is_sgx) /* meaning unknown in .sgx; offset 0 = not a stream (a RGND sample) */ - name_offset = read_32bitLE(chunk_offset+0x04,sf_head); - codec = read_8bit(chunk_offset+0x08,sf_head); - channels = read_8bit(chunk_offset+0x09,sf_head); + name_offset = read_u32le(chunk_offset+0x04,sf_head); + codec = read_u8(chunk_offset+0x08,sf_head); + channels = read_u8(chunk_offset+0x09,sf_head); /* 0x0a null */ - sample_rate = read_32bitLE(chunk_offset+0x0c,sf_head); + sample_rate = read_s32le(chunk_offset+0x0c,sf_head); - /* 0x10 info_type: meaning of the next value - * (00=null, 30/40=data size without padding (ADPCM, ATRAC3plus), 80/A0=block size (AC3) */ - /* 0x14 info_value (see above) */ - /* 0x18 unknown (ex. 0x0008/0010/3307/CC02/etc)x2 */ - /* 0x1c null */ + /* 0x10: info_type, meaning of the next value + * (00=null, 30/40=data size without padding (ADPCM, ATRAC3plus), 80/A0=block size (AC3) */ + /* 0x14: info_value (see above) */ + /* 0x18: unknown (ex. 0x0008/0010/3307/CC02/etc)x2 */ + /* 0x1c: null */ - num_samples = read_32bitLE(chunk_offset+0x20,sf_head); - loop_start_sample = read_32bitLE(chunk_offset+0x24,sf_head); - loop_end_sample = read_32bitLE(chunk_offset+0x28,sf_head); - stream_size = read_32bitLE(chunk_offset+0x2c,sf_head); /* stream size (without padding) / interleave (for type3) */ + num_samples = read_s32le(chunk_offset+0x20,sf_head); + loop_start_sample = read_s32le(chunk_offset+0x24,sf_head); + loop_end_sample = read_s32le(chunk_offset+0x28,sf_head); + stream_size = read_u32le(chunk_offset+0x2c,sf_head); /* stream size (without padding) / interleave (for type3) */ if (is_sgx) { stream_offset = 0x0; } else{ - stream_offset = read_32bitLE(chunk_offset+0x30,sf_head); + stream_offset = read_u32le(chunk_offset+0x30,sf_head); } - /* 0x34 SGX: unknown; SGD/SGH: stream size (with padding) / interleave */ + /* 0x34: SGX = unknown + * SGD/SGH = stream size (with padding) / interleave */ - loop_flag = loop_start_sample!=0xffffffff && loop_end_sample!=0xffffffff; + loop_flag = loop_start_sample != -1 && loop_end_sample != -1; start_offset = data_offset + stream_offset; } @@ -121,7 +151,7 @@ VGMSTREAM* init_vgmstream_sgxd(STREAMFILE* sf) { #ifdef VGM_USE_VORBIS case 0x02: /* Ogg Vorbis [Ni no Kuni: Wrath of the White Witch Remastered (PC)] (codec hijack?) */ - vgmstream->codec_data = init_ogg_vorbis(sf, start_offset, stream_size, NULL); + vgmstream->codec_data = init_ogg_vorbis(sf_body, start_offset, stream_size, NULL); if (!vgmstream->codec_data) goto fail; vgmstream->coding_type = coding_OGG_VORBIS; vgmstream->layout_type = layout_none; @@ -130,7 +160,7 @@ VGMSTREAM* init_vgmstream_sgxd(STREAMFILE* sf) { case 0x03: /* PS-ADPCM [Genji (PS3), Ape Escape Move (PS3)]*/ vgmstream->coding_type = coding_PSX; vgmstream->layout_type = layout_interleave; - if (is_sgx || is_sgb) { + if (!is_sgd) { vgmstream->interleave_block_size = 0x10; } else { /* this only seems to happen with SFX */ vgmstream->interleave_block_size = stream_size; @@ -143,7 +173,7 @@ VGMSTREAM* init_vgmstream_sgxd(STREAMFILE* sf) { #ifdef VGM_USE_FFMPEG case 0x04: { /* ATRAC3plus [Kurohyo 1/2 (PSP), BraveStory (PSP)] */ - vgmstream->codec_data = init_ffmpeg_atrac3_riff(sf, start_offset, NULL); + vgmstream->codec_data = init_ffmpeg_atrac3_riff(sf_body, start_offset, NULL); if (!vgmstream->codec_data) goto fail; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; @@ -163,7 +193,7 @@ VGMSTREAM* init_vgmstream_sgxd(STREAMFILE* sf) { #ifdef VGM_USE_FFMPEG case 0x06: { /* AC3 [Tokyo Jungle (PS3), Afrika (PS3)] */ - vgmstream->codec_data = init_ffmpeg_offset(sf, start_offset, stream_size); + vgmstream->codec_data = init_ffmpeg_offset(sf_body, start_offset, stream_size); if (!vgmstream->codec_data) goto fail; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; @@ -173,7 +203,6 @@ VGMSTREAM* init_vgmstream_sgxd(STREAMFILE* sf) { ffmpeg_set_skip_samples(vgmstream->codec_data, 256); /* SGXD loop/sample values are relative (without skip samples), no need to adjust */ - break; } #endif @@ -183,14 +212,16 @@ VGMSTREAM* init_vgmstream_sgxd(STREAMFILE* sf) { goto fail; } - if (!vgmstream_open_stream(vgmstream, sf, start_offset)) + if (!vgmstream_open_stream(vgmstream, sf_body, start_offset)) goto fail; - if (is_sgb && sf_head) close_streamfile(sf_head); + if (sf != sf_head) close_streamfile(sf_head); + if (sf != sf_body) close_streamfile(sf_body); return vgmstream; fail: - if (is_sgb && sf_head) close_streamfile(sf_head); + if (sf != sf_head) close_streamfile(sf_head); + if (sf != sf_body) close_streamfile(sf_body); close_vgmstream(vgmstream); return NULL; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/sli.c b/Frameworks/vgmstream/vgmstream/src/meta/sli.c index 41554ed1a..b4a994c05 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/sli.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/sli.c @@ -1,116 +1,110 @@ -#include "meta.h" -#include - - -/* .sli+ogg/opus - KiriKiri engine / WaveLoopManager loop points loader [Fate/Stay Night (PC), World End Economica (PC)] */ -VGMSTREAM * init_vgmstream_sli_ogg(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - STREAMFILE * streamData = NULL; - int32_t loop_start = -1, loop_length = -1; - int32_t loop_from = -1, loop_to = -1; - - /* checks */ - if (!check_extensions(streamFile, "sli")) - goto fail; - - { - /* try with file.ogg/opus.sli=header and file.ogg/opus=data */ - char basename[PATH_LIMIT]; - get_streamfile_basename(streamFile,basename,PATH_LIMIT); - streamData = open_streamfile_by_filename(streamFile, basename); - if (!streamData) goto fail; - } - - - /* let the real initer do the parsing */ - if (check_extensions(streamData, "ogg")) { /* Fate/Stay Night (PC) */ -#ifdef VGM_USE_VORBIS - vgmstream = init_vgmstream_ogg_vorbis(streamData); - if (!vgmstream) goto fail; - - vgmstream->meta_type = meta_OGG_SLI; -#else - goto fail; -#endif - } - else if (check_extensions(streamData, "opus")) { /* Sabbat of the Witch (PC) */ -#ifdef VGM_USE_FFMPEG - vgmstream = init_vgmstream_ffmpeg(streamData); - if (!vgmstream) goto fail; - - /* FFmpeg's Opus encoder delay is borked but no need to fix: - * somehow sli+opus use 0 in the OpusHead (to simplify looping?) */ - - vgmstream->meta_type = meta_OPUS_SLI; -#else - goto fail; -#endif - } - else { - goto fail; - } - - - /* find loop text */ - { - char line[PATH_LIMIT]; - size_t bytes_read; - off_t sli_offset; - int line_ok; - - sli_offset = 0; - while ((loop_start == -1 || loop_length == -1) && sli_offset < get_streamfile_size(streamFile)) { - char *endptr, *foundptr; - - bytes_read = read_line(line, sizeof(line), sli_offset, streamFile, &line_ok); - if (!line_ok) goto fail; - - if (memcmp("LoopStart=",line,10)==0 && line[10] != '\0') { - loop_start = strtol(line+10,&endptr,10); - if (*endptr != '\0') { - loop_start = -1; /* if it didn't parse cleanly */ - } - } - else if (memcmp("LoopLength=",line,11)==0 && line[11] != '\0') { - loop_length = strtol(line+11,&endptr,10); - if (*endptr != '\0') { - loop_length = -1; /* if it didn't parse cleanly */ - } - } - - /* a completely different format (2.0?), also with .sli extension and can be handled similarly */ - if ((foundptr = strstr(line,"To=")) != NULL && isdigit(foundptr[3])) { - loop_to = strtol(foundptr+3,&endptr,10); - if (*endptr != ';') { - loop_to = -1; - } - } - if ((foundptr = strstr(line,"From=")) != NULL && isdigit(foundptr[5])) { - loop_from = strtol(foundptr+5,&endptr,10); - if (*endptr != ';') { - loop_from = -1; - } - } - - sli_offset += bytes_read; - } - } - - if (loop_start != -1 && loop_length != -1) { /* v1 */ - vgmstream_force_loop(vgmstream,1,loop_start, loop_start+loop_length); - } - else if (loop_from != -1 && loop_to != -1) { /* v2 */ - vgmstream_force_loop(vgmstream,1,loop_to, loop_from); - } - else { - goto fail; /* if there's no loop points the .sli wasn't valid */ - } - - close_streamfile(streamData); - return vgmstream; - -fail: - close_streamfile(streamData); - close_vgmstream(vgmstream); - return NULL; -} +#include "meta.h" +#include + + +/* .sli+ogg/opus - KiriKiri engine / WaveLoopManager loop points loader [Fate/Stay Night (PC), World End Economica (PC)] */ +VGMSTREAM* init_vgmstream_sli_ogg(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + STREAMFILE* sf_data = NULL; + int32_t loop_start = -1, loop_length = -1; + int32_t loop_from = -1, loop_to = -1; + + /* checks */ + if (!check_extensions(sf, "sli")) + goto fail; + + { + /* try with file.ogg/opus.sli=header and file.ogg/opus=data */ + char basename[PATH_LIMIT]; + get_streamfile_basename(sf,basename,PATH_LIMIT); + sf_data = open_streamfile_by_filename(sf, basename); + if (!sf_data) goto fail; + } + + if (!is_id32be(0x00, sf_data, "OggS")) + goto fail; + + /* let the real initer do the parsing */ + if (is_id32be(0x1c, sf_data, "Opus")) { /* Sabbat of the Witch (PC) */ + vgmstream = init_vgmstream_ogg_opus(sf_data); + if (!vgmstream) goto fail; + + /* somehow sli+opus use 0 encoder delay in the OpusHead (to simplify looping?) */ + vgmstream->meta_type = meta_OPUS_SLI; + } + else { /* Fate/Stay Night (PC) */ + vgmstream = init_vgmstream_ogg_vorbis(sf_data); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_OGG_SLI; + } + + + /* find loop text */ + { + char line[PATH_LIMIT]; + size_t bytes_read; + off_t sli_offset; + int line_ok; + + sli_offset = 0; + while ((loop_start == -1 || loop_length == -1) && sli_offset < get_streamfile_size(sf)) { + char *endptr, *foundptr; + + bytes_read = read_line(line, sizeof(line), sli_offset, sf, &line_ok); + if (!line_ok) goto fail; + sli_offset += bytes_read; + /* files may be padded with 0s */ + + /* comments in v2.0 [Sabbath of the Witch (PC), KARAKARA (PC)] */ + if (line[0] == '#') + continue; + + if (memcmp("LoopStart=", line,10) == 0 && line[10] != '\0') { + loop_start = strtol(line + 10, &endptr, 10); + if (*endptr != '\0') { + loop_start = -1; /* if it didn't parse cleanly */ + } + } + else if (memcmp("LoopLength=", line, 11) == 0 && line[11] != '\0') { + loop_length = strtol(line + 11, &endptr, 10); + if (*endptr != '\0') { + loop_length = -1; /* if it didn't parse cleanly */ + } + } + + /* a completely different format ("#2.00"?), can be handled similarly */ + if ((foundptr = strstr(line,"To=")) != NULL && isdigit(foundptr[3])) { + loop_to = strtol(foundptr + 3, &endptr, 10); + if (*endptr != ';') { + loop_to = -1; + } + } + if ((foundptr = strstr(line,"From=")) != NULL && isdigit(foundptr[5])) { + loop_from = strtol(foundptr + 5, &endptr, 10); + if (*endptr != ';') { + loop_from = -1; + } + } + + } + } + + if (loop_start != -1 && loop_length != -1) { /* v1 */ + vgmstream_force_loop(vgmstream, 1, loop_start, loop_start + loop_length); + } + else if (loop_from != -1 && loop_to != -1) { /* v2 */ + vgmstream_force_loop(vgmstream, 1, loop_to, loop_from); + } + else { + goto fail; /* if there's no loop points the .sli wasn't valid */ + } + + close_streamfile(sf_data); + return vgmstream; + +fail: + close_streamfile(sf_data); + close_vgmstream(vgmstream); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ubi_ckd.c b/Frameworks/vgmstream/vgmstream/src/meta/ubi_ckd.c index 17776a564..baac19f1d 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ubi_ckd.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ubi_ckd.c @@ -1,5 +1,6 @@ #include "meta.h" #include "../coding/coding.h" +#include "../coding/coding.h" typedef enum { MSADPCM, DSP, MP3, XMA2 } ckd_codec; @@ -9,31 +10,36 @@ VGMSTREAM* init_vgmstream_ubi_ckd(STREAMFILE* sf) { VGMSTREAM* vgmstream = NULL; off_t start_offset, first_offset = 0x0c, chunk_offset; size_t chunk_size, data_size; - int loop_flag, channel_count, interleave = 0, format; + int loop_flag, channels, interleave = 0, format; ckd_codec codec; int big_endian; uint32_t (*read_u32)(off_t,STREAMFILE*); uint16_t (*read_u16)(off_t,STREAMFILE*); /* checks */ + if (!is_id32be(0x00,sf, "RIFF")) + goto fail; + if (!is_id32be(0x08,sf, "WAVE")) + goto fail; + /* .wav.ckd: main (other files are called .xxx.ckd too) */ if (!check_extensions(sf,"ckd")) goto fail; - /* another slighly funny RIFF */ - if (read_u32be(0x00,sf) != 0x52494646) /* "RIFF" */ + /* another slighly funny RIFF, mostly standard except machine endian and minor oddities */ + if (!is_id32be(0x0c,sf, "fmt ")) goto fail; - if (read_u32be(0x08,sf) != 0x57415645) /* "WAVE" */ - goto fail; - /* RIFF size is normal */ big_endian = guess_endianness32bit(0x04, sf); read_u32 = big_endian ? read_u32be : read_u32le; read_u16 = big_endian ? read_u16be : read_u16le; + if (read_u32(0x04, sf) + 0x04 + 0x04 != get_streamfile_size(sf)) + goto fail; + loop_flag = 0; format = read_u16(0x14,sf); - channel_count = read_u16(0x16,sf); + channels = read_u16(0x16,sf); switch(format) { case 0x0002: @@ -50,7 +56,7 @@ VGMSTREAM* init_vgmstream_ubi_ckd(STREAMFILE* sf) { } else if (find_chunk_be(sf, 0x6461744C,first_offset,0, &chunk_offset,&chunk_size)) { /* "datL" */ /* mono "datL" or full interleave with a "datR" after the "datL" (no check, pretend it exists) */ start_offset = chunk_offset; - data_size = chunk_size * channel_count; + data_size = chunk_size * channels; interleave = (0x4+0x4) + chunk_size; /* don't forget to skip the "datR"+size chunk */ } else { goto fail; @@ -107,11 +113,11 @@ VGMSTREAM* init_vgmstream_ubi_ckd(STREAMFILE* sf) { /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); + vgmstream = allocate_vgmstream(channels,loop_flag); if (!vgmstream) goto fail; vgmstream->sample_rate = read_u32(0x18,sf); - vgmstream->num_samples = dsp_bytes_to_samples(data_size, channel_count); + vgmstream->num_samples = dsp_bytes_to_samples(data_size, channels); vgmstream->coding_type = coding_NGC_DSP; vgmstream->meta_type = meta_UBI_CKD; @@ -170,4 +176,3 @@ fail: close_vgmstream(vgmstream); return NULL; } - diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ubi_ckd_cwav.c b/Frameworks/vgmstream/vgmstream/src/meta/ubi_ckd_cwav.c new file mode 100644 index 000000000..f7f51af22 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/ubi_ckd_cwav.c @@ -0,0 +1,37 @@ +#include "meta.h" +#include "../coding/coding.h" +#include "ubi_ckd_cwav_streamfile.h" + +/* CKD RIFF - UbiArt Framework (v1) audio container [Rayman Origins (3DS)] */ +VGMSTREAM* init_vgmstream_ubi_ckd_cwav(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + STREAMFILE* temp_sf = NULL; + + /* checks */ + if (!is_id32be(0x00,sf, "RIFF")) + goto fail; + if (!is_id32be(0x08,sf, "WAVE")) + goto fail; + + if (!(is_id32be(0x0c,sf, "dsph") || is_id32be(0x0c,sf, "cwav"))) + goto fail; + + /* .wav: main (unlike .wav.cdk of other versions) */ + if (!check_extensions(sf,"wav,lwav")) + goto fail; + + /* inside dsph (header+start, optional) and cwav (body, always) RIFF chunks is a full "CWAV", + * since dsph also contains some data just deblock */ + + temp_sf = setup_ubi_ckd_cwav_streamfile(sf); + if (!temp_sf) goto fail; + + vgmstream = init_vgmstream_rwsd(temp_sf); + if (!vgmstream) goto fail; + + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ubi_ckd_cwav_streamfile.h b/Frameworks/vgmstream/vgmstream/src/meta/ubi_ckd_cwav_streamfile.h new file mode 100644 index 000000000..e1a81b6c1 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/ubi_ckd_cwav_streamfile.h @@ -0,0 +1,37 @@ +#ifndef _UBI_CKD_CWAV_STREAMFILE_H_ +#define _UBI_CKD_CWAV_STREAMFILE_H_ +#include "deblock_streamfile.h" + +static void block_callback(STREAMFILE *sf, deblock_io_data *data) { + uint32_t chunk_type = read_u32be(data->physical_offset + 0x00, sf); + uint32_t chunk_size = read_u32le(data->physical_offset + 0x04, sf); + + if (chunk_type == get_id32be("RIFF")) { + data->data_size = 0x0; + data->skip_size = 0x0; + data->block_size = 0x0c; + } + else { + data->data_size = chunk_size; + data->skip_size = 0x08; + data->block_size = data->data_size + data->skip_size; + } +} + +/* Deblocks CWAV streams inside RIFF */ +static STREAMFILE* setup_ubi_ckd_cwav_streamfile(STREAMFILE* sf) { + STREAMFILE *new_sf = NULL; + deblock_config_t cfg = {0}; + + cfg.stream_start = 0x00; + cfg.stream_size = get_streamfile_size(sf); + cfg.block_callback = block_callback; + + /* setup sf */ + new_sf = open_wrap_streamfile(sf); + new_sf = open_io_deblock_streamfile_f(new_sf, &cfg); + new_sf = open_fakename_streamfile_f(new_sf, NULL, "bcwav"); + return new_sf; +} + +#endif /* _UBI_CKD_CWAV_STREAMFILE_H_ */ diff --git a/Frameworks/vgmstream/vgmstream/src/plugins.c b/Frameworks/vgmstream/vgmstream/src/plugins.c index b6206fa29..998fee803 100644 --- a/Frameworks/vgmstream/vgmstream/src/plugins.c +++ b/Frameworks/vgmstream/vgmstream/src/plugins.c @@ -65,7 +65,7 @@ int vgmstream_ctx_is_valid(const char* filename, vgmstream_ctx_valid_cfg *cfg) { } void vgmstream_get_title(char* buf, int buf_len, const char* filename, VGMSTREAM* vgmstream, vgmstream_title_t* cfg) { - const char *pos; + const char* pos; char* pos2; char temp[1024]; @@ -79,6 +79,13 @@ void vgmstream_get_title(char* buf, int buf_len, const char* filename, VGMSTREAM pos = filename; else pos++; + + /* special case for foobar that uses a (archive)|(subfile) notation when opening a 7z/zip/etc directly */ + if (cfg && cfg->remove_archive) { + const char* subpos = strchr(pos, '|'); + if (subpos) + pos = subpos + 1; + } strncpy(buf, pos, buf_len); /* name without extension */ diff --git a/Frameworks/vgmstream/vgmstream/src/plugins.h b/Frameworks/vgmstream/vgmstream/src/plugins.h index 39688d821..e63c4aa9b 100644 --- a/Frameworks/vgmstream/vgmstream/src/plugins.h +++ b/Frameworks/vgmstream/vgmstream/src/plugins.h @@ -72,6 +72,7 @@ typedef struct { int force_title; int subsong_range; int remove_extension; + int remove_archive; } vgmstream_title_t; /* get a simple title for plugins */ diff --git a/Frameworks/vgmstream/vgmstream/src/streamfile.c b/Frameworks/vgmstream/vgmstream/src/streamfile.c index 4843f824e..19b1d6aa9 100644 --- a/Frameworks/vgmstream/vgmstream/src/streamfile.c +++ b/Frameworks/vgmstream/vgmstream/src/streamfile.c @@ -93,10 +93,10 @@ static STREAMFILE* open_stdio_streamfile_buffer_by_file(FILE *infile, const char static size_t stdio_read(STDIO_STREAMFILE* sf, uint8_t* dst, offv_t offset, size_t length) { size_t read_total = 0; - if (!sf->infile || !dst || length <= 0 || offset < 0) + if (/*!sf->infile ||*/ !dst || length <= 0 || offset < 0) return 0; - //;VGM_LOG("stdio: read %lx + %x (buf %lx + %x)\n", offset, length, sf->buf_offset, sf->valid_size); + //;VGM_LOG("stdio: read %lx + %x (buf %lx + %x)\n", offset, length, sf->buf_offset, sf->valid_size, sf->buf_size); /* is the part of the requested length in the buffer? */ if (offset >= sf->buf_offset && offset < sf->buf_offset + sf->valid_size) { @@ -124,6 +124,10 @@ static size_t stdio_read(STDIO_STREAMFILE* sf, uint8_t* dst, offv_t offset, size } #endif + /* possible if all data was copied to buf and FD closed */ + if (!sf->infile) + return read_total; + /* read the rest of the requested length */ while (length > 0) { size_t length_to_read; @@ -179,12 +183,15 @@ static size_t stdio_read(STDIO_STREAMFILE* sf, uint8_t* dst, offv_t offset, size sf->offset = offset; /* last fread offset */ return read_total; } + static size_t stdio_get_size(STDIO_STREAMFILE* sf) { return sf->file_size; } + static offv_t stdio_get_offset(STDIO_STREAMFILE* sf) { return sf->offset; } + static void stdio_get_name(STDIO_STREAMFILE* sf, char* name, size_t name_size) { int copy_size = sf->name_len + 1; if (copy_size > name_size) @@ -221,7 +228,7 @@ static STREAMFILE* stdio_open(STDIO_STREAMFILE* sf, const char* const filename, /* on failure just close and try the default path (which will probably fail a second time) */ } #endif - // a normal open, open a new file + return open_stdio_streamfile_buffer(filename, buf_size); } @@ -270,13 +277,29 @@ static STREAMFILE* open_stdio_streamfile_buffer_by_file(FILE* infile, const char this_sf->file_size = 0; /* allow virtual, non-existing files */ } - /* Typically fseek(o)/ftell(o) may only handle up to ~2.14GB, signed 32b = 0x7FFFFFFF - * (happens in banks like FSB, though rarely). Should work if configured properly, log otherwise. */ + /* Typically fseek(o)/ftell(o) may only handle up to ~2.14GB, signed 32b = 0x7FFFFFFF (rarely + * happens in giant banks like FSB/KTSR). Should work if configured properly using ftell_v, log otherwise. */ if (this_sf->file_size == 0xFFFFFFFF) { /* -1 on error */ vgm_logi("STREAMFILE: file size too big (report)\n"); goto fail; /* can be ignored but may result in strange/unexpected behaviors */ } + /* Rarely a TXTP needs to open *many* streamfiles = many file descriptors = reaches OS limit = error. + * Ideally should detect better and open/close as needed or reuse FDs for files that don't play at + * the same time, but it's complex since every SF is separate (would need some kind of FD manager). + * For the time being, if the file is smaller that buffer we can just read it fully and close the FD, + * that should help since big TXTP usually just need many small files. + * Doubles as an optimization as most files given will be read fully into buf on first read. */ + if (this_sf->file_size && this_sf->file_size < this_sf->buf_size && this_sf->infile) { + //;VGM_LOG("stdio: fit filesize %x into buf %x\n", sf->file_size, sf->buf_size); + + this_sf->buf_offset = 0; + this_sf->valid_size = fread(this_sf->buf, sizeof(uint8_t), this_sf->file_size, this_sf->infile); + + fclose(this_sf->infile); + this_sf->infile = NULL; + } + return &this_sf->vt; fail: diff --git a/Frameworks/vgmstream/vgmstream/src/vgmstream.c b/Frameworks/vgmstream/vgmstream/src/vgmstream.c index 89965820b..1b83af06b 100644 --- a/Frameworks/vgmstream/vgmstream/src/vgmstream.c +++ b/Frameworks/vgmstream/vgmstream/src/vgmstream.c @@ -53,7 +53,6 @@ VGMSTREAM* (*init_vgmstream_functions[])(STREAMFILE* sf) = { init_vgmstream_vpk, init_vgmstream_genh, init_vgmstream_ogg_vorbis, - init_vgmstream_sli_ogg, init_vgmstream_sfl_ogg, init_vgmstream_sadb, init_vgmstream_ps2_bmdx, @@ -152,7 +151,6 @@ VGMSTREAM* (*init_vgmstream_functions[])(STREAMFILE* sf) = { init_vgmstream_ish_isd, init_vgmstream_gsp_gsb, init_vgmstream_ydsp, - init_vgmstream_msvp, init_vgmstream_ngc_ssm, init_vgmstream_ps2_joe, init_vgmstream_vgs, @@ -519,6 +517,7 @@ VGMSTREAM* (*init_vgmstream_functions[])(STREAMFILE* sf) = { init_vgmstream_wbk, init_vgmstream_wbk_nslb, init_vgmstream_dsp_apex, + init_vgmstream_ubi_ckd_cwav, /* lower priority metas (no clean header identity, somewhat ambiguous, or need extension/companion file to identify) */ init_vgmstream_agsc, @@ -529,6 +528,7 @@ VGMSTREAM* (*init_vgmstream_functions[])(STREAMFILE* sf) = { init_vgmstream_mic_koei, init_vgmstream_seb, init_vgmstream_ps2_pnb, + init_vgmstream_sli_ogg, /* 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 */ diff --git a/Frameworks/vgmstream/vgmstream/src/vgmstream.h b/Frameworks/vgmstream/vgmstream/src/vgmstream.h index 8d144cb3c..b56bb783c 100644 --- a/Frameworks/vgmstream/vgmstream/src/vgmstream.h +++ b/Frameworks/vgmstream/vgmstream/src/vgmstream.h @@ -425,7 +425,6 @@ typedef enum { meta_FFCC_STR, /* Final Fantasy: Crystal Chronicles */ meta_UBI_JADE, /* Beyond Good & Evil, Rayman Raving Rabbids */ meta_GCA, /* Metal Slug Anthology */ - meta_MSVP, /* Popcap Hits */ meta_NGC_SSM, /* Golden Gashbell Full Power */ meta_PS2_JOE, /* Wall-E / Pixar games */ meta_NGC_YMF, /* WWE WrestleMania X8 */ @@ -674,7 +673,7 @@ typedef enum { meta_DSP_ITL, /* Charinko Hero (GC) */ meta_A2M, /* Scooby-Doo! Unmasked (PS2) */ meta_AHV, /* Headhunter (PS2) */ - meta_MSV, /* Fight Club (PS2) */ + meta_MSV, meta_SDF, meta_SVG, /* Hunter - The Reckoning - Wayward (PS2) */ meta_VIS, /* AirForce Delta Strike (PS2) */