From 2a52dd831a23be92361d621e1404182f5b6b7c2e Mon Sep 17 00:00:00 2001 From: Christopher Snowhill Date: Wed, 6 Jan 2021 01:04:54 -0800 Subject: [PATCH] Updated VGMStream to r1050-3548-g64ffe5ea --- .../libvgmstream.xcodeproj/project.pbxproj | 4 + Frameworks/vgmstream/vgmstream/src/formats.c | 1 + Frameworks/vgmstream/vgmstream/src/meta/acx.c | 43 ++ .../vgmstream/vgmstream/src/meta/adx_keys.h | 24 +- Frameworks/vgmstream/vgmstream/src/meta/awc.c | 2 +- Frameworks/vgmstream/vgmstream/src/meta/csb.c | 41 +- .../vgmstream/vgmstream/src/meta/fsb_keys.h | 6 +- Frameworks/vgmstream/vgmstream/src/meta/kwb.c | 438 +++++++++++------- .../vgmstream/vgmstream/src/meta/meta.h | 2 + .../vgmstream/src/meta/ngc_dsp_std.c | 2 + .../vgmstream/vgmstream/src/meta/ps2_exst.c | 65 +-- .../vgmstream/vgmstream/src/meta/ubi_bao.c | 36 +- .../vgmstream/vgmstream/src/meta/ubi_sb.c | 4 +- .../vgmstream/vgmstream/src/meta/wwise.c | 3 +- Frameworks/vgmstream/vgmstream/src/meta/xnb.c | 150 ++++-- .../vgmstream/vgmstream/src/streamfile.c | 183 ++++---- .../vgmstream/vgmstream/src/vgmstream.c | 1 + 17 files changed, 646 insertions(+), 359 deletions(-) create mode 100644 Frameworks/vgmstream/vgmstream/src/meta/acx.c diff --git a/Frameworks/vgmstream/libvgmstream.xcodeproj/project.pbxproj b/Frameworks/vgmstream/libvgmstream.xcodeproj/project.pbxproj index b8f17ad52..25eadaa1e 100644 --- a/Frameworks/vgmstream/libvgmstream.xcodeproj/project.pbxproj +++ b/Frameworks/vgmstream/libvgmstream.xcodeproj/project.pbxproj @@ -622,6 +622,7 @@ 83F1EE2D245D4FB20076E182 /* imuse_decoder.c in Sources */ = {isa = PBXBuildFile; fileRef = 83F1EE28245D4FB10076E182 /* imuse_decoder.c */; }; 83F1EE2E245D4FB20076E182 /* vadpcm_decoder.c in Sources */ = {isa = PBXBuildFile; fileRef = 83F1EE2C245D4FB20076E182 /* vadpcm_decoder.c */; }; 83F1EE30245D4FC10076E182 /* imuse.c in Sources */ = {isa = PBXBuildFile; fileRef = 83F1EE2F245D4FC10076E182 /* imuse.c */; }; + 83F2CCE525A5B41600F46FA8 /* acx.c in Sources */ = {isa = PBXBuildFile; fileRef = 83F2CCE125A5B41600F46FA8 /* acx.c */; }; 83F5F8831908D0A400C8E65F /* fsb5.c in Sources */ = {isa = PBXBuildFile; fileRef = 83F5F8821908D0A400C8E65F /* fsb5.c */; }; 83FBD506235D31F800D35BCD /* riff_ogg_streamfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 83FBD502235D31F700D35BCD /* riff_ogg_streamfile.h */; }; 83FC176D23AC58D100E1025F /* xma_ue3.c in Sources */ = {isa = PBXBuildFile; fileRef = 83FC176A23AC58D100E1025F /* xma_ue3.c */; }; @@ -1363,6 +1364,7 @@ 83F1EE28245D4FB10076E182 /* imuse_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = imuse_decoder.c; sourceTree = ""; }; 83F1EE2C245D4FB20076E182 /* vadpcm_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = vadpcm_decoder.c; sourceTree = ""; }; 83F1EE2F245D4FC10076E182 /* imuse.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = imuse.c; sourceTree = ""; }; + 83F2CCE125A5B41600F46FA8 /* acx.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = acx.c; sourceTree = ""; }; 83F412871E932F9A002E37D0 /* Vorbis.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Vorbis.xcodeproj; path = ../Vorbis/macosx/Vorbis.xcodeproj; sourceTree = ""; }; 83F5F8821908D0A400C8E65F /* fsb5.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = fsb5.c; sourceTree = ""; }; 83FBD502235D31F700D35BCD /* riff_ogg_streamfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = riff_ogg_streamfile.h; sourceTree = ""; }; @@ -1685,6 +1687,7 @@ 836F6E2A18BDC2180095E648 /* aax.c */, 837CEAD623487E8300E62A4A /* acb.c */, 836F6E2B18BDC2180095E648 /* acm.c */, + 83F2CCE125A5B41600F46FA8 /* acx.c */, 83AA7F7A2519C042004C5298 /* adp_konami.c */, 834FE0CF215C79E8000A5D3D /* adpcm_capcom.c */, 836F6E2C18BDC2180095E648 /* ads.c */, @@ -2674,6 +2677,7 @@ 836F6F9918BDC2190095E648 /* maxis_xa.c in Sources */, 836F702118BDC2190095E648 /* rs03.c in Sources */, 836F6F8818BDC2190095E648 /* fsb.c in Sources */, + 83F2CCE525A5B41600F46FA8 /* acx.c in Sources */, 83FC176D23AC58D100E1025F /* xma_ue3.c in Sources */, 836F6FE518BDC2190095E648 /* ps2_lpcm.c in Sources */, 8375737321F9507D00F01AF5 /* oki_decoder.c in Sources */, diff --git a/Frameworks/vgmstream/vgmstream/src/formats.c b/Frameworks/vgmstream/vgmstream/src/formats.c index 3d27fd46c..3d31de067 100644 --- a/Frameworks/vgmstream/vgmstream/src/formats.c +++ b/Frameworks/vgmstream/vgmstream/src/formats.c @@ -39,6 +39,7 @@ static const char* extension_list[] = { //"ac3", //common, FFmpeg/not parsed (AC3) "acb", "acm", + "acx", "ad", //txth/reserved [Xenosaga Freaks (PS2)] "adc", //txth/reserved [Tomb Raider The Last Revelation (DC), Tomb Raider Chronicles (DC)] "adm", diff --git a/Frameworks/vgmstream/vgmstream/src/meta/acx.c b/Frameworks/vgmstream/vgmstream/src/meta/acx.c new file mode 100644 index 000000000..430ea4487 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/acx.c @@ -0,0 +1,43 @@ +#include "meta.h" +#include "../coding/coding.h" + +/* .acx - CRI container [Baroque (SAT), Persona 3 (PS2), THE iDOLM@STER: Live For You (X360)] */ +VGMSTREAM* init_vgmstream_acx(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + STREAMFILE* temp_sf = NULL; + off_t subfile_offset; + size_t subfile_size; + int total_subsongs, target_subsong = sf->stream_index; + + + /* checks */ + if (!check_extensions(sf,"acx")) + goto fail; + if (read_u32be(0x00,sf) != 0x00000000) + goto fail; + + /* simple container for sfx and rarely music [Burning Rangers (SAT)], + * mainly used until .csb was introduced */ + + total_subsongs = read_u32be(0x04,sf); + if (target_subsong == 0) target_subsong = 1; + if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail; + + subfile_offset = read_u32be(0x08 + (target_subsong-1) * 0x08 + 0x00,sf); + subfile_size = read_u32be(0x08 + (target_subsong-1) * 0x08 + 0x04,sf); + + temp_sf = setup_subfile_streamfile(sf, subfile_offset, subfile_size, "adx"); + if (!temp_sf) goto fail; + + vgmstream = init_vgmstream_adx(temp_sf); + if (!vgmstream) goto fail; + + vgmstream->num_streams = total_subsongs; + close_streamfile(temp_sf); + return vgmstream; + +fail: + close_streamfile(temp_sf); + close_vgmstream(vgmstream); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/adx_keys.h b/Frameworks/vgmstream/vgmstream/src/meta/adx_keys.h index 158b978ca..46edc4db2 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/adx_keys.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/adx_keys.h @@ -20,7 +20,7 @@ static const adxkey_info adxkey8_list[] = { {0x49e1,0x4a57,0x553d, "karaage",0}, /* Blood+ (PS2) [Grasshopper Manufacture] */ - {0x5f5d,0x58bd,0x55ed, NULL,0}, // keystring not in ELF? + {0x5f5d,0x58bd,0x55ed, "LOVELOVE",0}, // obfuscated keystring is "KNUDKNUD", adds +1 to chars to get final key /* Killer7 (PS2) [Grasshopper Manufacture] */ {0x50fb,0x5803,0x5701, "GHM",0}, @@ -34,14 +34,14 @@ static const adxkey_info adxkey8_list[] = { /* Phantasy Star Universe (PC), Phantasy Star Universe: Ambition of the Illuminus (PS2) [Sonic Team] */ {0x5deb,0x5f27,0x673f, "3x5k62bg9ptbwy",0}, - /* Senko no Ronde [G.rev] */ + /* Senko no Ronde Rev.X (X360) [G.rev] */ {0x46d3,0x5ced,0x474d, "ranatus",0}, /* NiGHTS: Journey of Dreams (Wii) [Sonic Team] */ {0x440b,0x6539,0x5723, "sakakit4649",0}, - /* unknown source */ - {0x586d,0x5d65,0x63eb, NULL,0}, // from guessadx (unique?) + /* The iDOLM@STER: Live For You (X360) [Bandai Namco] (.aix) */ + {0x586d,0x5d65,0x63eb, "75_501NO_003B",0}, /* Shuffle! On the Stage (PS2) [Navel] */ {0x4969,0x5deb,0x467f, "SHUF",0}, @@ -64,8 +64,8 @@ static const adxkey_info adxkey8_list[] = { /* Soulcalibur IV (PS3) [Namco] */ {0x59ed,0x4679,0x46c9, "SC4Test",0}, - /* Senko no Ronde DUO (X360) [G.rev] */ - {0x6157,0x6809,0x4045, NULL,0}, // from guessadx + /* Senko no Ronde DUO (X360/AC) [G.rev] */ + {0x6157,0x6809,0x4045, "yomesokushitsu",0}, /* Nogizaka Haruka no Himitsu: Cosplay Hajimemashita (PS2) [Vridge] */ {0x45af,0x5f27,0x52b1, "SKFHSIA",0}, @@ -100,16 +100,16 @@ static const adxkey_info adxkey8_list[] = { /* Soshite Kono Uchuu ni Kirameku Kimi no Shi XXX (PS2) [Datam Polystar] */ {0x5f5d,0x552b,0x5507, "DATAM-KK2",0}, - /* Sakura Taisen: Atsuki Chishio Ni (PS2) [Sega] */ - {0x645d,0x6011,0x5c29, NULL,0}, // confirmed unique with guessadx + /* Sakura Taisen: Atsuki Chishio ni (PS2) [Sega] */ + {0x645d,0x6011,0x5c29, NULL,0}, // possible key: "[Seq][ADX] illegal cri or libsd status." - /* Sakura Taisen 3 ~Paris wa Moeteiru ka~ (PS2) [Sega] */ - {0x62ad,0x4b13,0x5957, NULL,0}, // confirmed unique with guessadx + /* Sakura Taisen Monogatari: Mysterious Paris (PS2) [Sega] */ + {0x62ad,0x4b13,0x5957, "inoue4126",0}, /* Sotsugyou 2nd Generation (PS2) [Jinx] */ {0x6305,0x509f,0x4c01, NULL,0}, // First guess from guessadx, other was {0x6307,0x509f,0x2ac5} - /* La Corda d'Oro (PSP) [Koei] */ + /* Kin'iro no Corda -La Corda d'Oro- (PSP) [Koei] */ {0x55b7,0x67e5,0x5387, NULL,0}, // keystring not in ELF? /* Nanatsuiro * Drops Pure!! (PS2) [Media Works] */ @@ -127,7 +127,7 @@ static const adxkey_info adxkey8_list[] = { /* Sora no Otoshimono: DokiDoki Summer Vacation (PSP) [Kadokawa Shoten] */ {0x5e75,0x4a89,0x4c61, "funen-gomi",0}, - /* Boku wa Koukuu Kanseikan: Airport Hero Naha (PSP) [Sonic Powered] */ + /* Boku wa Koukuu Kanseikan: Airport Hero Naha/Narita/Shinchitose/Haneda/Kankuu (PSP) [Sonic Powered] */ {0x64ab,0x5297,0x632f, "sonic",0}, /* Lucky Star: Net Idol Meister (PSP) [Vridge, Kadokawa Shoten] */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/awc.c b/Frameworks/vgmstream/vgmstream/src/meta/awc.c index d1f82c40e..b3dad4f03 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/awc.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/awc.c @@ -162,7 +162,7 @@ VGMSTREAM* init_vgmstream_awc(STREAMFILE* sf) { vgmstream->layout_data = build_layered_awc(sf, &awc); if (!vgmstream->layout_data) goto fail; vgmstream->layout_type = layout_layered; - vgmstream->coding_type = coding_FFmpeg; + vgmstream->coding_type = coding_VORBIS_custom; } else { vorbis_custom_config cfg = {0}; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/csb.c b/Frameworks/vgmstream/vgmstream/src/meta/csb.c index dae785d0d..a56bed015 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/csb.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/csb.c @@ -4,22 +4,22 @@ /* CSB (Cue Sheet Binary?) - CRI container of memory audio, often together with a .cpk wave bank */ -VGMSTREAM * init_vgmstream_csb(STREAMFILE *streamFile) { - VGMSTREAM *vgmstream = NULL; - STREAMFILE *temp_sf = NULL; +VGMSTREAM* init_vgmstream_csb(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + STREAMFILE* temp_sf = NULL; off_t subfile_offset; size_t subfile_size; utf_context *utf = NULL; utf_context *utf_sdl = NULL; - int total_subsongs, target_subsong = streamFile->stream_index; + int total_subsongs, target_subsong = sf->stream_index; uint8_t fmt = 0; - const char *stream_name; + const char* stream_name; /* checks */ - if (!check_extensions(streamFile, "csb")) + if (!check_extensions(sf, "csb")) goto fail; - if (read_32bitBE(0x00,streamFile) != 0x40555446) /* "@UTF" */ + if (!is_id32be(0x00,sf, "@UTF")) goto fail; /* .csb is an early, simpler version of .acb+awk (see acb.c) used until ~2013? @@ -35,32 +35,41 @@ VGMSTREAM * init_vgmstream_csb(STREAMFILE *streamFile) { int found = 0; - utf = utf_open(streamFile, table_offset, &rows, &name); + utf = utf_open(sf, table_offset, &rows, &name); if (!utf) goto fail; if (strcmp(name, "TBLCSB") != 0) goto fail; /* each TBLCSB row has a name and subtable with actual things: - * - INFO (TBL_INFO): table type/version + * - INFO (TBL_INFO): table type/version, rarely ommited [Nights: Journey of Dreams (Wii)-some csb] * - CUE (TBLCUE): base cues * - SYNTH (TBLSYN): cue configs * - SOUND_ELEMENT (TBLSDL): audio info/data (usually AAX) * - ISAAC (TBLISC): 3D config * - VOICE_LIMIT_GROUP (TBLVLG): system info? - * Subtable can be empty but must still appear (0 rows). + * Subtable can be empty but still appear (0 rows). */ - sdl_row = 3; /* should use fixed order */ - - /* get SOUND_ELEMENT and table */ - if (!utf_query_string(utf, sdl_row, "name", &row_name) || strcmp(row_name, "SOUND_ELEMENT") != 0) + sdl_row = -1; + for (i = 0; i < rows; i++) { + if (!utf_query_string(utf, i, "name", &row_name)) + goto fail; + if (strcmp(row_name, "SOUND_ELEMENT") == 0) { + sdl_row = i; /* usually 2 or 3 */ + break; + } + } + if (sdl_row < 0) goto fail; + + + /* read SOUND_ELEMENT table */ if (!utf_query_u8(utf, sdl_row, "ttype", &ttype) || ttype != 4) goto fail; if (!utf_query_data(utf, sdl_row, "utf", &sdl_offset, &sdl_size)) goto fail; - utf_sdl = utf_open(streamFile, sdl_offset, &sdl_rows, &sdl_name); + utf_sdl = utf_open(sf, sdl_offset, &sdl_rows, &sdl_name); if (!utf_sdl) goto fail; if (strcmp(sdl_name, "TBLSDL") != 0) @@ -109,7 +118,7 @@ VGMSTREAM * init_vgmstream_csb(STREAMFILE *streamFile) { //;VGM_LOG("CSB: subfile offset=%lx + %x\n", subfile_offset, subfile_size); - temp_sf = setup_subfile_streamfile(streamFile, subfile_offset, subfile_size, "aax"); + temp_sf = setup_subfile_streamfile(sf, subfile_offset, subfile_size, "aax"); if (!temp_sf) goto fail; switch(fmt) { diff --git a/Frameworks/vgmstream/vgmstream/src/meta/fsb_keys.h b/Frameworks/vgmstream/vgmstream/src/meta/fsb_keys.h index 16640d55f..2d626c1d6 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/fsb_keys.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/fsb_keys.h @@ -80,6 +80,9 @@ static const uint8_t key_ghm[] = { 0x8C,0xFA,0xF3,0x14,0xB1,0x53,0xDA,0xAB,0x2B, /* Worms Rumble Beta (PC) */ //"FXnTffGJ9LS855Gc" static const uint8_t key_wrb[] = { 0x46,0x58,0x6E,0x54,0x66,0x66,0x47,0x4A,0x39,0x4C,0x53,0x38,0x35,0x35,0x47,0x63 }; +/* Bubble Fighter (PC) */ //"qjvkeoqkrdhkdckd" +static const uint8_t key_bbf[] = { 0x71,0x6A,0x76,0x6B,0x65,0x6F,0x71,0x6B,0x72,0x64,0x68,0x6B,0x64,0x63,0x6B,0x64 }; + // Unknown: // - Battle: Los Angeles // - Guitar Hero: Warriors of Rock, DJ hero FSB @@ -90,7 +93,7 @@ typedef struct { int is_fsb5; /* FSB5 or FSB4/3*/ int is_alt; /* alt XOR mode (seemingly not tied to FSB version or anything) */ size_t fsbkey_size; - const uint8_t *fsbkey; + const uint8_t* fsbkey; } fsbkey_info; static const fsbkey_info fsbkey_list[] = { @@ -153,6 +156,7 @@ static const fsbkey_info fsbkey_list[] = { { 1,0, sizeof(key_scp),key_scp },// FSB5 { 0,1, sizeof(key_ghm),key_ghm },// FSB4 { 1,0, sizeof(key_wrb),key_wrb },// FSB5 + { 0,0, sizeof(key_bbf),key_bbf },// FSB4 }; static const int fsbkey_list_count = sizeof(fsbkey_list) / sizeof(fsbkey_list[0]); diff --git a/Frameworks/vgmstream/vgmstream/src/meta/kwb.c b/Frameworks/vgmstream/vgmstream/src/meta/kwb.c index d40aac1d7..746b57a6c 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/kwb.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/kwb.c @@ -7,6 +7,7 @@ typedef struct { int big_endian; int total_subsongs; int target_subsong; + int found; kwb_codec codec; int channels; @@ -26,6 +27,7 @@ typedef struct { static int parse_kwb(kwb_header* kwb, STREAMFILE* sf_h, STREAMFILE* sf_b); static int parse_xws(kwb_header* kwb, STREAMFILE* sf); +static VGMSTREAM* init_vgmstream_koei_wavebank(kwb_header* kwb, STREAMFILE* sf_h, STREAMFILE* sf_b); /* KWB - WaveBank from Koei games */ @@ -33,7 +35,6 @@ VGMSTREAM* init_vgmstream_kwb(STREAMFILE* sf) { VGMSTREAM* vgmstream = NULL; STREAMFILE *sf_h = NULL, *sf_b = NULL; kwb_header kwb = {0}; - int32_t (*read_s32)(off_t,STREAMFILE*) = NULL; int target_subsong = sf->stream_index; @@ -70,89 +71,11 @@ VGMSTREAM* init_vgmstream_kwb(STREAMFILE* sf) { if (!parse_kwb(&kwb, sf_h, sf_b)) goto fail; - read_s32 = kwb.big_endian ? read_s32be : read_s32le; - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(kwb.channels, kwb.loop_flag); + vgmstream = init_vgmstream_koei_wavebank(&kwb, sf_h, sf_b); if (!vgmstream) goto fail; - vgmstream->meta_type = meta_KWB; - vgmstream->sample_rate = kwb.sample_rate; - vgmstream->num_samples = kwb.num_samples; - vgmstream->stream_size = kwb.stream_size; - vgmstream->num_streams = kwb.total_subsongs; - - switch(kwb.codec) { - case PCM16: /* PCM */ - vgmstream->coding_type = coding_PCM16LE; - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = 0x02; - break; - - case MSADPCM: - vgmstream->coding_type = coding_MSADPCM; - vgmstream->layout_type = layout_none; - vgmstream->frame_size = kwb.block_size; - break; - - case DSP_HEAD: - case DSP_BODY: - if (kwb.channels > 1) goto fail; - vgmstream->coding_type = coding_NGC_DSP; /* subinterleave? */ - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = 0x08; - if (kwb.codec == DSP_HEAD) { - dsp_read_coefs(vgmstream, sf_h, kwb.dsp_offset + 0x1c, 0x60, kwb.big_endian); - dsp_read_hist (vgmstream, sf_h, kwb.dsp_offset + 0x40, 0x60, kwb.big_endian); - } - else { - /* typical DSP header + data */ - vgmstream->num_samples = read_s32(kwb.stream_offset + 0x00, sf_b); - dsp_read_coefs(vgmstream, sf_b, kwb.stream_offset + 0x1c, 0x60, kwb.big_endian); - dsp_read_hist (vgmstream, sf_b, kwb.stream_offset + 0x40, 0x60, kwb.big_endian); - kwb.stream_offset += 0x60; - } - - break; - -#ifdef VGM_USE_ATRAC9 - case AT9: { - atrac9_config cfg = {0}; - - { - size_t extra_size = read_u32le(kwb.stream_offset + 0x00, sf_b); - uint32_t config_data = read_u32be(kwb.stream_offset + 0x04, sf_b); - /* 0x0c: encoder delay? */ - /* 0x0e: encoder padding? */ - /* 0x10: samples per frame */ - /* 0x12: frame size */ - - cfg.channels = vgmstream->channels; - cfg.config_data = config_data; - - kwb.stream_offset += extra_size; - kwb.stream_size -= extra_size; - } - - vgmstream->codec_data = init_atrac9(&cfg); - if (!vgmstream->codec_data) goto fail; - vgmstream->coding_type = coding_ATRAC9; - vgmstream->layout_type = layout_none; - - //TODO: check encoder delay - vgmstream->num_samples = atrac9_bytes_to_samples_cfg(kwb.stream_size, cfg.config_data); - break; - } -#endif - default: - goto fail; - } - if (sf_h != sf) close_streamfile(sf_h); - - if (!vgmstream_open_stream(vgmstream, sf_b, kwb.stream_offset)) - goto fail; return vgmstream; fail: @@ -164,7 +87,6 @@ fail: /* XWS - WaveStream? from Koei games */ VGMSTREAM* init_vgmstream_xws(STREAMFILE* sf) { VGMSTREAM* vgmstream = NULL; - STREAMFILE* temp_sf = NULL; kwb_header kwb = {0}; int target_subsong = sf->stream_index; @@ -179,37 +101,138 @@ VGMSTREAM* init_vgmstream_xws(STREAMFILE* sf) { if (!parse_xws(&kwb, sf)) goto fail; - if (kwb.codec == MSF) { - if (kwb.stream_offset == 0) { - vgmstream = init_vgmstream_silence(0,0,0); /* dummy, whatevs */ - if (!vgmstream) goto fail; - } - else { - kwb.stream_size = read_u32be(kwb.stream_offset + 0x0c, sf) + 0x40; + vgmstream = init_vgmstream_koei_wavebank(&kwb, sf, sf); + if (!vgmstream) goto fail; - temp_sf = setup_subfile_streamfile(sf, kwb.stream_offset, kwb.stream_size, "msf"); - if (!temp_sf) goto fail; - - vgmstream = init_vgmstream_msf(temp_sf); - if (!vgmstream) goto fail; - } - - vgmstream->num_streams = kwb.total_subsongs; - } - else { - goto fail; - } - - close_streamfile(temp_sf); return vgmstream; fail: - close_streamfile(temp_sf); + close_vgmstream(vgmstream); return NULL; } +static VGMSTREAM* init_vgmstream_koei_wavebank(kwb_header* kwb, STREAMFILE* sf_h, STREAMFILE* sf_b) { + VGMSTREAM* vgmstream = NULL; + uint32_t (*read_u32)(off_t,STREAMFILE*) = NULL; + int32_t (*read_s32)(off_t,STREAMFILE*) = NULL; -static int parse_type_kwb2(kwb_header* kwb, off_t offset, STREAMFILE* sf_h) { + + read_u32 = kwb->big_endian ? read_u32be : read_u32le; + read_s32 = kwb->big_endian ? read_s32be : read_s32le; + + /* container */ + if (kwb->codec == MSF) { + if (kwb->stream_offset == 0) { + vgmstream = init_vgmstream_silence(0,0,0); /* dummy, whatevs */ + if (!vgmstream) goto fail; + } + else { + STREAMFILE* temp_sf = NULL; + + kwb->stream_size = read_u32(kwb->stream_offset + 0x0c, sf_h) + 0x40; + + temp_sf = setup_subfile_streamfile(sf_h, kwb->stream_offset, kwb->stream_size, "msf"); + if (!temp_sf) goto fail; + + vgmstream = init_vgmstream_msf(temp_sf); + close_streamfile(temp_sf); + if (!vgmstream) goto fail; + } + + vgmstream->num_streams = kwb->total_subsongs; + return vgmstream; + } + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(kwb->channels, kwb->loop_flag); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_KWB; + vgmstream->sample_rate = kwb->sample_rate; + vgmstream->num_samples = kwb->num_samples; + vgmstream->stream_size = kwb->stream_size; + vgmstream->num_streams = kwb->total_subsongs; + + switch(kwb->codec) { + case PCM16: /* PCM */ + vgmstream->coding_type = coding_PCM16LE; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x02; + break; + + case MSADPCM: + vgmstream->coding_type = coding_MSADPCM; + vgmstream->layout_type = layout_none; + vgmstream->frame_size = kwb->block_size; + break; + + case DSP_HEAD: + case DSP_BODY: + if (kwb->channels > 1) goto fail; + vgmstream->coding_type = coding_NGC_DSP; /* subinterleave? */ + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x08; + if (kwb->codec == DSP_HEAD) { + dsp_read_coefs(vgmstream, sf_h, kwb->dsp_offset + 0x1c, 0x60, kwb->big_endian); + dsp_read_hist (vgmstream, sf_h, kwb->dsp_offset + 0x40, 0x60, kwb->big_endian); + } + else { + /* typical DSP header + data */ + vgmstream->num_samples = read_s32(kwb->stream_offset + 0x00, sf_b); + dsp_read_coefs(vgmstream, sf_b, kwb->stream_offset + 0x1c, 0x60, kwb->big_endian); + dsp_read_hist (vgmstream, sf_b, kwb->stream_offset + 0x40, 0x60, kwb->big_endian); + kwb->stream_offset += 0x60; + } + + break; + +#ifdef VGM_USE_ATRAC9 + case AT9: { + atrac9_config cfg = {0}; + + { + size_t extra_size = read_u32le(kwb->stream_offset + 0x00, sf_b); + uint32_t config_data = read_u32be(kwb->stream_offset + 0x04, sf_b); + /* 0x0c: encoder delay? */ + /* 0x0e: encoder padding? */ + /* 0x10: samples per frame */ + /* 0x12: frame size */ + + cfg.channels = vgmstream->channels; + cfg.config_data = config_data; + + kwb->stream_offset += extra_size; + kwb->stream_size -= extra_size; + } + + vgmstream->codec_data = init_atrac9(&cfg); + if (!vgmstream->codec_data) goto fail; + vgmstream->coding_type = coding_ATRAC9; + vgmstream->layout_type = layout_none; + + //TODO: check encoder delay + vgmstream->num_samples = atrac9_bytes_to_samples_cfg(kwb->stream_size, cfg.config_data); + break; + } +#endif + default: + goto fail; + } + + + if (!vgmstream_open_stream(vgmstream, sf_b, kwb->stream_offset)) + goto fail; + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} + +/* ************************************************************************* */ + +static int parse_type_kwb2(kwb_header* kwb, off_t offset, off_t body_offset, STREAMFILE* sf_h) { int i, j, sounds; /* 00: KWB2/KWBN id */ @@ -220,12 +243,15 @@ static int parse_type_kwb2(kwb_header* kwb, off_t offset, STREAMFILE* sf_h) { /* 10: null or 1 */ /* 14: offset to HDDB table (from type), can be null */ + //;VGM_LOG("KWB2: sounds %i, o=%lx\n", sounds, offset); + /* offset table to entries */ for (i = 0; i < sounds; i++) { off_t sound_offset = read_u32le(offset + 0x18 + i*0x04, sf_h); int subsounds, subsound_start, subsound_size; uint16_t version; + //;VGM_LOG("KWB2: entry %i, o=%lx, so=%lx\n", i, offset + 0x18 + i*0x04, sound_offset); if (sound_offset == 0) /* common... */ continue; @@ -258,6 +284,8 @@ static int parse_type_kwb2(kwb_header* kwb, off_t offset, STREAMFILE* sf_h) { kwb->total_subsongs++; if (kwb->total_subsongs != kwb->target_subsong) continue; + kwb->found = 1; + subsound_offset = subsound_start + j*subsound_size; kwb->sample_rate = read_u16le(subsound_offset + 0x00, sf_h); @@ -272,6 +300,8 @@ static int parse_type_kwb2(kwb_header* kwb, off_t offset, STREAMFILE* sf_h) { /* when size > 0x48 */ /* 0x48: subsound entry size */ /* rest: reserved per codec? (usually null) */ + + kwb->stream_offset += body_offset; switch(codec) { case 0x00: @@ -304,16 +334,14 @@ static int parse_type_kwb2(kwb_header* kwb, off_t offset, STREAMFILE* sf_h) { then name table (null terminated and one after other) */ - if (kwb->target_subsong < 0 || kwb->target_subsong > kwb->total_subsongs || kwb->total_subsongs < 1) goto fail; - return 1; fail: return 0; } -static int parse_type_k4hd(kwb_header* kwb, off_t offset, STREAMFILE* sf_h) { +static int parse_type_k4hd(kwb_header* kwb, off_t offset, off_t body_offset, STREAMFILE* sf_h) { off_t ppva_offset, header_offset; - int entries; + int entries, current_subsongs, relative_subsong; size_t entry_size; @@ -344,10 +372,14 @@ static int parse_type_k4hd(kwb_header* kwb, off_t offset, STREAMFILE* sf_h) { goto fail; } - kwb->total_subsongs = entries; - if (kwb->target_subsong < 0 || kwb->target_subsong > kwb->total_subsongs || kwb->total_subsongs < 1) goto fail; + current_subsongs = kwb->total_subsongs; + kwb->total_subsongs += entries; + if (kwb->target_subsong - 1 < current_subsongs || kwb->target_subsong > kwb->total_subsongs) + return 1; + kwb->found = 1; - header_offset = ppva_offset + 0x20 + (kwb->target_subsong-1) * entry_size; + relative_subsong = kwb->target_subsong - current_subsongs; + header_offset = ppva_offset + 0x20 + (relative_subsong-1) * entry_size; kwb->stream_offset = read_u32le(header_offset + 0x00, sf_h); kwb->sample_rate = read_u32le(header_offset + 0x04, sf_h); @@ -363,21 +395,22 @@ static int parse_type_k4hd(kwb_header* kwb, off_t offset, STREAMFILE* sf_h) { kwb->codec = AT9; kwb->channels = 1; /* always, devs use dual subsongs to fake stereo (like as hd3+bd3) */ + kwb->stream_offset += body_offset; return 1; fail: return 0; } -static int parse_type_sdsd(kwb_header* kwb, off_t offset, STREAMFILE* sf_h) { +static int parse_type_sdsd(kwb_header* kwb, off_t offset, off_t body_offset, STREAMFILE* sf_h) { /* has Vers, Head, Prog, Smpl sections (like Sony VABs) unknown codec, blocked with some common start, variable sized */ return 0; } -static int parse_type_sdwi(kwb_header* kwb, off_t offset, STREAMFILE* sf_h) { +static int parse_type_sdwi(kwb_header* kwb, off_t offset, off_t body_offset, STREAMFILE* sf_h) { off_t smpl_offset, header_offset; - int entries; + int entries, current_subsongs, relative_subsong; size_t entry_size; @@ -404,10 +437,14 @@ static int parse_type_sdwi(kwb_header* kwb, off_t offset, STREAMFILE* sf_h) { entries = read_u32le(smpl_offset + 0x0c, sf_h); /* LE! */ entry_size = 0x40; - kwb->total_subsongs = entries; - if (kwb->target_subsong < 0 || kwb->target_subsong > kwb->total_subsongs || kwb->total_subsongs < 1) goto fail; + current_subsongs = kwb->total_subsongs; + kwb->total_subsongs += entries; + if (kwb->target_subsong - 1 < current_subsongs || kwb->target_subsong > kwb->total_subsongs) + return 1; + kwb->found = 1; - header_offset = smpl_offset + 0x10 + (kwb->target_subsong-1) * entry_size; + relative_subsong = kwb->target_subsong - current_subsongs; + header_offset = smpl_offset + 0x10 + (relative_subsong-1) * entry_size; /* 00: "SS" + ID (0..N) */ kwb->stream_offset = read_u32be(header_offset + 0x04, sf_h); @@ -427,11 +464,14 @@ static int parse_type_sdwi(kwb_header* kwb, off_t offset, STREAMFILE* sf_h) { kwb->codec = DSP_BODY; kwb->channels = 1; + kwb->stream_offset += body_offset; + return 1; fail: return 0; } + static int parse_kwb(kwb_header* kwb, STREAMFILE* sf_h, STREAMFILE* sf_b) { off_t head_offset, body_offset, start; uint32_t type; @@ -485,22 +525,22 @@ static int parse_kwb(kwb_header* kwb, STREAMFILE* sf_h, STREAMFILE* sf_b) { switch(type) { case 0x4B574232: /* "KWB2" [Bladestorm Nightmare (PC), Dissidia NT (PC)] */ case 0x4B57424E: /* "KWBN" [Fire Emblem Warriors (Switch)] */ - if (!parse_type_kwb2(kwb, head_offset, sf_h)) + if (!parse_type_kwb2(kwb, head_offset, body_offset, sf_h)) goto fail; break; case 0x4B344844: /* "K4HD" [Dissidia NT (PS4), (Vita) */ - if (!parse_type_k4hd(kwb, head_offset, sf_h)) + if (!parse_type_k4hd(kwb, head_offset, body_offset, sf_h)) goto fail; break; case 0x53447364: /* "SDsd" (PS3? leftover files) */ - if (!parse_type_sdsd(kwb, head_offset, sf_h)) + if (!parse_type_sdsd(kwb, head_offset, body_offset, sf_h)) goto fail; break; case 0x53445769: /* "SDWi" [Fatal Frame 5 (WiiU)] */ - if (!parse_type_sdwi(kwb, head_offset, sf_h)) + if (!parse_type_sdwi(kwb, head_offset, body_offset, sf_h)) goto fail; break; @@ -508,7 +548,8 @@ static int parse_kwb(kwb_header* kwb, STREAMFILE* sf_h, STREAMFILE* sf_b) { goto fail; } - kwb->stream_offset += body_offset; + if (!kwb->found) + goto fail; return 1; fail: @@ -517,17 +558,112 @@ fail: static int parse_type_msfbank(kwb_header* kwb, off_t offset, STREAMFILE* sf) { /* this is just like XWSF, abridged: */ + int entries, current_subsongs, relative_subsong; off_t header_offset; + + entries = read_u32be(offset + 0x14, sf); - kwb->total_subsongs = read_u32be(offset + 0x14, sf); - if (kwb->target_subsong < 0 || kwb->target_subsong > kwb->total_subsongs || kwb->total_subsongs < 1) goto fail; + current_subsongs = kwb->total_subsongs; + kwb->total_subsongs += entries; + if (kwb->target_subsong - 1 < current_subsongs || kwb->target_subsong > kwb->total_subsongs) + return 1; + kwb->found = 1; - header_offset = offset + 0x30 + (kwb->target_subsong-1) * 0x04; + relative_subsong = kwb->target_subsong - current_subsongs; + header_offset = offset + 0x30 + (relative_subsong-1) * 0x04; /* just a dumb table pointing to MSF, entries can be dummy */ - kwb->stream_offset = read_u32be(header_offset, sf); + kwb->stream_offset = read_u32be(header_offset, sf); kwb->codec = MSF; + kwb->stream_offset += offset; + + return 1; +//fail: +// return 0; +} + +static int parse_type_xwsfile(kwb_header* kwb, off_t offset, STREAMFILE* sf) { + off_t table1_offset, table2_offset; + int i, chunks, chunks2; + uint32_t (*read_u32)(off_t,STREAMFILE*) = NULL; + + + if (!(is_id32be(offset + 0x00, sf, "XWSF") && is_id32be(offset + 0x04, sf, "ILE\0")) && + !(is_id32be(offset + 0x00, sf, "tdpa") && is_id32be(offset + 0x04, sf, "ck\0\0"))) + goto fail; + + kwb->big_endian = read_u8(offset + 0x08, sf) == 0xFF; + /* 0x0a: version? (0100: NG2/NG3 PS3, 0101: DoA LR PC) */ + + read_u32 = kwb->big_endian ? read_u32be : read_u32le; + + /* 0x0c: tables start */ + /* 0x10: file size */ + chunks = read_u32(offset + 0x14, sf); + chunks2 = read_u32(offset + 0x18, sf); + /* 0x1c: null */ + if (chunks != chunks2) + goto fail; + + table1_offset = read_u32(offset + 0x20, sf); /* offsets */ + table2_offset = read_u32(offset + 0x24, sf); /* sizes */ + /* 0x28: null */ + /* 0x2c: null */ + + + i = 0; + while (i < chunks) { + uint32_t entry_type, head_offset, body_offset, head_size; + //;VGM_LOG("XWS: entry %i/%i\n", i, chunks); + + /* NG2/NG3 PS3 have table1+2, DoA LR PC removes table2 and includes body offset in entries */ + if (table2_offset) { + head_offset = read_u32(offset + table1_offset + i * 0x04 + 0x00, sf); + head_size = read_u32(offset + table2_offset + i * 0x04 + 0x00, sf); + body_offset = head_offset; + i += 1; + + /* sometimes has file end offset as entry with no size*/ + if (!head_size) + continue; + } + else { + head_offset = read_u32(offset + table1_offset + i * 0x04 + 0x00, sf); + body_offset = read_u32(offset + table1_offset + i * 0x04 + 0x04, sf); + i += 2; + } + + if (!head_offset) /* just in case */ + continue; + + + head_offset += offset; + body_offset += offset; + entry_type = read_u32be(head_offset + 0x00, sf); + //;VGM_LOG("XWS: head=%x, body=%x\n", head_offset, body_offset); + + if (entry_type == get_id32be("XWSF")) { /* + "ILE\0" */ + if (!parse_type_xwsfile(kwb, head_offset, sf)) + goto fail; + } + else if (entry_type == get_id32be("CUEB") || entry_type < 0x100) { + ; /* CUE-like info (may start with 0 or a low number instead) */ + } + else if (entry_type == get_id32be("MSFB")) { /* + "ANK\0" */ + if (!parse_type_msfbank(kwb, head_offset, sf)) + goto fail; + } + else if (entry_type == get_id32be("KWB2")) { + if (!parse_type_kwb2(kwb, head_offset, body_offset, sf)) + goto fail; + } + else { + VGM_LOG("XWS: unknown type %x at head=%x, body=%x\n", entry_type, head_offset, body_offset); + goto fail; + } + } + return 1; fail: return 0; @@ -535,59 +671,23 @@ fail: static int parse_xws(kwb_header* kwb, STREAMFILE* sf) { - off_t head_offset, body_offset, start; - uint32_t (*read_u32)(off_t,STREAMFILE*) = NULL; - int chunks, chunks2; - off_t msfb_offset; - /* format is similar to WHD1 with some annoyances of its own - * variations: - * - tdpack: points to N XWSFILE - * - XWSFILE w/ 4 chunks: CUEBANK offset, ? offset, MSFBANK offset, end offset (PS3) + /* Format is similar to WHD1 with some annoyances of its own. Variations: + * - XWSFILE w/ N chunks: CUE offsets + 1 MSFBANK offset * [Ninja Gaiden Sigma 2 (PS3), Ninja Gaiden 3 Razor's Edge (PS3)] * - XWSFILE w/ 2*N chunks: KWB2 offset + data offset * N (ex. 3 pairs = 6 chunks) * [Dead or Alive 5 Last Round (PC)] + * - tdpack: same but points to N XWSFILE + * [Ninja Gaiden 3 Razor's Edge (PS3)] * - * for now basic support for the second case, others we'd have to map subsong N to internal bank M + * Needs to call sub-parts multiple times to fill total subsongs when parsing xwsfile. */ - - if (read_u32be(0x00, sf) != 0x58575346 || /* "XWSF" */ - read_u32be(0x04, sf) != 0x494C4500) /* "ILE\0" */ + if (!parse_type_xwsfile(kwb, 0x00, sf)) goto fail; - kwb->big_endian = read_u8(0x08, sf) == 0xFF; - /* 0x0a: version? */ - - read_u32 = kwb->big_endian ? read_u32be : read_u32le; - - start = read_u32(0x0c, sf); - /* 0x10: file size */ - chunks = read_u32(0x14, sf); - chunks2 = read_u32(0x18, sf); - /* 0x1c: null */ - /* 0x20: some size? */ - /* 0x24: some size? */ - if (chunks != chunks2) + if (!kwb->found) goto fail; - if (chunks != 4) - goto fail; - - msfb_offset = read_u32(start + 0x08, sf); - if (read_u32be(msfb_offset, sf) == 0x4D534642) { /* "MSFB" + "ANK\0" */ - head_offset = msfb_offset; - body_offset = msfb_offset; /* relative to start */ - - if (!parse_type_msfbank(kwb, head_offset, sf)) - goto fail; - } - else { - goto fail; - } - - - kwb->stream_offset += body_offset; - return 1; fail: return 0; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/meta.h b/Frameworks/vgmstream/vgmstream/src/meta/meta.h index 95b372323..361d28aa6 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/meta.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/meta.h @@ -934,4 +934,6 @@ VGMSTREAM *init_vgmstream_sbk(STREAMFILE *sf); VGMSTREAM* init_vgmstream_ifs(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_acx(STREAMFILE* sf); + #endif /*_META_H*/ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ngc_dsp_std.c b/Frameworks/vgmstream/vgmstream/src/meta/ngc_dsp_std.c index e56bf6e93..8d8635935 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ngc_dsp_std.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ngc_dsp_std.c @@ -660,6 +660,8 @@ VGMSTREAM* init_vgmstream_sadb(STREAMFILE* sf) { dspm.start_offset = read_32bitBE(0x48,sf); dspm.interleave = 0x10; + dspm.ignore_loop_ps = 1; /* does something strange with offsets/etc, ignore */ + dspm.meta_type = meta_DSP_SADB; return init_vgmstream_dsp_common(sf, &dspm); fail: diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ps2_exst.c b/Frameworks/vgmstream/vgmstream/src/meta/ps2_exst.c index 68a3388e4..5d9a7312f 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ps2_exst.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ps2_exst.c @@ -3,66 +3,71 @@ /* EXST - from Sony games [Shadow of the Colossus (PS2), Gacha Mecha Stadium Saru Battle (PS2)] */ -VGMSTREAM * init_vgmstream_ps2_exst(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - STREAMFILE * streamBody = NULL; +VGMSTREAM* init_vgmstream_ps2_exst(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + STREAMFILE* sf_body = NULL; off_t start_offset; - int loop_flag, channel_count; + int loop_flag, channels, sample_rate; size_t block_size, num_blocks, loop_start_block; /* checks */ - /* .sts+int: main [Shadow of the Colossus (PS2)] (some .sts have manually joined header+body) + /* .sts+int: standard [Shadow of the Colossus (PS2)] (some fake .sts have manually joined header+body) * .x: header+body [Ape Escape 3 (PS2)] */ - if (!check_extensions(streamFile, "sts,x")) + if (!check_extensions(sf, "sts,x")) goto fail; - if (read_32bitBE(0x00,streamFile) != 0x45585354) /* "EXST" */ + if (!is_id32be(0x00,sf, "EXST")) goto fail; - streamBody = open_streamfile_by_ext(streamFile,"int"); - if (!streamBody) { - /* data+body joined */ - start_offset = 0x78; - if (get_streamfile_size(streamFile) < start_offset) - goto fail; - } - else { - /* body is separate */ + sf_body = open_streamfile_by_ext(sf,"int"); + if (sf_body) { + /* separate header+body (header is 0x78) */ start_offset = 0x00; } + else { + /* joint header+body */ + start_offset = 0x78; + /* Gacharoku 2 has header+data but padded header (ELF has pointers + size to SOUND.PCK, and + * treats them as single files, no extension but there are Sg2ExStAdpcm* calls in the ELF) */ + if ((get_streamfile_size(sf) % 0x10) == 0) + start_offset = 0x80; + if (get_streamfile_size(sf) < start_offset) + goto fail; + } - channel_count = read_16bitLE(0x06,streamFile); - loop_flag = read_32bitLE(0x0C,streamFile) == 1; - loop_start_block = read_32bitLE(0x10,streamFile); - num_blocks = read_32bitLE(0x14,streamFile); + channels = read_u16le(0x06,sf); + sample_rate = read_u32le(0x08,sf); + loop_flag = read_u32le(0x0C,sf) == 1; + loop_start_block = read_u32le(0x10,sf); + num_blocks = read_u32le(0x14,sf); + /* 0x18: 0x24 config per channel? (volume+panning+etc?) */ + /* rest is padding up to 0x78 */ /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); + vgmstream = allocate_vgmstream(channels, loop_flag); if (!vgmstream) goto fail; - vgmstream->sample_rate = read_32bitLE(0x08,streamFile); vgmstream->meta_type = meta_PS2_EXST; + vgmstream->sample_rate = sample_rate; vgmstream->coding_type = coding_PSX; vgmstream->layout_type = layout_interleave; vgmstream->interleave_block_size = 0x400; block_size = vgmstream->interleave_block_size * vgmstream->channels; - vgmstream->num_samples = ps_bytes_to_samples(num_blocks*block_size, channel_count); - if (vgmstream->loop_flag) { - vgmstream->loop_start_sample = ps_bytes_to_samples(loop_start_block*block_size, channel_count);; - vgmstream->loop_end_sample = vgmstream->num_samples; - } + vgmstream->num_samples = ps_bytes_to_samples(num_blocks * block_size, channels); + vgmstream->loop_start_sample = ps_bytes_to_samples(loop_start_block * block_size, channels); + vgmstream->loop_end_sample = vgmstream->num_samples; - if (!vgmstream_open_stream(vgmstream,streamBody ? streamBody : streamFile,start_offset)) + if (!vgmstream_open_stream(vgmstream, sf_body ? sf_body : sf, start_offset)) goto fail; - close_streamfile(streamBody); + close_streamfile(sf_body); return vgmstream; fail: - close_streamfile(streamBody); + close_streamfile(sf_body); close_vgmstream(vgmstream); return NULL; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ubi_bao.c b/Frameworks/vgmstream/vgmstream/src/meta/ubi_bao.c index 052fc7722..fe9375245 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ubi_bao.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ubi_bao.c @@ -9,7 +9,7 @@ typedef enum { CODEC_NONE = 0, UBI_IMA, RAW_PCM, RAW_PSX, RAW_XMA1, RAW_XMA2_OLD, RAW_XMA2_NEW, RAW_AT3, RAW_AT3_105, FMT_AT3, RAW_DSP, FMT_OGG } ubi_bao_codec; typedef enum { TYPE_NONE = 0, UBI_AUDIO, UBI_LAYER, UBI_SEQUENCE, UBI_SILENCE } ubi_bao_type; -typedef enum { FILE_NONE = 0, UBI_FORGE, UBI_FORGE_b } ubi_bao_file; +typedef enum { FILE_NONE = 0, UBI_FORGE, UBI_FORGE_b, UBI_FAT } ubi_bao_file; typedef struct { size_t bao_class; @@ -1403,6 +1403,13 @@ static STREAMFILE* open_atomic_bao(ubi_bao_file file_type, uint32_t file_id, int goto fail; + case UBI_FAT: + snprintf(buf,buf_size, "%08x.bao", file_id); + sf_bao = open_streamfile_by_filename(sf, buf); + if (sf_bao) return sf_bao; + + goto fail; + default: goto fail; } @@ -1747,6 +1754,30 @@ static int config_bao_version(ubi_bao_header* bao, STREAMFILE* sf) { bao->cfg.file_type = UBI_FORGE; return 1; + case 0x001B0200: /* Beowulf (PS3/X360)-atomic-bin+fat */ + config_bao_entry(bao, 0xA0, 0x24); + + config_bao_audio_b(bao, 0x08, 0x1c, 0x28, 0x34, 1, 1); /* 0x2c: prefetch flag? */ + config_bao_audio_m(bao, 0x44, 0x48, 0x50, 0x58, 0x64, 0x74); + bao->cfg.audio_interleave = 0x10; + bao->cfg.audio_fix_psx_samples = 1; + + config_bao_sequence(bao, 0x2c, 0x20, 0x1c, 0x14); + + config_bao_layer_m(bao, 0x4c, 0x20, 0x2c, 0x44, 0x00, 0x50, 0x00, 0x00, 1); /* stream size: 0x48? */ + config_bao_layer_e(bao, 0x30, 0x00, 0x04, 0x08, 0x10); + + config_bao_silence_f(bao, 0x1c); + + bao->cfg.codec_map[0x00] = RAW_XMA1; + bao->cfg.codec_map[0x02] = RAW_PSX; + bao->cfg.codec_map[0x03] = UBI_IMA; + bao->cfg.codec_map[0x04] = FMT_OGG; + bao->cfg.codec_map[0x07] = RAW_AT3_105; + + bao->cfg.file_type = UBI_FAT; + return 1; + case 0x001F0008: /* Rayman Raving Rabbids: TV Party (Wii)-package */ case 0x001F0010: /* Prince of Persia 2008 (PC/PS3/X360)-atomic-forge, Far Cry 2 (PS3)-atomic-dunia? */ case 0x001F0011: /* Naruto: The Broken Bond (X360)-package */ @@ -1879,9 +1910,6 @@ static int config_bao_version(ubi_bao_header* bao, STREAMFILE* sf) { bao->cfg.file_type = UBI_FORGE_b; return 1; - case 0x001B0200: /* Beowulf (PS3)-atomic-bin+fat */ - /* same as 0x001B0100 except: - * - base 0xA0, skip 0x24, name style %08x (.bao/sbao?) */ case 0x001C0000: /* Lost: Via Domus (PS3)-atomic-gear */ /* same as 0x001B0100 except: * - base 0xA0, skip 0x24, name style %08x.bao (not .sbao?) */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ubi_sb.c b/Frameworks/vgmstream/vgmstream/src/meta/ubi_sb.c index aef7d05b7..fe0f79938 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ubi_sb.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ubi_sb.c @@ -3988,9 +3988,11 @@ static int config_sb_version(ubi_sb_header* sb, STREAMFILE* sf) { return 1; } + /* Naruto: Rise of a Ninja (2007)(X360)-bank */ /* Rainbow Six Vegas 2 (2008)(PS3)-bank */ /* Rainbow Six Vegas 2 (2008)(X360)-bank */ - if ((sb->version == 0x001C0000 && sb->platform == UBI_PS3) || + if ((sb->version == 0x001B0001 && sb->platform == UBI_X360) || + (sb->version == 0x001C0000 && sb->platform == UBI_PS3) || (sb->version == 0x001C0000 && sb->platform == UBI_X360)) { config_sb_entry(sb, 0x64, 0x7c); diff --git a/Frameworks/vgmstream/vgmstream/src/meta/wwise.c b/Frameworks/vgmstream/vgmstream/src/meta/wwise.c index f689e5617..f6777fa73 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/wwise.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/wwise.c @@ -96,7 +96,8 @@ VGMSTREAM* init_vgmstream_wwise(STREAMFILE* sf) { switch(ww.codec) { case PCM: /* common */ /* normally riff.c has priority but it's needed when .wem is used */ - if (ww.fmt_size != 0x10 && ww.fmt_size != 0x18 && ww.fmt_size != 0x28) goto fail; /* old, new/Limbo (PC) */ + /* old=0x10, 0x12=Army of Two: the 40th Day (PS3), new/Limbo (PC) */ + if (ww.fmt_size != 0x10 && ww.fmt_size != 0x12 && ww.fmt_size != 0x18 && ww.fmt_size != 0x28) goto fail; if (ww.bits_per_sample != 16) goto fail; vgmstream->coding_type = (ww.big_endian ? coding_PCM16BE : coding_PCM16LE); diff --git a/Frameworks/vgmstream/vgmstream/src/meta/xnb.c b/Frameworks/vgmstream/vgmstream/src/meta/xnb.c index 0b824739b..e07059dc2 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/xnb.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/xnb.c @@ -12,13 +12,14 @@ VGMSTREAM* init_vgmstream_xnb(STREAMFILE* sf) { int big_endian, flags, codec, sample_rate, block_align, bps; size_t data_size; char platform; - int is_ogg = 0, is_at9 = 0; + int is_sound = 0, is_ogg = 0, is_at9 = 0, is_song = 0; + char song_name[255+1]; /* checks */ if (!check_extensions(sf,"xnb")) goto fail; - if ((read_32bitBE(0x00, sf) & 0xFFFFFF00) != 0x584E4200) /* "XNB" */ + if ((read_u32be(0x00, sf) & 0xFFFFFF00) != get_id32be("XNB\0")) goto fail; /* XNA Studio platforms: 'w' = Windows, 'm' = Windows Phone 7, 'x' = X360 @@ -32,9 +33,9 @@ VGMSTREAM* init_vgmstream_xnb(STREAMFILE* sf) { flags = read_u8(0x05, sf); //if (flags & 0x01) goto fail; /* "HiDef profile" content (no actual difference) */ - + /* full size */ - if (read_32bitLE(0x06, sf) != get_streamfile_size(sf)) { + if (read_u32le(0x06, sf) != get_streamfile_size(sf)) { goto fail; } @@ -59,49 +60,73 @@ VGMSTREAM* init_vgmstream_xnb(STREAMFILE* sf) { /* XNB contains "type reader" class references to parse "shared resource" data (can be any implemented filetype) */ { char reader_name[255+1]; - size_t reader_string_len; - uint32_t fmt_chunk_size; - const char* type_sound = "Microsoft.Xna.Framework.Content.SoundEffectReader"; /* partial "fmt" chunk or XMA */ - const char* type_ogg = "SoundEffectFromOggReader"; /* has extra text info after base part */ - //const char* type_song = "Microsoft.Xna.Framework.Content.SongReader"; /* references a companion .wma */ + size_t string_len; + uint8_t type_count; + const static char* type_sound = "Microsoft.Xna.Framework.Content.SoundEffectReader"; /* partial "fmt" chunk or XMA */ + const static char* type_ogg = "SoundEffectFromOggReader"; /* has extra text info after base part */ + const static char* type_song = "Microsoft.Xna.Framework.Content.SongReader"; /* references a companion .wma */ + const static char* type_int32 = "Microsoft.Xna.Framework.Content.Int32Reader"; /* extra crap */ - /* type reader count, accept only one for now */ - if (read_u8(offset++, sf_h) != 1) + type_count = read_u8(offset++, sf_h); + + /* check type reader string */ + string_len = read_u8(offset++, sf_h); /* doesn't count null */ + if (read_string(reader_name, string_len+1, offset, sf_h) != string_len) goto fail; - reader_string_len = read_u8(offset++, sf_h); /* doesn't count null */ - if (reader_string_len > 255) goto fail; - - /* check SoundEffect type string */ - if (read_string(reader_name, reader_string_len+1, offset, sf_h) != reader_string_len) + if (strcmp(reader_name, type_sound) == 0) { + if (type_count != 1) goto fail; + is_sound = 1; + } + else if (strncmp(reader_name, type_ogg, strlen(type_ogg)) == 0) { /* has extra info after base string */ + if (type_count != 1) goto fail; + is_ogg = 1; + } + else if (strcmp(reader_name, type_song) == 0) { + if (type_count != 2) goto fail; + is_song = 1; + } + else { goto fail; - if (strcmp(reader_name, type_sound) != 0) { - is_ogg = strncmp(reader_name, type_ogg, strlen(type_ogg)) == 0; - if (!is_ogg) - goto fail; } - offset += reader_string_len + 1; + offset += string_len + 1; + + if (is_song) { + offset += 3; + + string_len = read_u8(offset++, sf_h); + if (read_string(reader_name, string_len+1, offset, sf_h) != string_len) + goto fail; + + if (strcmp(reader_name, type_int32) != 0) + goto fail; + + offset += string_len + 1; + } + offset += 0x04; /* reader version, 0 */ - /* shared resource count */ + /* shared resource number 1 */ if (read_u8(offset++, sf_h) != 1) goto fail; - /* shared resource: partial "fmt" chunk */ - fmt_chunk_size = read_32bitLE(offset, sf_h); - offset += 0x04; + /* read shared resource */ + if (is_sound || is_ogg) { + /* partial "fmt" chunk */ + uint32_t (*read_u32)(off_t,STREAMFILE*) = big_endian ? read_u32be : read_u32le; + uint16_t (*read_u16)(off_t,STREAMFILE*) = big_endian ? read_u16be : read_u16le; + uint32_t fmt_chunk_size; - { - int32_t (*read_32bit)(off_t,STREAMFILE*) = big_endian ? read_32bitBE : read_32bitLE; - int16_t (*read_16bit)(off_t,STREAMFILE*) = big_endian ? read_16bitBE : read_16bitLE; + fmt_chunk_size = read_u32le(offset, sf_h); + offset += 0x04; - codec = (uint16_t)read_16bit(offset+0x00, sf_h); - channel_count = read_16bit(offset+0x02, sf_h); - sample_rate = read_32bit(offset+0x04, sf_h); + codec = read_u16(offset+0x00, sf_h); + channel_count = read_u16(offset+0x02, sf_h); + sample_rate = read_u32(offset+0x04, sf_h); /* 0x08: byte rate */ - block_align = read_16bit(offset+0x0c, sf_h); - bps = read_16bit(offset+0x0e, sf_h); + block_align = read_u16(offset+0x0c, sf_h); + bps = read_u16(offset+0x0e, sf_h); if (codec == 0x0002) { if (!msadpcm_check_coefs(sf_h, offset + 0x14)) @@ -115,30 +140,44 @@ VGMSTREAM* init_vgmstream_xnb(STREAMFILE* sf) { if (codec == 0xFFFF) { if (platform != 'S') goto fail; - sample_rate = read_32bit(offset+fmt_chunk_size+0x04+0x08, sf_h); + sample_rate = read_u32(offset+fmt_chunk_size+0x04+0x08, sf_h); } /* mini-fmt has AT9 stuff then a regular RIFF [Square Heroes (PS4)] */ if (codec == 0xFFFE) { is_at9 = 1; } - - /* regular (with loop tags) Ogg poses as PCM [Little Savior (PC)] */ + + /* Ogg (with loop tags) poses as PCM [Little Savior (PC)] */ + + offset += fmt_chunk_size; + + data_size = read_u32le(offset, sf_h); + offset += 0x04; + + start_offset = offset; } + else if (is_song) { + /* filename (typically same as .xnb but .wma) */ + string_len = read_u8(offset++, sf_h); - offset += fmt_chunk_size; + if (read_string(song_name, string_len+1, offset, sf_h) != string_len + 1) + goto fail; - data_size = read_32bitLE(offset, sf_h); - offset += 0x04; - - start_offset = offset; + start_offset = 0; + data_size = 0; + /* after name is shared resource number 1 + 32b int (durationMs?) */ + } + else { + goto fail; + } } /* container handling */ if (is_ogg || is_at9) { STREAMFILE* temp_sf = NULL; const char* fake_ext = is_ogg ? "ogg" : "at9"; - + /* after data_size is loop start + loop length and offset? (same as loop tags), 0 if not enabled */ temp_sf = setup_subfile_streamfile(sf_h, start_offset, data_size, fake_ext); @@ -158,6 +197,31 @@ VGMSTREAM* init_vgmstream_xnb(STREAMFILE* sf) { if (sf_h != sf) close_streamfile(sf_h); return vgmstream; } + else if (is_song) { + STREAMFILE* sf_body = open_streamfile_by_filename(sf, song_name); + if (!sf_body) goto fail; + + if (read_u32be(0x00, sf_body) == 0x01000080) { + STREAMFILE* temp_sf = setup_subfile_streamfile(sf_body, 0x00, get_streamfile_size(sf_body), "opus"); + if (!temp_sf) goto fail; + + /* MonoGame with NXOpus [Clan N (Switch)] */ + vgmstream = init_vgmstream_opus_std(temp_sf); + close_streamfile(temp_sf); + } + else { +#ifdef VGM_USE_FFMPEG + /* XNA with WMA [Guncraft: Blocked and Loaded (X360)] */ + vgmstream = init_vgmstream_ffmpeg(sf_body); +#endif + } + close_streamfile(sf_body); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_XNB; + if (sf_h != sf) close_streamfile(sf_h); + return vgmstream; + } /* build the VGMSTREAM */ @@ -221,7 +285,7 @@ VGMSTREAM* init_vgmstream_xnb(STREAMFILE* sf) { vgmstream->coding_type = coding_NGC_DSP; vgmstream->layout_type = layout_interleave; vgmstream->interleave_block_size = data_size / channel_count; - vgmstream->num_samples = read_32bitLE(start_offset + 0x00, sf_h); + vgmstream->num_samples = read_s32le(start_offset + 0x00, sf_h); //vgmstream->num_samples = dsp_bytes_to_samples(data_size - 0x60*channel_count, channel_count); dsp_read_coefs(vgmstream, sf_h, start_offset + 0x1c, vgmstream->interleave_block_size, big_endian); diff --git a/Frameworks/vgmstream/vgmstream/src/streamfile.c b/Frameworks/vgmstream/vgmstream/src/streamfile.c index 9e7cf0042..84530db1b 100644 --- a/Frameworks/vgmstream/vgmstream/src/streamfile.c +++ b/Frameworks/vgmstream/vgmstream/src/streamfile.c @@ -861,15 +861,15 @@ STREAMFILE* open_multifile_streamfile_f(STREAMFILE **streamfiles, size_t streamf /* **************************************************** */ -STREAMFILE* open_streamfile(STREAMFILE *streamfile, const char *pathname) { - return streamfile->open(streamfile, pathname, STREAMFILE_DEFAULT_BUFFER_SIZE); +STREAMFILE* open_streamfile(STREAMFILE* sf, const char* pathname) { + return sf->open(sf, pathname, STREAMFILE_DEFAULT_BUFFER_SIZE); } -STREAMFILE* open_streamfile_by_ext(STREAMFILE *streamfile, const char *ext) { +STREAMFILE* open_streamfile_by_ext(STREAMFILE* sf, const char* ext) { char filename[PATH_LIMIT]; int filename_len, fileext_len; - streamfile->get_name(streamfile, filename, sizeof(filename)); + sf->get_name(sf, filename, sizeof(filename)); filename_len = strlen(filename); fileext_len = strlen(filename_extension(filename)); @@ -882,17 +882,17 @@ STREAMFILE* open_streamfile_by_ext(STREAMFILE *streamfile, const char *ext) { strcpy(filename + filename_len - fileext_len, ext); } - return streamfile->open(streamfile, filename, STREAMFILE_DEFAULT_BUFFER_SIZE); + return sf->open(sf, filename, STREAMFILE_DEFAULT_BUFFER_SIZE); } -STREAMFILE* open_streamfile_by_filename(STREAMFILE *streamfile, const char * filename) { +STREAMFILE* open_streamfile_by_filename(STREAMFILE* sf, const char* filename) { char fullname[PATH_LIMIT]; char partname[PATH_LIMIT]; char *path, *name; - if (!streamfile || !filename || !filename[0]) return NULL; + if (!sf || !filename || !filename[0]) return NULL; - streamfile->get_name(streamfile, fullname, sizeof(fullname)); + sf->get_name(sf, fullname, sizeof(fullname)); //todo normalize separators in a better way, safeops, improve copying path = strrchr(fullname,DIR_SEPARATOR); @@ -903,10 +903,10 @@ STREAMFILE* open_streamfile_by_filename(STREAMFILE *streamfile, const char * fil fix_dir_separators(partname); /* normalize relative paths as don't work ok in some plugins */ - if (partname[0]=='.' && partname[1] == DIR_SEPARATOR) { /* './name' */ + if (partname[0] == '.' && partname[1] == DIR_SEPARATOR) { /* './name' */ name = partname + 2; /* ignore './' */ } - else if (partname[0]=='.' && partname[1]=='.' && partname[2] == DIR_SEPARATOR) { /* '../name' */ + else if (partname[0] == '.' && partname[1] == '.' && partname[2] == DIR_SEPARATOR) { /* '../name' */ char *pathprev; path[0] = '\0'; /* remove last separator so next call works */ @@ -931,23 +931,23 @@ STREAMFILE* open_streamfile_by_filename(STREAMFILE *streamfile, const char * fil strcpy(fullname, filename); } - return streamfile->open(streamfile, fullname, STREAMFILE_DEFAULT_BUFFER_SIZE); + return sf->open(sf, fullname, STREAMFILE_DEFAULT_BUFFER_SIZE); } -STREAMFILE* reopen_streamfile(STREAMFILE *streamfile, size_t buffer_size) { +STREAMFILE* reopen_streamfile(STREAMFILE* sf, size_t buffer_size) { char pathname[PATH_LIMIT]; - if (!streamfile) return NULL; + if (!sf) return NULL; if (buffer_size == 0) buffer_size = STREAMFILE_DEFAULT_BUFFER_SIZE; - streamfile->get_name(streamfile,pathname,sizeof(pathname)); - return streamfile->open(streamfile,pathname,buffer_size); + sf->get_name(sf, pathname,sizeof(pathname)); + return sf->open(sf, pathname, buffer_size); } /* **************************************************** */ -size_t read_line(char *buf, int buf_size, off_t offset, STREAMFILE *sf, int *p_line_ok) { +size_t read_line(char* buf, int buf_size, off_t offset, STREAMFILE* sf, int* p_line_ok) { int i; off_t file_size = get_streamfile_size(sf); int extra_bytes = 0; /* how many bytes over those put in the buffer were read */ @@ -996,7 +996,7 @@ size_t read_line(char *buf, int buf_size, off_t offset, STREAMFILE *sf, int *p_l return i + extra_bytes; } -size_t read_string(char *buf, size_t buf_size, off_t offset, STREAMFILE *sf) { +size_t read_string(char* buf, size_t buf_size, off_t offset, STREAMFILE* sf) { size_t pos; for (pos = 0; pos < buf_size; pos++) { @@ -1019,7 +1019,7 @@ fail: return 0; } -size_t read_string_utf16(char *buf, size_t buf_size, off_t offset, STREAMFILE *sf, int big_endian) { +size_t read_string_utf16(char* buf, size_t buf_size, off_t offset, STREAMFILE* sf, int big_endian) { size_t pos, offpos; uint16_t (*read_u16)(off_t,STREAMFILE*) = big_endian ? read_u16be : read_u16le; @@ -1049,16 +1049,16 @@ size_t read_string_utf16be(char* buf, size_t buf_size, off_t offset, STREAMFILE* return read_string_utf16(buf, buf_size, offset, sf, 1); } +/* ************************************************************************* */ - -size_t read_key_file(uint8_t *buf, size_t buf_size, STREAMFILE *sf) { +size_t read_key_file(uint8_t* buf, size_t buf_size, STREAMFILE* sf) { char keyname[PATH_LIMIT]; char filename[PATH_LIMIT]; const char *path, *ext; - STREAMFILE * streamFileKey = NULL; + STREAMFILE* sf_key = NULL; size_t keysize; - sf->get_name(sf,filename,sizeof(filename)); + get_streamfile_name(sf, filename, sizeof(filename)); if (strlen(filename)+4 > sizeof(keyname)) goto fail; @@ -1067,20 +1067,20 @@ size_t read_key_file(uint8_t *buf, size_t buf_size, STREAMFILE *sf) { ext = strrchr(filename,'.'); if (ext!=NULL) ext = ext+1; - path = strrchr(filename,DIR_SEPARATOR); + path = strrchr(filename, DIR_SEPARATOR); if (path!=NULL) path = path+1; /* "(name.ext)key" */ strcpy(keyname, filename); strcat(keyname, "key"); - streamFileKey = sf->open(sf,keyname,STREAMFILE_DEFAULT_BUFFER_SIZE); - if (streamFileKey) goto found; + sf_key = sf->open(sf, keyname, STREAMFILE_DEFAULT_BUFFER_SIZE); + if (sf_key) goto found; /* "(name.ext)KEY" */ /* strcpy(keyname+strlen(keyname)-3,"KEY"); - streamFileKey = streamFile->open(streamFile,keyname,STREAMFILE_DEFAULT_BUFFER_SIZE); - if (streamFileKey) goto found; + sf_key = sf->open(sf, keyname, STREAMFILE_DEFAULT_BUFFER_SIZE); + if (sf_key) goto found; */ @@ -1094,38 +1094,38 @@ size_t read_key_file(uint8_t *buf, size_t buf_size, STREAMFILE *sf) { } if (ext) strcat(keyname, ext); strcat(keyname, "key"); - streamFileKey = sf->open(sf,keyname,STREAMFILE_DEFAULT_BUFFER_SIZE); - if (streamFileKey) goto found; + sf_key = sf->open(sf, keyname, STREAMFILE_DEFAULT_BUFFER_SIZE); + if (sf_key) goto found; /* "(.ext)KEY" */ /* strcpy(keyname+strlen(keyname)-3,"KEY"); - streamFileKey = streamFile->open(streamFile,keyname,STREAMFILE_DEFAULT_BUFFER_SIZE); - if (streamFileKey) goto found; + sf_key = sf->open(sf, keyname, STREAMFILE_DEFAULT_BUFFER_SIZE); + if (sf_key) goto found; */ goto fail; } found: - keysize = get_streamfile_size(streamFileKey); + keysize = get_streamfile_size(sf_key); if (keysize > buf_size) goto fail; - if (read_streamfile(buf, 0, keysize, streamFileKey) != keysize) + if (read_streamfile(buf, 0, keysize, sf_key) != keysize) goto fail; - close_streamfile(streamFileKey); + close_streamfile(sf_key); return keysize; fail: - close_streamfile(streamFileKey); + close_streamfile(sf_key); return 0; } -STREAMFILE *read_filemap_file(STREAMFILE *sf, int file_num) { +STREAMFILE* read_filemap_file(STREAMFILE* sf, int file_num) { char filename[PATH_LIMIT]; off_t txt_offset, file_size; - STREAMFILE *sf_map = NULL; + STREAMFILE* sf_map = NULL; sf_map = open_streamfile_by_filename(sf, ".txtm"); if (!sf_map) goto fail; @@ -1163,7 +1163,7 @@ STREAMFILE *read_filemap_file(STREAMFILE *sf, int file_num) { if (strcmp(key, filename) == 0) { int n; char subval[PATH_LIMIT]; - const char *current = val; + const char* current = val; int i; for (i = 0; i <= file_num; i++) { @@ -1192,7 +1192,7 @@ fail: return NULL; } -void fix_dir_separators(char * filename) { +void fix_dir_separators(char* filename) { char c; int i = 0; while ((c = filename[i]) != '\0') { @@ -1202,11 +1202,13 @@ void fix_dir_separators(char * filename) { } } -int check_extensions(STREAMFILE *sf, const char * cmp_exts) { +/* ************************************************************************* */ + +int check_extensions(STREAMFILE* sf, const char* cmp_exts) { char filename[PATH_LIMIT]; - const char * ext = NULL; - const char * cmp_ext = NULL; - const char * ststr_res = NULL; + const char* ext = NULL; + const char* cmp_ext = NULL; + const char* ststr_res = NULL; size_t ext_len, cmp_len; sf->get_name(sf,filename,sizeof(filename)); @@ -1232,6 +1234,7 @@ int check_extensions(STREAMFILE *sf, const char * cmp_exts) { return 0; } +/* ************************************************************************* */ /** * Find a chunk starting from an offset, and save its offset/size (if not NULL), with offset after id/size. @@ -1241,11 +1244,11 @@ int check_extensions(STREAMFILE *sf, const char * cmp_exts) { * * returns 0 on failure */ -static int find_chunk_internal(STREAMFILE *streamFile, uint32_t chunk_id, off_t start_offset, size_t max_size, int full_chunk_size, off_t *out_chunk_offset, size_t *out_chunk_size, int big_endian_type, int big_endian_size, int zero_size_end) { +static int find_chunk_internal(STREAMFILE* sf, uint32_t chunk_id, off_t start_offset, size_t max_size, int full_chunk_size, off_t *out_chunk_offset, size_t *out_chunk_size, int big_endian_type, int big_endian_size, int zero_size_end) { int32_t (*read_32bit_type)(off_t,STREAMFILE*) = big_endian_type ? read_32bitBE : read_32bitLE; int32_t (*read_32bit_size)(off_t,STREAMFILE*) = big_endian_size ? read_32bitBE : read_32bitLE; off_t offset, max_offset; - size_t file_size = get_streamfile_size(streamFile); + size_t file_size = get_streamfile_size(sf); if (max_size == 0) max_size = file_size; @@ -1258,8 +1261,8 @@ static int find_chunk_internal(STREAMFILE *streamFile, uint32_t chunk_id, off_t /* read chunks */ while (offset < max_offset) { - uint32_t chunk_type = read_32bit_type(offset + 0x00,streamFile); - uint32_t chunk_size = read_32bit_size(offset + 0x04,streamFile); + uint32_t chunk_type = read_32bit_type(offset + 0x00,sf); + uint32_t chunk_size = read_32bit_size(offset + 0x04,sf); //;VGM_LOG("CHUNK: type=%x, size=%x at %lx\n", chunk_type, chunk_size, offset); if (chunk_type == 0xFFFFFFFF || chunk_size == 0xFFFFFFFF) @@ -1280,36 +1283,39 @@ static int find_chunk_internal(STREAMFILE *streamFile, uint32_t chunk_id, off_t return 0; } -int find_chunk_be(STREAMFILE *streamFile, uint32_t chunk_id, off_t start_offset, int full_chunk_size, off_t *out_chunk_offset, size_t *out_chunk_size) { - return find_chunk(streamFile, chunk_id, start_offset, full_chunk_size, out_chunk_offset, out_chunk_size, 1, 0); +int find_chunk_be(STREAMFILE* sf, uint32_t chunk_id, off_t start_offset, int full_chunk_size, off_t *out_chunk_offset, size_t *out_chunk_size) { + return find_chunk(sf, chunk_id, start_offset, full_chunk_size, out_chunk_offset, out_chunk_size, 1, 0); } -int find_chunk_le(STREAMFILE *streamFile, uint32_t chunk_id, off_t start_offset, int full_chunk_size, off_t *out_chunk_offset, size_t *out_chunk_size) { - return find_chunk(streamFile, chunk_id, start_offset, full_chunk_size, out_chunk_offset, out_chunk_size, 0, 0); +int find_chunk_le(STREAMFILE* sf, uint32_t chunk_id, off_t start_offset, int full_chunk_size, off_t *out_chunk_offset, size_t *out_chunk_size) { + return find_chunk(sf, chunk_id, start_offset, full_chunk_size, out_chunk_offset, out_chunk_size, 0, 0); } -int find_chunk(STREAMFILE *streamFile, uint32_t chunk_id, off_t start_offset, int full_chunk_size, off_t *out_chunk_offset, size_t *out_chunk_size, int big_endian_size, int zero_size_end) { - return find_chunk_internal(streamFile, chunk_id, start_offset, 0, full_chunk_size, out_chunk_offset, out_chunk_size, 1, big_endian_size, zero_size_end); +int find_chunk(STREAMFILE* sf, uint32_t chunk_id, off_t start_offset, int full_chunk_size, off_t *out_chunk_offset, size_t *out_chunk_size, int big_endian_size, int zero_size_end) { + return find_chunk_internal(sf, chunk_id, start_offset, 0, full_chunk_size, out_chunk_offset, out_chunk_size, 1, big_endian_size, zero_size_end); } -int find_chunk_riff_le(STREAMFILE *streamFile, uint32_t chunk_id, off_t start_offset, size_t max_size, off_t *out_chunk_offset, size_t *out_chunk_size) { - return find_chunk_internal(streamFile, chunk_id, start_offset, max_size, 0, out_chunk_offset, out_chunk_size, 1, 0, 0); +int find_chunk_riff_le(STREAMFILE* sf, uint32_t chunk_id, off_t start_offset, size_t max_size, off_t *out_chunk_offset, size_t *out_chunk_size) { + return find_chunk_internal(sf, chunk_id, start_offset, max_size, 0, out_chunk_offset, out_chunk_size, 1, 0, 0); } -int find_chunk_riff_be(STREAMFILE *streamFile, uint32_t chunk_id, off_t start_offset, size_t max_size, off_t *out_chunk_offset, size_t *out_chunk_size) { - return find_chunk_internal(streamFile, chunk_id, start_offset, max_size, 0, out_chunk_offset, out_chunk_size, 1, 1, 0); +int find_chunk_riff_be(STREAMFILE* sf, uint32_t chunk_id, off_t start_offset, size_t max_size, off_t *out_chunk_offset, size_t *out_chunk_size) { + return find_chunk_internal(sf, chunk_id, start_offset, max_size, 0, out_chunk_offset, out_chunk_size, 1, 1, 0); } -int find_chunk_riff_ve(STREAMFILE *streamFile, uint32_t chunk_id, off_t start_offset, size_t max_size, off_t *out_chunk_offset, size_t *out_chunk_size, int big_endian) { - return find_chunk_internal(streamFile, chunk_id, start_offset, max_size, 0, out_chunk_offset, out_chunk_size, big_endian, big_endian, 0); +int find_chunk_riff_ve(STREAMFILE* sf, uint32_t chunk_id, off_t start_offset, size_t max_size, off_t *out_chunk_offset, size_t *out_chunk_size, int big_endian) { + return find_chunk_internal(sf, chunk_id, start_offset, max_size, 0, out_chunk_offset, out_chunk_size, big_endian, big_endian, 0); } +/* ************************************************************************* */ + /* copies name as-is (may include full path included) */ -void get_streamfile_name(STREAMFILE *streamFile, char * buffer, size_t size) { - streamFile->get_name(streamFile,buffer,size); +void get_streamfile_name(STREAMFILE* sf, char* buffer, size_t size) { + sf->get_name(sf, buffer, size); } + /* copies the filename without path */ -void get_streamfile_filename(STREAMFILE *streamFile, char * buffer, size_t size) { +void get_streamfile_filename(STREAMFILE* sf, char* buffer, size_t size) { char foldername[PATH_LIMIT]; - const char *path; + const char* path; - streamFile->get_name(streamFile,foldername,sizeof(foldername)); + get_streamfile_name(sf, foldername, sizeof(foldername)); //todo Windows CMD accepts both \\ and /, better way to handle this? path = strrchr(foldername,'\\'); @@ -1325,22 +1331,24 @@ void get_streamfile_filename(STREAMFILE *streamFile, char * buffer, size_t size) strcpy(buffer, foldername); } } -/* copies the filename without path or extension */ -void get_streamfile_basename(STREAMFILE *streamFile, char * buffer, size_t size) { - char *ext; - get_streamfile_filename(streamFile,buffer,size); +/* copies the filename without path or extension */ +void get_streamfile_basename(STREAMFILE* sf, char* buffer, size_t size) { + char* ext; + + get_streamfile_filename(sf, buffer, size); ext = strrchr(buffer,'.'); if (ext) { ext[0] = '\0'; /* remove .ext from buffer */ } } -/* copies path removing name (NULL when if filename has no path) */ -void get_streamfile_path(STREAMFILE *streamFile, char * buffer, size_t size) { - const char *path; - streamFile->get_name(streamFile,buffer,size); +/* copies path removing name (NULL when if filename has no path) */ +void get_streamfile_path(STREAMFILE* sf, char* buffer, size_t size) { + const char* path; + + get_streamfile_name(sf, buffer, size); path = strrchr(buffer,DIR_SEPARATOR); if (path!=NULL) path = path+1; /* includes "/" */ @@ -1351,34 +1359,47 @@ void get_streamfile_path(STREAMFILE *streamFile, char * buffer, size_t size) { buffer[0] = '\0'; } } -void get_streamfile_ext(STREAMFILE *streamFile, char * filename, size_t size) { - streamFile->get_name(streamFile,filename,size); - strcpy(filename, filename_extension(filename)); + +/* copies extension only */ +void get_streamfile_ext(STREAMFILE* sf, char* buffer, size_t size) { + char filename[PATH_LIMIT]; + const char* extension = NULL; + + get_streamfile_name(sf, filename, sizeof(filename)); + extension = filename_extension(filename); + if (!extension) { + buffer[0] = '\n'; + } + else { + strncpy(buffer, extension, size); //todo use something better + } } +/* ************************************************************************* */ + /* debug util, mainly for custom IO testing */ -void dump_streamfile(STREAMFILE *streamFile, int num) { +void dump_streamfile(STREAMFILE* sf, int num) { #ifdef VGM_DEBUG_OUTPUT off_t offset = 0; - FILE *f = NULL; + FILE* f = NULL; if (num >= 0) { char filename[PATH_LIMIT]; char dumpname[PATH_LIMIT]; - get_streamfile_filename(streamFile, filename, PATH_LIMIT); + get_streamfile_filename(sf, filename, PATH_LIMIT); snprintf(dumpname,PATH_LIMIT, "%s_%02i.dump", filename, num); f = fopen(dumpname,"wb"); if (!f) return; } - VGM_LOG("dump streamfile: size %x\n", get_streamfile_size(streamFile)); - while (offset < get_streamfile_size(streamFile)) { + VGM_LOG("dump streamfile: size %x\n", get_streamfile_size(sf)); + while (offset < get_streamfile_size(sf)) { uint8_t buffer[0x8000]; size_t read; - read = read_streamfile(buffer,offset,0x8000,streamFile); + read = read_streamfile(buffer,offset,0x8000,sf); if(!read) { VGM_LOG("dump streamfile: can't read at %lx\n", offset); break; diff --git a/Frameworks/vgmstream/vgmstream/src/vgmstream.c b/Frameworks/vgmstream/vgmstream/src/vgmstream.c index dac2703c4..bcf888c07 100644 --- a/Frameworks/vgmstream/vgmstream/src/vgmstream.c +++ b/Frameworks/vgmstream/vgmstream/src/vgmstream.c @@ -516,6 +516,7 @@ VGMSTREAM* (*init_vgmstream_functions[])(STREAMFILE* sf) = { init_vgmstream_dsp_wiiadpcm, init_vgmstream_dsp_cwac, init_vgmstream_ifs, + init_vgmstream_acx, /* 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 */