diff --git a/Frameworks/vgmstream/libvgmstream.xcodeproj/project.pbxproj b/Frameworks/vgmstream/libvgmstream.xcodeproj/project.pbxproj index e3dd0a6c1..e79dcd016 100644 --- a/Frameworks/vgmstream/libvgmstream.xcodeproj/project.pbxproj +++ b/Frameworks/vgmstream/libvgmstream.xcodeproj/project.pbxproj @@ -50,7 +50,6 @@ 8306B0E020984590000302D4 /* ppst_streamfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 8306B0C52098458D000302D4 /* ppst_streamfile.h */; }; 8306B0E120984590000302D4 /* ubi_lyn_ogg_streamfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 8306B0C62098458D000302D4 /* ubi_lyn_ogg_streamfile.h */; }; 8306B0E220984590000302D4 /* smv.c in Sources */ = {isa = PBXBuildFile; fileRef = 8306B0C72098458D000302D4 /* smv.c */; }; - 8306B0E320984590000302D4 /* aax_utf.h in Headers */ = {isa = PBXBuildFile; fileRef = 8306B0C82098458D000302D4 /* aax_utf.h */; }; 8306B0E420984590000302D4 /* wave.c in Sources */ = {isa = PBXBuildFile; fileRef = 8306B0C92098458E000302D4 /* wave.c */; }; 8306B0E520984590000302D4 /* ubi_lyn.c in Sources */ = {isa = PBXBuildFile; fileRef = 8306B0CA2098458E000302D4 /* ubi_lyn.c */; }; 8306B0E620984590000302D4 /* msb_msh.c in Sources */ = {isa = PBXBuildFile; fileRef = 8306B0CB2098458E000302D4 /* msb_msh.c */; }; @@ -567,6 +566,10 @@ 83F0AA6121E2028C004BBC04 /* vsv.c in Sources */ = {isa = PBXBuildFile; fileRef = 83F0AA5E21E2028C004BBC04 /* vsv.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 */; }; + 83FC176E23AC58D100E1025F /* csb.c in Sources */ = {isa = PBXBuildFile; fileRef = 83FC176B23AC58D100E1025F /* csb.c */; }; + 83FC176F23AC58D100E1025F /* cri_utf.h in Headers */ = {isa = PBXBuildFile; fileRef = 83FC176C23AC58D100E1025F /* cri_utf.h */; }; + 83FC177123AC59A800E1025F /* cri_utf.c in Sources */ = {isa = PBXBuildFile; fileRef = 83FC177023AC59A800E1025F /* cri_utf.c */; }; 83FF0EBC1E93282100C58054 /* wwise.c in Sources */ = {isa = PBXBuildFile; fileRef = 83FF0EBB1E93282100C58054 /* wwise.c */; }; /* End PBXBuildFile section */ @@ -739,7 +742,6 @@ 8306B0C52098458D000302D4 /* ppst_streamfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ppst_streamfile.h; sourceTree = ""; }; 8306B0C62098458D000302D4 /* ubi_lyn_ogg_streamfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ubi_lyn_ogg_streamfile.h; sourceTree = ""; }; 8306B0C72098458D000302D4 /* smv.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = smv.c; sourceTree = ""; }; - 8306B0C82098458D000302D4 /* aax_utf.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = aax_utf.h; sourceTree = ""; }; 8306B0C92098458E000302D4 /* wave.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = wave.c; sourceTree = ""; }; 8306B0CA2098458E000302D4 /* ubi_lyn.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ubi_lyn.c; sourceTree = ""; }; 8306B0CB2098458E000302D4 /* msb_msh.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = msb_msh.c; sourceTree = ""; }; @@ -1253,6 +1255,10 @@ 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 = ""; }; + 83FC176A23AC58D100E1025F /* xma_ue3.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xma_ue3.c; sourceTree = ""; }; + 83FC176B23AC58D100E1025F /* csb.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = csb.c; sourceTree = ""; }; + 83FC176C23AC58D100E1025F /* cri_utf.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cri_utf.h; sourceTree = ""; }; + 83FC177023AC59A800E1025F /* cri_utf.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = cri_utf.c; sourceTree = ""; }; 83FF0EBB1E93282100C58054 /* wwise.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = wwise.c; sourceTree = ""; }; /* End PBXFileReference section */ @@ -1543,7 +1549,6 @@ 83C727FE22BC893900678B4A /* 9tav.c */, 8351F32A2212B57000A606E4 /* 208.c */, 834FE0C8215C79E7000A5D3D /* a2m.c */, - 8306B0C82098458D000302D4 /* aax_utf.h */, 836F6E2A18BDC2180095E648 /* aax.c */, 837CEAD523487E8300E62A4A /* acb_utf.h */, 837CEAD623487E8300E62A4A /* acb.c */, @@ -1590,6 +1595,9 @@ 8306B0CF2098458F000302D4 /* caf.c */, 836F6E3B18BDC2180095E648 /* capdsp.c */, 834FE0E8215C79EC000A5D3D /* ck.c */, + 83FC177023AC59A800E1025F /* cri_utf.c */, + 83FC176C23AC58D100E1025F /* cri_utf.h */, + 83FC176B23AC58D100E1025F /* csb.c */, 834FE0D8215C79EA000A5D3D /* csmp.c */, 836F6E3C18BDC2180095E648 /* Cstr.c */, 836F6E3D18BDC2180095E648 /* dc_asd.c */, @@ -1919,6 +1927,7 @@ 837CEAEE23487F2C00E62A4A /* xavs_streamfile.h */, 837CEAE423487F2A00E62A4A /* xavs.c */, 836F6F0C18BDC2190095E648 /* xbox_ims.c */, + 83FC176A23AC58D100E1025F /* xma_ue3.c */, 8350C0541E071881009E0A93 /* xma.c */, 834FE0DC215C79EA000A5D3D /* xmd.c */, 837CEAEC23487F2C00E62A4A /* xmu.c */, @@ -1992,6 +2001,7 @@ 8351F32E2212B57000A606E4 /* ubi_bao_streamfile.h in Headers */, 8349A9111FE6258200E26435 /* bar_streamfile.h in Headers */, 836F6F2718BDC2190095E648 /* g72x_state.h in Headers */, + 83FC176F23AC58D100E1025F /* cri_utf.h in Headers */, 83C7282822BC8C1500678B4A /* mixing.h in Headers */, 832BF82C21E0514B006F50F1 /* hca_keys_awb.h in Headers */, 8306B0E020984590000302D4 /* ppst_streamfile.h in Headers */, @@ -2013,7 +2023,6 @@ 837CEAD923487E8300E62A4A /* acb_utf.h in Headers */, 837CEADB23487E8300E62A4A /* bgw_streamfile.h in Headers */, 837CEB0323487F2C00E62A4A /* xavs_streamfile.h in Headers */, - 8306B0E320984590000302D4 /* aax_utf.h in Headers */, 8306B0E120984590000302D4 /* ubi_lyn_ogg_streamfile.h in Headers */, 836F705918BDC2190095E648 /* vgmstream.h in Headers */, 83C7281522BC893D00678B4A /* txth_streamfile.h in Headers */, @@ -2251,6 +2260,7 @@ 83AA5D161F6E2F600020821C /* ea_xa_decoder.c in Sources */, 836F6F9B18BDC2190095E648 /* mn_str.c in Sources */, 832BF82821E0514B006F50F1 /* xwma.c in Sources */, + 83FC176E23AC58D100E1025F /* csb.c in Sources */, 8306B0EB20984590000302D4 /* wave_segmented.c in Sources */, 836F6F9F18BDC2190095E648 /* musc.c in Sources */, 8349A9121FE6258200E26435 /* vsf_tta.c in Sources */, @@ -2472,6 +2482,7 @@ 836F6F9918BDC2190095E648 /* maxis_xa.c in Sources */, 836F702118BDC2190095E648 /* rs03.c in Sources */, 836F6F8818BDC2190095E648 /* fsb.c in Sources */, + 83FC176D23AC58D100E1025F /* xma_ue3.c in Sources */, 836F6FE518BDC2190095E648 /* ps2_lpcm.c in Sources */, 8375737321F9507D00F01AF5 /* oki_decoder.c in Sources */, 836F6FB318BDC2190095E648 /* ngc_lps.c in Sources */, @@ -2588,6 +2599,7 @@ 836F6F9118BDC2190095E648 /* ios_psnd.c in Sources */, 836F700618BDC2190095E648 /* ps2_vgs.c in Sources */, 834FE10F215C79ED000A5D3D /* sdf.c in Sources */, + 83FC177123AC59A800E1025F /* cri_utf.c in Sources */, 834FE0FF215C79ED000A5D3D /* sqex_scd_sscf.c in Sources */, 836F6FAA18BDC2190095E648 /* ngc_bh2pcm.c in Sources */, 831BA6211EAC61A500CF89B0 /* x360_pasx.c in Sources */, diff --git a/Frameworks/vgmstream/vgmstream/src/coding/mpeg_custom_utils.c b/Frameworks/vgmstream/vgmstream/src/coding/mpeg_custom_utils.c index 8a2d47c0b..88eb711ff 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/mpeg_custom_utils.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/mpeg_custom_utils.c @@ -365,41 +365,50 @@ size_t mpeg_get_samples(STREAMFILE *sf, off_t start_offset, size_t bytes) { } /* detect Xing header (disguised as a normal frame) */ - if (frames < 3 && /* should be first after tags */ - info.frame_size >= 0x24 + 0x78 && - read_u32be(offset + 0x04, sf) == 0 && - (read_u32be(offset + 0x24, sf) == 0x58696E67 || /* "Xing" (mainly for VBR) */ - read_u32be(offset + 0x24, sf) == 0x496E666F)) { /* "Info" (mainly for CBR) */ - uint32_t flags = read_u32be(offset + 0x28, sf); + if (frames < 3) { /* should be first after tags */ + /* frame is empty so Xing goes after MPEG side info */ + off_t xing_offset; + if (info.version == 1) + xing_offset = (info.channels == 2 ? 0x20 : 0x11) + 0x04; + else + xing_offset = (info.channels == 2 ? 0x11 : 0x09) + 0x04; - if (flags & 1) { /* other flags indicate seek table and stuff */ - uint32_t frame_count = read_u32be(offset + 0x2c, sf); - samples = frame_count * info.frame_samples; - } + if (info.frame_size >= xing_offset + 0x78 && + read_u32be(offset + 0x04, sf) == 0 && /* empty frame */ + (read_u32be(offset + xing_offset, sf) == 0x58696E67 || /* "Xing" (mainly for VBR) */ + read_u32be(offset + xing_offset, sf) == 0x496E666F)) { /* "Info" (mainly for CBR) */ + uint32_t flags = read_u32be(offset + xing_offset + 0x04, sf); - /* vendor specific */ - if (info.frame_size > 0x24 + 0x78 + 0x24 && - read_u32be(offset + 0x9c, sf) == 0x4C414D45) { /* "LAME" */ - if (info.layer == 3) { - uint32_t delays = read_u32be(offset + 0xb0, sf); - encoder_delay = ((delays >> 12) & 0xFFF); - encoder_padding = ((delays >> 0) & 0xFFF); - - encoder_delay += (528 + 1); /* implicit MDCT decoder delay (seen in LAME source) */ - if (encoder_padding > 528 + 1) - encoder_padding -= (528 + 1); + if (flags & 1) { + uint32_t frame_count = read_u32be(offset + xing_offset + 0x08, sf); + samples = frame_count * info.frame_samples; } - else { - encoder_delay = 240 + 1; + /* other flags indicate seek table and stuff */ + + /* vendor specific */ + if (info.frame_size > xing_offset + 0x78 + 0x24 && + read_u32be(offset + xing_offset + 0x78, sf) == 0x4C414D45) { /* "LAME" */ + if (info.layer == 3) { + uint32_t delays = read_u32be(offset + xing_offset + 0x8C, sf); + encoder_delay = ((delays >> 12) & 0xFFF); + encoder_padding = ((delays >> 0) & 0xFFF); + + encoder_delay += (528 + 1); /* implicit MDCT decoder delay (seen in LAME source) */ + if (encoder_padding > 528 + 1) + encoder_padding -= (528 + 1); + } + else { + encoder_delay = 240 + 1; + } + + /* replay gain and stuff */ } - /* replay gain and stuff */ - } + /* there is also "iTunes" vendor with no apparent extra info, iTunes delays are in "iTunSMPB" ID3 tag */ - /* there is also "iTunes" vendor with no apparent extra info, iTunes delays are in "iTunSMPB" ID3 tag */ - - ;VGM_LOG("MPEG: found Xing header\n"); - break; /* we got samples */ + ;VGM_LOG("MPEG: found Xing header\n"); + break; /* we got samples */ + } } //TODO: detect "VBRI" header (Fraunhofer encoder) diff --git a/Frameworks/vgmstream/vgmstream/src/coding/psx_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/psx_decoder.c index 190ff0882..3b5b2b7ec 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/psx_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/psx_decoder.c @@ -230,7 +230,7 @@ void decode_psx_pivotal(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channe * - 0x7 (0111): End marker and don't decode * - 0x8+(1NNN): Not valid */ -static int ps_find_loop_offsets_internal(STREAMFILE *streamFile, off_t start_offset, size_t data_size, int channels, size_t interleave, int32_t * out_loop_start, int32_t * out_loop_end, int config) { +static int ps_find_loop_offsets_internal(STREAMFILE *sf, off_t start_offset, size_t data_size, int channels, size_t interleave, int32_t * p_loop_start, int32_t * p_loop_end, int config) { int num_samples = 0, loop_start = 0, loop_end = 0; int loop_start_found = 0, loop_end_found = 0; off_t offset = start_offset; @@ -243,7 +243,7 @@ static int ps_find_loop_offsets_internal(STREAMFILE *streamFile, off_t start_off return 0; while (offset < max_offset) { - uint8_t flag = (uint8_t)read_8bit(offset+0x01,streamFile) & 0x0F; /* lower nibble only (for HEVAG) */ + uint8_t flag = read_u8(offset+0x01, sf) & 0x0F; /* lower nibble only (for HEVAG) */ /* theoretically possible and would use last 0x06 */ VGM_ASSERT_ONCE(loop_start_found && flag == 0x06, "PS LOOPS: multiple loop start found at %x\n", (uint32_t)offset); @@ -260,7 +260,7 @@ static int ps_find_loop_offsets_internal(STREAMFILE *streamFile, off_t start_off /* ignore strange case in Commandos (PS2), has many loop starts and ends */ if (channels == 1 && offset + 0x10 < max_offset - && ((uint8_t)read_8bit(offset+0x11,streamFile) & 0x0F) == 0x06) { + && (read_u8(offset + 0x11, sf) & 0x0F) == 0x06) { loop_end = 0; loop_end_found = 0; } @@ -271,25 +271,22 @@ static int ps_find_loop_offsets_internal(STREAMFILE *streamFile, off_t start_off /* hack for some games that don't have loop points but do full loops, * if there is a "partial" 0x07 end flag pretend it wants to loop - * (sometimes this will loop non-looping tracks, and won't loop all repeating files) */ + * (sometimes this will loop non-looping tracks, and won't loop all repeating files) + * seems only used in Ratchet & Clank series and Ecco the Dolphin */ if (flag == 0x01 && detect_full_loops) { - static const uint8_t eof1[0x10] = {0x00,0x07,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77}; /* common */ - static const uint8_t eof2[0x10] = {0x00,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; - //static const uint8_t eofx[0x10] = {0x07,0x00,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77}; /* sometimes loops */ - //static const uint8_t eofx[0x10] = {0xNN,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; /* sometimes loops */ + static const uint8_t eof[0x10] = {0xFF,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; uint8_t buf[0x10]; + uint8_t hdr = read_u8(offset + 0x00, sf); - int read = read_streamfile(buf,offset+0x10,0x10,streamFile); - + int read = read_streamfile(buf, offset+0x10, sizeof(buf), sf); if (read > 0 - /* also test some extra stuff */ - && buf[0] != 0x00 /* skip padding */ - && buf[0] != 0x0c - && buf[0] != 0x3c /* skip Ecco the Dolphin (PS2), Ratchet & Clank 2 (PS2), lame hack */ + && buf[0] != 0x00 /* ignore blank frame */ + && buf[0] != 0x0c /* ignore silent frame */ + && buf[0] != 0x3c /* ignore some L-R tracks with different end flags */ ) { - /* assume full loop if there isn't an EOF tag after current frame */ - if (memcmp(buf,eof1,0x10) != 0 && memcmp(buf,eof2,0x10) != 0) { + /* assume full loop with repeated frame header and null frame */ + if (hdr == buf[0] && memcmp(buf+1, eof+1, sizeof(buf) - 1) == 0) { loop_start = 28; /* skip first frame as it's null in PS-ADPCM */ loop_end = num_samples + 28; /* loop end after this frame */ loop_start_found = 1; @@ -318,20 +315,20 @@ static int ps_find_loop_offsets_internal(STREAMFILE *streamFile, off_t start_off /* From Sony's docs: if only loop_end is set loop back to "phoneme region start", but in practice doesn't */ if (loop_start_found && loop_end_found) { - *out_loop_start = loop_start; - *out_loop_end = loop_end; + *p_loop_start = loop_start; + *p_loop_end = loop_end; return 1; } return 0; /* no loop */ } -int ps_find_loop_offsets(STREAMFILE *streamFile, off_t start_offset, size_t data_size, int channels, size_t interleave, int32_t * out_loop_start, int32_t * out_loop_end) { - return ps_find_loop_offsets_internal(streamFile, start_offset, data_size, channels, interleave, out_loop_start, out_loop_end, 0); +int ps_find_loop_offsets(STREAMFILE *sf, off_t start_offset, size_t data_size, int channels, size_t interleave, int32_t *p_loop_start, int32_t *p_loop_end) { + return ps_find_loop_offsets_internal(sf, start_offset, data_size, channels, interleave, p_loop_start, p_loop_end, 0); } -int ps_find_loop_offsets_full(STREAMFILE *streamFile, off_t start_offset, size_t data_size, int channels, size_t interleave, int32_t * out_loop_start, int32_t * out_loop_end) { - return ps_find_loop_offsets_internal(streamFile, start_offset, data_size, channels, interleave, out_loop_start, out_loop_end, 1); +int ps_find_loop_offsets_full(STREAMFILE *sf, off_t start_offset, size_t data_size, int channels, size_t interleave, int32_t *p_loop_start, int32_t *p_loop_end) { + return ps_find_loop_offsets_internal(sf, start_offset, data_size, channels, interleave, p_loop_start, p_loop_end, 1); } size_t ps_find_padding(STREAMFILE *streamFile, off_t start_offset, size_t data_size, int channels, size_t interleave, int discard_empty) { diff --git a/Frameworks/vgmstream/vgmstream/src/formats.c b/Frameworks/vgmstream/vgmstream/src/formats.c index 32fd57363..e8020a640 100644 --- a/Frameworks/vgmstream/vgmstream/src/formats.c +++ b/Frameworks/vgmstream/vgmstream/src/formats.c @@ -528,6 +528,7 @@ static const char* extension_list[] = { "wave", "wavebatch", "wavm", + "wavx", //txth/reserved [LEGO Star Wars (Xbox)] "wb", "wd", "wem", @@ -999,7 +1000,7 @@ static const meta_info meta_info_list[] = { {meta_FFCC_STR, "Final Fantasy: Crystal Chronicles STR header"}, {meta_SAT_BAKA, "BAKA header from Crypt Killer"}, {meta_NDS_SWAV, "SWAV Header"}, - {meta_PS2_VSF, "Musashi: Samurai Legend VSF Header"}, + {meta_VSF, "Square-Enix VSF header"}, {meta_NDS_RRDS, "Ridger Racer DS Header"}, {meta_PS2_TK5, "Tekken 5 Stream Header"}, {meta_PS2_SND, "Might and Magic SSND Header"}, @@ -1250,6 +1251,7 @@ static const meta_info meta_info_list[] = { {meta_BMP_KONAMI, "Konami BMP header"}, {meta_ISB, "Creative ISACT header"}, {meta_XSSB, "Artoon XSSB header"}, + {meta_XMA_UE3, "Unreal Engine XMA header"}, }; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/aax.c b/Frameworks/vgmstream/vgmstream/src/meta/aax.c index 3418620e8..542899f5c 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/aax.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/aax.c @@ -1,7 +1,7 @@ #include "meta.h" #include "../layout/layout.h" #include "../coding/coding.h" -#include "aax_utf.h" +#include "cri_utf.h" #define MAX_SEGMENTS 2 /* usually segment0=intro, segment1=loop/main */ @@ -9,63 +9,62 @@ /* AAX - segmented ADX [Bayonetta (PS3), Pandora's Tower (Wii), Catherine (X360), Binary Domain (PS3)] */ VGMSTREAM * init_vgmstream_aax(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; - int is_hca; - int loop_flag = 0, channel_count = 0; int32_t sample_count, loop_start_sample = 0, loop_end_sample = 0; - int segment_count; segmented_layout_data *data = NULL; - int table_error = 0; - const long top_offset = 0x00; + int segment_count, loop_segment = 0, is_hca; off_t segment_offset[MAX_SEGMENTS]; size_t segment_size[MAX_SEGMENTS]; int i; + utf_context *utf = NULL; /* checks */ - if (!check_extensions(streamFile, "aax")) + /* .aax: often with extension (with either HCA or AAX tables) + * (extensionless): sometimes without [PES 2013 (PC)] */ + if (!check_extensions(streamFile, "aax,")) goto fail; if (read_32bitBE(0x00,streamFile) != 0x40555446) /* "@UTF" */ goto fail; - - /* get segment count, offsets and sizes */ + /* .aax contains a simple UTF table, each row being a segment pointing to a CRI audio format */ { - struct utf_query_result result; - long aax_string_table_offset; - long aax_string_table_size; - long aax_data_offset; + int rows; + const char* name; + uint32_t table_offset = 0x00; - result = query_utf_nofail(streamFile, top_offset, NULL, &table_error); - if (table_error) goto fail; - segment_count = result.rows; - if (segment_count > MAX_SEGMENTS) goto fail; + utf = utf_open(streamFile, table_offset, &rows, &name); + if (!utf) goto fail; - aax_string_table_offset = top_offset+0x08 + result.string_table_offset; - aax_data_offset = top_offset+0x08 + result.data_offset; - aax_string_table_size = aax_data_offset - aax_string_table_offset; - - if (result.name_offset+0x04 > aax_string_table_size) - goto fail; - - if (read_32bitBE(aax_string_table_offset + result.name_offset, streamFile) == 0x41415800) /* "AAX\0" */ + if (strcmp(name, "AAX") == 0) is_hca = 0; - else if (read_32bitBE(aax_string_table_offset + result.name_offset, streamFile) == 0x48434100) /* "HCA\0" */ + else if (strcmp(name, "HCA") == 0) is_hca = 1; else goto fail; + segment_count = rows; + if (segment_count > MAX_SEGMENTS) goto fail; + + /* get offsets of constituent segments */ for (i = 0; i < segment_count; i++) { - struct offset_size_pair offset_size; + uint32_t offset, size; + int8_t segment_loop_flag = 0; - offset_size = query_utf_data(streamFile, top_offset, i, "data", &table_error); - if (table_error) goto fail; + if (!utf_query_s8(utf, i, "lpflg", &segment_loop_flag)) /* usually in last segment */ + goto fail; + if (!utf_query_data(utf, i, "data", &offset, &size)) + goto fail; - segment_offset[i] = aax_data_offset + offset_size.offset; - segment_size[i] = offset_size.size; + segment_offset[i] = table_offset + offset; + segment_size[i] = size; + if (segment_loop_flag) { + loop_flag = 1; + loop_segment = i; + } } } @@ -75,14 +74,14 @@ VGMSTREAM * init_vgmstream_aax(STREAMFILE *streamFile) { /* open each segment subfile */ for (i = 0; i < segment_count; i++) { - STREAMFILE* temp_streamFile = setup_subfile_streamfile(streamFile, segment_offset[i],segment_size[i], (is_hca ? "hca" : "adx")); - if (!temp_streamFile) goto fail; + STREAMFILE* temp_sf = setup_subfile_streamfile(streamFile, segment_offset[i],segment_size[i], (is_hca ? "hca" : "adx")); + if (!temp_sf) goto fail; data->segments[i] = is_hca ? - init_vgmstream_hca(temp_streamFile) : - init_vgmstream_adx(temp_streamFile); + init_vgmstream_hca(temp_sf) : + init_vgmstream_adx(temp_sf); - close_streamfile(temp_streamFile); + close_streamfile(temp_sf); if (!data->segments[i]) goto fail; } @@ -93,26 +92,22 @@ VGMSTREAM * init_vgmstream_aax(STREAMFILE *streamFile) { /* get looping and samples */ sample_count = 0; - loop_flag = 0; for (i = 0; i < segment_count; i++) { - int segment_loop_flag = query_utf_1byte(streamFile, top_offset, i, "lpflg", &table_error); - if (table_error) segment_loop_flag = 0; - if (!loop_flag && segment_loop_flag) { + if (loop_flag && loop_segment == i) { loop_start_sample = sample_count; } sample_count += data->segments[i]->num_samples; - if (!loop_flag && segment_loop_flag) { + if (loop_flag && loop_segment == i) { loop_end_sample = sample_count; - loop_flag = 1; } } - channel_count = data->segments[0]->channels; + /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; @@ -128,9 +123,11 @@ VGMSTREAM * init_vgmstream_aax(STREAMFILE *streamFile) { vgmstream->layout_data = data; + utf_close(utf); return vgmstream; fail: + utf_close(utf); close_vgmstream(vgmstream); free_layout_segmented(data); return NULL; @@ -141,92 +138,80 @@ fail: VGMSTREAM * init_vgmstream_utf_dsp(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; off_t start_offset; - size_t channel_size; - - int loop_flag = 0, channel_count, sample_rate; - long sample_count; - - int table_error = 0; - const long top_offset = 0x00; - long top_data_offset, segment_count; - long body_offset, body_size; - long header_offset, header_size; + int8_t loop_flag = 0, channel_count; + int sample_rate, num_samples, loop_start, loop_end, interleave; + uint32_t data_offset, data_size, header_offset, header_size; + utf_context *utf = NULL; /* checks */ - /* files don't have extension, we accept "" for CLI and .aax for plugins (they aren't exactly .aax though) */ + /* .aax: assumed + * (extensionless): extracted names inside csb/cpk often don't have extensions */ if (!check_extensions(streamFile, "aax,")) goto fail; if (read_32bitBE(0x00,streamFile) != 0x40555446) /* "@UTF" */ goto fail; - /* get segment count, offsets and sizes*/ + /* .aax contains a simple UTF table with one row and various columns being header info */ { - struct utf_query_result result; - long top_string_table_offset; - long top_string_table_size; - long name_offset; - - result = query_utf_nofail(streamFile, top_offset, NULL, &table_error); - if (table_error) goto fail; + int rows; + const char* name; + uint32_t table_offset = 0x00; - segment_count = result.rows; - if (segment_count != 1) goto fail; /* only simple stuff for now (multisegment not known) */ - top_string_table_offset = top_offset + 8 + result.string_table_offset; - top_data_offset = top_offset + 8 + result.data_offset; - top_string_table_size = top_data_offset - top_string_table_offset; + utf = utf_open(streamFile, table_offset, &rows, &name); + if (!utf) goto fail; - if (result.name_offset+10 > top_string_table_size) goto fail; - - name_offset = top_string_table_offset + result.name_offset; - if (read_32bitBE(name_offset+0x00, streamFile) != 0x41445043 || /* "ADPC" */ - read_32bitBE(name_offset+0x04, streamFile) != 0x4D5F5749 || /* "M_WI" */ - read_16bitBE(name_offset+0x08, streamFile) != 0x4900) /* "I\0" */ + if (strcmp(name, "ADPCM_WII") != 0) goto fail; + + if (rows != 1) + goto fail; + + if (!utf_query_s32(utf, 0, "sfreq", &sample_rate)) + goto fail; + if (!utf_query_s32(utf, 0, "nsmpl", &num_samples)) + goto fail; + if (!utf_query_s8(utf, 0, "nch", &channel_count)) + goto fail; + if (!utf_query_s8(utf, 0, "lpflg", &loop_flag)) /* full loops */ + goto fail; + /* for some reason data is stored before header */ + if (!utf_query_data(utf, 0, "data", &data_offset, &data_size)) + goto fail; + if (!utf_query_data(utf, 0, "header", &header_offset, &header_size)) + goto fail; + + if (channel_count < 1 || channel_count > 2) + goto fail; + if (header_size != channel_count * 0x60) + goto fail; + + start_offset = data_offset; + interleave = (data_size+7) / 8 * 8 / channel_count; + + loop_start = read_32bitBE(header_offset + 0x10, streamFile); + loop_end = read_32bitBE(header_offset + 0x14, streamFile); } - /* get sizes */ - { - struct offset_size_pair offset_size; - - offset_size = query_utf_data(streamFile, top_offset, 0, "data", &table_error); - if (table_error) goto fail; - body_offset = top_data_offset + offset_size.offset; - body_size = offset_size.size; - - offset_size = query_utf_data(streamFile, top_offset, 0, "header", &table_error); - if (table_error) goto fail; - header_offset = top_data_offset + offset_size.offset; - header_size = offset_size.size; - } - - channel_count = query_utf_1byte(streamFile, top_offset, 0, "nch", &table_error); - sample_count = query_utf_4byte(streamFile, top_offset, 0, "nsmpl", &table_error); - sample_rate = query_utf_4byte(streamFile, top_offset, 0, "sfreq", &table_error); - if (table_error) goto fail; - if (channel_count != 1 && channel_count != 2) goto fail; - if (header_size != channel_count * 0x60) goto fail; - - start_offset = body_offset; - channel_size = (body_size+7) / 8 * 8 / channel_count; - /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; vgmstream->sample_rate = sample_rate; - vgmstream->num_samples = sample_count; + vgmstream->num_samples = num_samples; + vgmstream->loop_start_sample = dsp_nibbles_to_samples(loop_start); + vgmstream->loop_end_sample = dsp_nibbles_to_samples(loop_end) + 1; - vgmstream->meta_type = meta_UTF_DSP; vgmstream->coding_type = coding_NGC_DSP; vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = channel_size; + vgmstream->interleave_block_size = interleave; + vgmstream->meta_type = meta_UTF_DSP; dsp_read_coefs_be(vgmstream, streamFile, header_offset+0x1c, 0x60); - if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + if (!vgmstream_open_stream(vgmstream, streamFile, start_offset)) goto fail; return vgmstream; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/aax_utf.h b/Frameworks/vgmstream/vgmstream/src/meta/aax_utf.h deleted file mode 100644 index 24dc88dc3..000000000 --- a/Frameworks/vgmstream/vgmstream/src/meta/aax_utf.h +++ /dev/null @@ -1,442 +0,0 @@ -#ifndef _AAX_UTF_H_ -#define _AAX_UTF_H_ - -struct utf_query -{ - /* if 0 */ - const char *name; - int index; -}; - -struct offset_size_pair -{ - uint32_t offset; - uint32_t size; -}; - -struct utf_query_result -{ - int valid; /* table is valid */ - int found; - int type; /* one of COLUMN_TYPE_* */ - union - { - uint64_t value_u64; - uint32_t value_u32; - uint16_t value_u16; - uint8_t value_u8; - float value_float; - struct offset_size_pair value_data; - uint32_t value_string; - } value; - - /* info for the queried table */ - uint32_t rows; - uint32_t name_offset; - uint32_t string_table_offset; - uint32_t data_offset; -}; - - -#define COLUMN_STORAGE_MASK 0xf0 -#define COLUMN_STORAGE_PERROW 0x50 -#define COLUMN_STORAGE_CONSTANT 0x30 -#define COLUMN_STORAGE_ZERO 0x10 - -#define COLUMN_TYPE_MASK 0x0f -#define COLUMN_TYPE_DATA 0x0b -#define COLUMN_TYPE_STRING 0x0a -#define COLUMN_TYPE_FLOAT 0x08 -#define COLUMN_TYPE_8BYTE 0x06 -#define COLUMN_TYPE_4BYTE 0x04 -#define COLUMN_TYPE_2BYTE2 0x03 -#define COLUMN_TYPE_2BYTE 0x02 -#define COLUMN_TYPE_1BYTE2 0x01 -#define COLUMN_TYPE_1BYTE 0x00 - -struct utf_column_info -{ - uint8_t type; - const char *column_name; - long constant_offset; -}; - -struct utf_table_info -{ - long table_offset; - uint32_t table_size; - uint32_t schema_offset; - uint32_t rows_offset; - uint32_t string_table_offset; - uint32_t data_offset; - const char *string_table; - const char *table_name; - uint16_t columns; - uint16_t row_width; - uint32_t rows; - - const struct utf_column_info *schema; -}; - -/* @UTF table reading, abridged */ -static struct utf_query_result analyze_utf(STREAMFILE *infile, const long offset, const struct utf_query *query) -{ - unsigned char buf[4]; - struct utf_table_info table_info; - char *string_table = NULL; - struct utf_column_info * schema = NULL; - struct utf_query_result result; - uint32_t table_name_string; - int string_table_size; - - result.valid = 0; - - table_info.table_offset = offset; - - /* check header */ - { - static const char UTF_signature[4] = "@UTF"; /* intentionally unterminated */ - if (4 != read_streamfile(buf, offset, 4, infile)) goto cleanup_error; - if (memcmp(buf, UTF_signature, sizeof(UTF_signature))) - { - goto cleanup_error; - } - } - - /* get table size */ - table_info.table_size = read_32bitBE(offset+4, infile); - - table_info.schema_offset = 0x20; - table_info.rows_offset = read_32bitBE(offset+8, infile); - table_info.string_table_offset = read_32bitBE(offset+0xc,infile); - table_info.data_offset = read_32bitBE(offset+0x10,infile); - table_name_string = read_32bitBE(offset+0x14,infile); - table_info.columns = read_16bitBE(offset+0x18,infile); - table_info.row_width = read_16bitBE(offset+0x1a,infile); - table_info.rows = read_32bitBE(offset+0x1c,infile); - - /* allocate for string table */ - string_table_size = table_info.data_offset-table_info.string_table_offset; - string_table = malloc(string_table_size+1); - if (!string_table) goto cleanup_error; - table_info.string_table = string_table; - memset(string_table, 0, string_table_size+1); - - /* load schema */ - schema = malloc(sizeof(struct utf_column_info) * table_info.columns); - if (!schema) goto cleanup_error; - - { - int i; - long schema_current_offset = table_info.schema_offset; - for (i = 0; i < table_info.columns; i++) - { - schema[i].type = read_8bit(schema_current_offset,infile); - schema_current_offset ++; - schema[i].column_name = string_table + read_32bitBE(schema_current_offset,infile); - schema_current_offset += 4; - - if ((schema[i].type & COLUMN_STORAGE_MASK) == COLUMN_STORAGE_CONSTANT) - { - schema[i].constant_offset = schema_current_offset; - switch (schema[i].type & COLUMN_TYPE_MASK) - { - case COLUMN_TYPE_8BYTE: - case COLUMN_TYPE_DATA: - schema_current_offset+=8; - break; - case COLUMN_TYPE_STRING: - case COLUMN_TYPE_FLOAT: - case COLUMN_TYPE_4BYTE: - schema_current_offset+=4; - break; - case COLUMN_TYPE_2BYTE2: - case COLUMN_TYPE_2BYTE: - schema_current_offset+=2; - break; - case COLUMN_TYPE_1BYTE2: - case COLUMN_TYPE_1BYTE: - schema_current_offset++; - break; - default: - goto cleanup_error; - } - } - } - } - - table_info.schema = schema; - - /* read string table */ - read_streamfile((unsigned char *)string_table, - table_info.string_table_offset+8+offset, - string_table_size, infile); - table_info.table_name = table_info.string_table+table_name_string; - - /* fill in the default stuff */ - result.found = 0; - result.rows = table_info.rows; - result.name_offset = table_name_string; - result.string_table_offset = table_info.string_table_offset; - result.data_offset = table_info.data_offset; - - /* explore the values */ - if (query) { - int i, j; - - for (i = 0; i < table_info.rows; i++) - { - uint32_t row_offset = - table_info.table_offset + 8 + table_info.rows_offset + - i * table_info.row_width; - const uint32_t row_start_offset = row_offset; - - if (query && i != query->index) continue; - - for (j = 0; j < table_info.columns; j++) - { - uint8_t type = table_info.schema[j].type; - long constant_offset = table_info.schema[j].constant_offset; - int constant = 0; - - int qthis = (query && i == query->index && - !strcmp(table_info.schema[j].column_name, query->name)); - - if (qthis) - { - result.found = 1; - result.type = schema[j].type & COLUMN_TYPE_MASK; - } - - switch (schema[j].type & COLUMN_STORAGE_MASK) - { - case COLUMN_STORAGE_PERROW: - break; - case COLUMN_STORAGE_CONSTANT: - constant = 1; - break; - case COLUMN_STORAGE_ZERO: - if (qthis) - { - memset(&result.value, 0, - sizeof(result.value)); - } - continue; - default: - goto cleanup_error; - } - - if (1) - { - long data_offset; - int bytes_read; - - if (constant) - { - data_offset = constant_offset; - } - else - { - data_offset = row_offset; - } - - switch (type & COLUMN_TYPE_MASK) - { - case COLUMN_TYPE_STRING: - { - uint32_t string_offset; - string_offset = read_32bitBE(data_offset, infile); - bytes_read = 4; - if (qthis) - { - result.value.value_string = string_offset; - } - } - break; - case COLUMN_TYPE_DATA: - { - uint32_t vardata_offset, vardata_size; - - vardata_offset = read_32bitBE(data_offset, infile); - vardata_size = read_32bitBE(data_offset+4, infile); - bytes_read = 8; - if (qthis) - { - result.value.value_data.offset = vardata_offset; - result.value.value_data.size = vardata_size; - } - } - break; - - case COLUMN_TYPE_8BYTE: - { - uint64_t value = - read_32bitBE(data_offset, infile); - value <<= 32; - value |= - read_32bitBE(data_offset+4, infile); - if (qthis) - { - result.value.value_u64 = value; - } - bytes_read = 8; - break; - } - case COLUMN_TYPE_4BYTE: - { - uint32_t value = - read_32bitBE(data_offset, infile); - if (qthis) - { - result.value.value_u32 = value; - } - bytes_read = 4; - } - break; - case COLUMN_TYPE_2BYTE2: - case COLUMN_TYPE_2BYTE: - { - uint16_t value = - read_16bitBE(data_offset, infile); - if (qthis) - { - result.value.value_u16 = value; - } - bytes_read = 2; - } - break; - case COLUMN_TYPE_FLOAT: - if (sizeof(float) == 4) - { - union { - float float_value; - uint32_t int_value; - } int_float; - - int_float.int_value = read_32bitBE(data_offset, infile); - if (qthis) - { - result.value.value_float = int_float.float_value; - } - } - else - { - read_32bitBE(data_offset, infile); - if (qthis) - { - goto cleanup_error; - } - } - bytes_read = 4; - break; - case COLUMN_TYPE_1BYTE2: - case COLUMN_TYPE_1BYTE: - { - uint8_t value = - read_8bit(data_offset, infile); - if (qthis) - { - result.value.value_u8 = value; - } - bytes_read = 1; - } - break; - default: - goto cleanup_error; - } - - if (!constant) - { - row_offset += bytes_read; - } - } /* useless if end */ - } /* column for loop end */ - - if (row_offset - row_start_offset != table_info.row_width) - goto cleanup_error; - - if (query && i >= query->index) break; - } /* row for loop end */ - } /* explore values block end */ - -//cleanup: - - result.valid = 1; -cleanup_error: - - if (string_table) - { - free(string_table); - string_table = NULL; - } - - if (schema) - { - free(schema); - schema = NULL; - } - - return result; -} - -static struct utf_query_result query_utf(STREAMFILE *infile, const long offset, const struct utf_query *query) -{ - return analyze_utf(infile, offset, query); -} - -/*static*/ struct utf_query_result query_utf_nofail(STREAMFILE *infile, const long offset, const struct utf_query *query, int *error) -{ - const struct utf_query_result result = query_utf(infile, offset, query); - - if (error) - { - *error = 0; - if (!result.valid) *error = 1; - if (query && !result.found) *error = 1; - } - - return result; -} - -static struct utf_query_result query_utf_key(STREAMFILE *infile, const long offset, int index, const char *name, int *error) -{ - struct utf_query query; - query.index = index; - query.name = name; - - return query_utf_nofail(infile, offset, &query, error); -} - -/*static*/ uint8_t query_utf_1byte(STREAMFILE *infile, const long offset, int index, const char *name, int *error) -{ - struct utf_query_result result = query_utf_key(infile, offset, index, name, error); - if (error) - { - if (result.type != COLUMN_TYPE_1BYTE) *error = 1; - } - return result.value.value_u8; -} - -/*static*/ uint32_t query_utf_4byte(STREAMFILE *infile, const long offset, int index, const char *name, int *error) -{ - struct utf_query_result result = query_utf_key(infile, offset, index, name, error); - if (error) - { - if (result.type != COLUMN_TYPE_4BYTE) *error = 1; - } - return result.value.value_u32; -} - -/*static*/ struct offset_size_pair query_utf_data(STREAMFILE *infile, const long offset, - int index, const char *name, int *error) -{ - struct utf_query_result result = query_utf_key(infile, offset, index, name, error); - if (error) - { - if (result.type != COLUMN_TYPE_DATA) *error = 1; - } - return result.value.value_data; -} - - -#endif /* SRC_META_AAX_UTF_H_ */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/acb.c b/Frameworks/vgmstream/vgmstream/src/meta/acb.c index bfe06d884..27693b245 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/acb.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/acb.c @@ -1,6 +1,6 @@ #include "meta.h" #include "../coding/coding.h" -#include "acb_utf.h" +#include "cri_utf.h" /* ACB (Atom Cue sheet Binary) - CRI container of memory audio, often together with a .awb wave bank */ @@ -36,7 +36,7 @@ VGMSTREAM * init_vgmstream_acb(STREAMFILE *streamFile) { //todo acb+cpk is also possible - if (!utf_query_data(streamFile, utf, 0, "AwbFile", &offset, &size)) + if (!utf_query_data(utf, 0, "AwbFile", &offset, &size)) goto fail; subfile_offset = table_offset + offset; @@ -132,7 +132,7 @@ static int open_utf_subtable(acb_header* acb, STREAMFILE* *TableSf, utf_context* if (*Table != NULL) return 1; - if (!utf_query_data(acb->acbFile, acb->Header, 0, TableName, &offset, NULL)) + if (!utf_query_data(acb->Header, 0, TableName, &offset, NULL)) goto fail; /* open a buffered streamfile to avoid so much IO back and forth between all the tables */ @@ -189,16 +189,16 @@ static int load_acb_waveform(acb_header* acb, int16_t Index) { /* read Waveform[Index] */ if (!open_utf_subtable(acb, &acb->WaveformSf, &acb->WaveformTable, "WaveformTable", NULL)) goto fail; - if (!utf_query_s16(acb->WaveformSf, acb->WaveformTable, Index, "Id", &Waveform_Id)) { /* older versions use Id */ + if (!utf_query_s16(acb->WaveformTable, Index, "Id", &Waveform_Id)) { /* older versions use Id */ if (acb->is_memory) { - if (!utf_query_s16(acb->WaveformSf, acb->WaveformTable, Index, "MemoryAwbId", &Waveform_Id)) + if (!utf_query_s16(acb->WaveformTable, Index, "MemoryAwbId", &Waveform_Id)) goto fail; } else { - if (!utf_query_s16(acb->WaveformSf, acb->WaveformTable, Index, "StreamAwbId", &Waveform_Id)) + if (!utf_query_s16(acb->WaveformTable, Index, "StreamAwbId", &Waveform_Id)) goto fail; } } - if (!utf_query_s8(acb->WaveformSf, acb->WaveformTable, Index, "Streaming", &Waveform_Streaming)) + if (!utf_query_s8(acb->WaveformTable, Index, "Streaming", &Waveform_Streaming)) goto fail; //;VGM_LOG("ACB: Waveform[%i]: Id=%i, Streaming=%i\n", Index, Waveform_Id, Waveform_Streaming); @@ -230,9 +230,9 @@ static int load_acb_synth(acb_header* acb, int16_t Index) { /* read Synth[Index] */ if (!open_utf_subtable(acb, &acb->SynthSf, &acb->SynthTable, "SynthTable", NULL)) goto fail; - if (!utf_query_s8(acb->SynthSf, acb->SynthTable, Index, "Type", &Synth_Type)) + if (!utf_query_s8(acb->SynthTable, Index, "Type", &Synth_Type)) goto fail; - if (!utf_query_data(acb->SynthSf, acb->SynthTable, Index, "ReferenceItems", &Synth_ReferenceItems_offset, &Synth_ReferenceItems_size)) + if (!utf_query_data(acb->SynthTable, Index, "ReferenceItems", &Synth_ReferenceItems_offset, &Synth_ReferenceItems_size)) goto fail; //;VGM_LOG("ACB: Synth[%i]: Type=%x, ReferenceItems={%x,%x}\n", Index, Synth_Type, Synth_ReferenceItems_offset, Synth_ReferenceItems_size); @@ -308,7 +308,7 @@ static int load_acb_track_event_command(acb_header* acb, int16_t Index) { /* read Track[Index] */ if (!open_utf_subtable(acb, &acb->TrackSf, &acb->TrackTable, "TrackTable", NULL)) goto fail; - if (!utf_query_s16(acb->TrackSf, acb->TrackTable, Index, "EventIndex", &Track_EventIndex)) + if (!utf_query_s16(acb->TrackTable, Index, "EventIndex", &Track_EventIndex)) goto fail; //;VGM_LOG("ACB: Track[%i]: EventIndex=%i\n", Index, Track_EventIndex); @@ -317,7 +317,7 @@ static int load_acb_track_event_command(acb_header* acb, int16_t Index) { /* read Command[EventIndex] */ if (!open_utf_subtable(acb, &acb->TrackCommandSf, &acb->TrackCommandTable, "CommandTable", NULL)) goto fail; - if (!utf_query_data(acb->TrackCommandSf, acb->TrackCommandTable, Track_EventIndex, "Command", &Track_Command_offset, &Track_Command_size)) + if (!utf_query_data(acb->TrackCommandTable, Track_EventIndex, "Command", &Track_Command_offset, &Track_Command_size)) goto fail; //;VGM_LOG("ACB: Command[%i]: Command={%x,%x}\n", Track_EventIndex, Track_Command_offset,Track_Command_size); } @@ -325,7 +325,7 @@ static int load_acb_track_event_command(acb_header* acb, int16_t Index) { /* read TrackEvent[EventIndex] */ if (!open_utf_subtable(acb, &acb->TrackCommandSf, &acb->TrackCommandTable, "TrackEventTable", NULL)) goto fail; - if (!utf_query_data(acb->TrackCommandSf, acb->TrackCommandTable, Track_EventIndex, "Command", &Track_Command_offset, &Track_Command_size)) + if (!utf_query_data(acb->TrackCommandTable, Track_EventIndex, "Command", &Track_Command_offset, &Track_Command_size)) goto fail; //;VGM_LOG("ACB: TrackEvent[%i]: Command={%x,%x}\n", Track_EventIndex, Track_Command_offset,Track_Command_size); } @@ -398,9 +398,9 @@ static int load_acb_sequence(acb_header* acb, int16_t Index) { /* read Sequence[Index] */ if (!open_utf_subtable(acb, &acb->SequenceSf, &acb->SequenceTable, "SequenceTable", NULL)) goto fail; - if (!utf_query_s16(acb->SequenceSf, acb->SequenceTable, Index, "NumTracks", &Sequence_NumTracks)) + if (!utf_query_s16(acb->SequenceTable, Index, "NumTracks", &Sequence_NumTracks)) goto fail; - if (!utf_query_data(acb->SequenceSf, acb->SequenceTable, Index, "TrackIndex", &Sequence_TrackIndex_offset, &Sequence_TrackIndex_size)) + if (!utf_query_data(acb->SequenceTable, Index, "TrackIndex", &Sequence_TrackIndex_offset, &Sequence_TrackIndex_size)) goto fail; //;VGM_LOG("ACB: Sequence[%i]: NumTracks=%i, TrackIndex={%x, %x}\n", Index, Sequence_NumTracks, Sequence_TrackIndex_offset,Sequence_TrackIndex_size); @@ -441,9 +441,9 @@ static int load_acb_block(acb_header* acb, int16_t Index) { /* read Block[Index] */ if (!open_utf_subtable(acb, &acb->BlockSf, &acb->BlockTable, "BlockTable", NULL)) goto fail; - if (!utf_query_s16(acb->BlockSf, acb->BlockTable, Index, "NumTracks", &Block_NumTracks)) + if (!utf_query_s16(acb->BlockTable, Index, "NumTracks", &Block_NumTracks)) goto fail; - if (!utf_query_data(acb->BlockSf, acb->BlockTable, Index, "TrackIndex", &Block_TrackIndex_offset, &Block_TrackIndex_size)) + if (!utf_query_data(acb->BlockTable, Index, "TrackIndex", &Block_TrackIndex_offset, &Block_TrackIndex_size)) goto fail; //;VGM_LOG("ACB: Block[%i]: NumTracks=%i, TrackIndex={%x, %x}\n", Index, Block_NumTracks, Block_TrackIndex_offset,Block_TrackIndex_size); @@ -474,9 +474,9 @@ static int load_acb_cue(acb_header* acb, int16_t Index) { /* read Cue[Index] */ if (!open_utf_subtable(acb, &acb->CueSf, &acb->CueTable, "CueTable", NULL)) goto fail; - if (!utf_query_s8 (acb->CueSf, acb->CueTable, Index, "ReferenceType", &Cue_ReferenceType)) + if (!utf_query_s8 (acb->CueTable, Index, "ReferenceType", &Cue_ReferenceType)) goto fail; - if (!utf_query_s16(acb->CueSf, acb->CueTable, Index, "ReferenceIndex", &Cue_ReferenceIndex)) + if (!utf_query_s16(acb->CueTable, Index, "ReferenceIndex", &Cue_ReferenceIndex)) goto fail; //;VGM_LOG("ACB: Cue[%i]: ReferenceType=%i, ReferenceIndex=%i\n", Index, Cue_ReferenceType, Cue_ReferenceIndex); @@ -504,6 +504,8 @@ static int load_acb_cue(acb_header* acb, int16_t Index) { goto fail; break; + case 6: /* Cue > (Waveform/Synth/Command)? (ex. PES 2014) */ + case 7: /* Cue > (Waveform/Synth/Command)? (ex. PES 2014) */ default: VGM_LOG("ACB: unknown Cue.ReferenceType=%x, Cue.ReferenceIndex=%x\n", Cue_ReferenceType, Cue_ReferenceIndex); break; /* ignore and continue */ @@ -524,9 +526,9 @@ static int load_acb_cuename(acb_header* acb, int16_t Index) { /* read CueName[Index] */ if (!open_utf_subtable(acb, &acb->CueNameSf, &acb->CueNameTable, "CueNameTable", NULL)) goto fail; - if (!utf_query_s16(acb->CueNameSf, acb->CueNameTable, Index, "CueIndex", &CueName_CueIndex)) + if (!utf_query_s16(acb->CueNameTable, Index, "CueIndex", &CueName_CueIndex)) goto fail; - if (!utf_query_string(acb->CueNameSf, acb->CueNameTable, Index, "CueName", &CueName_CueName)) + if (!utf_query_string(acb->CueNameTable, Index, "CueName", &CueName_CueName)) goto fail; //;VGM_LOG("ACB: CueName[%i]: CueIndex=%i, CueName=%s\n", Index, CueName_CueIndex, CueName_CueName); @@ -583,8 +585,8 @@ void load_acb_wave_name(STREAMFILE *streamFile, VGMSTREAM* vgmstream, int waveid acb.target_waveid = waveid; acb.is_memory = is_memory; - acb.has_TrackEventTable = utf_query_data(acb.acbFile, acb.Header, 0, "TrackEventTable", NULL,NULL); - acb.has_CommandTable = utf_query_data(acb.acbFile, acb.Header, 0, "CommandTable", NULL,NULL); + acb.has_TrackEventTable = utf_query_data(acb.Header, 0, "TrackEventTable", NULL,NULL); + acb.has_CommandTable = utf_query_data(acb.Header, 0, "CommandTable", NULL,NULL); /* read all possible cue names and find which waveids are referenced by it */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/acb_utf.h b/Frameworks/vgmstream/vgmstream/src/meta/acb_utf.h index e081ca40e..8c7c27d55 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/acb_utf.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/acb_utf.h @@ -1,423 +1,376 @@ -#ifndef _ACB_UTF_H_ -#define _ACB_UTF_H_ - -/* CRI @UTF (Universal Table Format?) is a generic database-like table made of - * rows/columns that contain numbers/strings/ binarydata, which also can be other tables. - * - * A table starts with "@UTF" and defines some values (row/data/string offsets, counts, etc) - * then schema (columns type+name), then rows, string table and binary data. Formats using @UTF - * store and read data by row number + column name. Being a generic table with no fixed schema - * CRI uses it for different purposes (.acf: cues, .cpk: files, .aax: bgm, .usm: video, etc). - * - * (adapted from hcs's code to do multiple querys in the same table) - */ - -// todo divide into some .c file and use for other @UTF parsing - -/* API */ -typedef struct utf_context utf_context; /* opaque struct */ -/*static*/ utf_context* utf_open(STREAMFILE *streamfile, uint32_t table_offset, int* rows, const char* *row_name); -/*static*/ void utf_close(utf_context *utf); - -/*static*/ int utf_query_s8(STREAMFILE *streamfile, utf_context *utf, int row, const char* column, int8_t* value); -/*static*/ int utf_query_s16(STREAMFILE *streamfile, utf_context *utf, int row, const char* column, int16_t* value); -/*static*/ int utf_query_string(STREAMFILE *streamfile, utf_context *utf, int row, const char* column, const char* *value); -/*static*/ int utf_query_data(STREAMFILE *streamfile, utf_context *utf, int row, const char* column, uint32_t *offset, uint32_t *size); - -/* ************************************************* */ -/* INTERNALS */ - -/* possibly 3b+5b from clUTF decompilation */ -#define COLUMN_BITMASK_STORAGE 0xf0 -#define COLUMN_BITMASK_TYPE 0x0f - -#define COLUMN_STORAGE_ZERO 0x10 -#define COLUMN_STORAGE_CONSTANT 0x30 -#define COLUMN_STORAGE_ROW 0x50 -//#define COLUMN_STORAGE_CONSTANT2 0x70 /* from vgmtoolbox */ - -#define COLUMN_TYPE_SINT8 0x00 -#define COLUMN_TYPE_UINT8 0x01 -#define COLUMN_TYPE_SINT16 0x02 -#define COLUMN_TYPE_UINT16 0x03 -#define COLUMN_TYPE_SINT32 0x04 -#define COLUMN_TYPE_UINT32 0x05 -#define COLUMN_TYPE_SINT64 0x06 -//#define COLUMN_TYPE_UINT64 0x07 -#define COLUMN_TYPE_FLOAT 0x08 -//#define COLUMN_TYPE_DOUBLE 0x09 -#define COLUMN_TYPE_STRING 0x0a -#define COLUMN_TYPE_DATA 0x0b - - -typedef struct { - uint32_t offset; - uint32_t size; -} utf_data_t; -typedef struct { - int valid; /* table is valid */ - int found; - int type; /* one of COLUMN_TYPE_* */ - union { - int8_t value_s8; - uint8_t value_u8; - int16_t value_s16; - uint16_t value_u16; - int32_t value_s32; - uint32_t value_u32; - int64_t value_s64; - uint64_t value_u64; - float value_float; - double value_double; - utf_data_t value_data; - const char *value_string; - } value; -} utf_result; - - -typedef struct { - uint8_t flags; - const char *name; - uint32_t offset; -} utf_column; - -struct utf_context { - uint32_t table_offset; - - /* header */ - uint32_t table_size; - uint16_t version; - uint16_t rows_offset; - uint32_t strings_offset; - uint32_t data_offset; - uint32_t name_offset; - uint16_t columns; - uint16_t row_width; - uint32_t rows; - /*const*/ utf_column *schema; - - /* derived */ - uint32_t schema_offset; - uint32_t strings_size; - /*const*/ char *string_table; - const char *table_name; -}; - - -/* @UTF table reading, abridged */ -/*static*/ utf_context* utf_open(STREAMFILE *streamfile, uint32_t table_offset, int* rows, const char* *row_name) { - utf_context* utf = NULL; - - - utf = calloc(1, sizeof(utf_context)); - if (!utf) goto fail; - - utf->table_offset = table_offset; - - /* check header */ - if (read_32bitBE(table_offset + 0x00, streamfile) != 0x40555446) /* "@UTF" */ - goto fail; - - /* load table header */ - utf->table_size = read_32bitBE(table_offset + 0x04, streamfile) + 0x08; - utf->version = read_16bitBE(table_offset + 0x08, streamfile); - utf->rows_offset = read_16bitBE(table_offset + 0x0a, streamfile) + 0x08; - utf->strings_offset = read_32bitBE(table_offset + 0x0c, streamfile) + 0x08; - utf->data_offset = read_32bitBE(table_offset + 0x10, streamfile) + 0x08; - utf->name_offset = read_32bitBE(table_offset + 0x14, streamfile); /* within string table */ - utf->columns = read_16bitBE(table_offset + 0x18, streamfile); - utf->row_width = read_16bitBE(table_offset + 0x1a, streamfile); - utf->rows = read_32bitBE(table_offset + 0x1c, streamfile); - - utf->schema_offset = 0x20; - utf->strings_size = utf->data_offset - utf->strings_offset; - - /* 00: early (32b rows_offset?), 01: +2017 (no apparent differences) */ - if (utf->version != 0x00 && utf->version != 0x01) { - VGM_LOG("@UTF: unknown version\n"); - } - if (utf->table_offset + utf->table_size > get_streamfile_size(streamfile)) - goto fail; - if (utf->rows == 0 || utf->rows_offset > utf->table_size || utf->data_offset > utf->table_size) - goto fail; - if (utf->name_offset > utf->strings_size) - goto fail; - - - /* load string table */ - { - size_t read; - - utf->string_table = calloc(utf->strings_size + 1, sizeof(char)); - if (!utf->string_table) goto fail; - - utf->table_name = utf->string_table + utf->name_offset; - - read = read_streamfile((unsigned char*)utf->string_table, utf->table_offset + utf->strings_offset, utf->strings_size, streamfile); - if (utf->strings_size != read) goto fail; - } - - - /* load column schema */ - { - int i; - uint32_t value_size, column_offset = 0; - uint32_t schema_offset = utf->table_offset + utf->schema_offset; - - - utf->schema = malloc(sizeof(utf_column) * utf->columns); - if (!utf->schema) goto fail; - - for (i = 0; i < utf->columns; i++) { - uint8_t flags = read_8bit(schema_offset + 0x00, streamfile); - uint32_t name_offset = read_32bitBE(schema_offset + 0x01, streamfile); - if (name_offset > utf->strings_size) - goto fail; - - utf->schema[i].flags = flags; - utf->schema[i].name = utf->string_table + name_offset; - schema_offset += 0x01 + 0x04; - - switch (utf->schema[i].flags & COLUMN_BITMASK_TYPE) { - case COLUMN_TYPE_SINT8: - case COLUMN_TYPE_UINT8: - value_size = 0x01; - break; - case COLUMN_TYPE_SINT16: - case COLUMN_TYPE_UINT16: - value_size = 0x02; - break; - case COLUMN_TYPE_SINT32: - case COLUMN_TYPE_UINT32: - case COLUMN_TYPE_FLOAT: - case COLUMN_TYPE_STRING: - value_size = 0x04; - break; - case COLUMN_TYPE_SINT64: - //case COLUMN_TYPE_UINT64: - //case COLUMN_TYPE_DOUBLE: - case COLUMN_TYPE_DATA: - value_size = 0x08; - break; - default: - VGM_LOG("@UTF: unknown column type\n"); - goto fail; - } - - switch (utf->schema[i].flags & COLUMN_BITMASK_STORAGE) { - case COLUMN_STORAGE_ROW: - utf->schema[i].offset = column_offset; - column_offset += value_size; - break; - case COLUMN_STORAGE_CONSTANT: - //case COLUMN_STORAGE_CONSTANT2: - utf->schema[i].offset = schema_offset - (utf->table_offset + utf->schema_offset); /* relative to schema */ - schema_offset += value_size; - break; - case COLUMN_STORAGE_ZERO: - utf->schema[i].offset = 0; /* ? */ - break; - default: - VGM_LOG("@UTF: unknown column storage\n"); - goto fail; - } - } - } - - - /* write info */ - if (rows) *rows = utf->rows; - if (row_name) *row_name = utf->string_table + utf->name_offset; - - return utf; -fail: - utf_close(utf); - return NULL; -} - -/*static*/ void utf_close(utf_context *utf) { - if (!utf) return; - - free(utf->string_table); - free(utf->schema); - free(utf); -} - - -static int utf_query(STREAMFILE *streamfile, utf_context *utf, int row, const char* column, utf_result* result) { - int i; - - - /* fill in the default stuff */ - result->valid = 0; - result->found = 0; - - if (row >= utf->rows || row < 0) - goto fail; - - /* find target column */ - for (i = 0; i < utf->columns; i++) { - utf_column* col = &utf->schema[i]; - uint32_t data_offset; - - if (strcmp(col->name, column) != 0) - continue; - - result->found = 1; - result->type = col->flags & COLUMN_BITMASK_TYPE; - - switch (col->flags & COLUMN_BITMASK_STORAGE) { - case COLUMN_STORAGE_ROW: - data_offset = utf->table_offset + utf->rows_offset + row * utf->row_width + col->offset; - break; - case COLUMN_STORAGE_CONSTANT: - //case COLUMN_STORAGE_CONSTANT2: - data_offset = utf->table_offset + utf->schema_offset + col->offset; - break; - case COLUMN_STORAGE_ZERO: - data_offset = 0; - memset(&result->value, 0, sizeof(result->value)); - break; - default: - goto fail; - } - - /* ignore zero value */ - if (!data_offset) - break; - - /* read row/constant value */ - switch (col->flags & COLUMN_BITMASK_TYPE) { - case COLUMN_TYPE_SINT8: - result->value.value_u8 = read_8bit(data_offset, streamfile); - break; - case COLUMN_TYPE_UINT8: - result->value.value_u8 = (uint8_t)read_8bit(data_offset, streamfile); - break; - case COLUMN_TYPE_SINT16: - result->value.value_s16 = read_16bitBE(data_offset, streamfile); - break; - case COLUMN_TYPE_UINT16: - result->value.value_u16 = (uint16_t)read_16bitBE(data_offset, streamfile); - break; - case COLUMN_TYPE_SINT32: - result->value.value_s32 = read_32bitBE(data_offset, streamfile); - break; - case COLUMN_TYPE_UINT32: - result->value.value_u32 = (uint32_t)read_32bitBE(data_offset, streamfile); - break; - case COLUMN_TYPE_SINT64: - result->value.value_s64 = read_64bitBE(data_offset, streamfile); - break; -#if 0 - case COLUMN_TYPE_UINT64: - result->value.value_u64 = read_64bitBE(data_offset, streamfile); - break; -#endif - case COLUMN_TYPE_FLOAT: { - union { //todo inline function? - float float_value; - uint32_t int_value; - } cnv; - - if (sizeof(float) != 4) { - VGM_LOG("@UTF: can't convert float\n"); - goto fail; - } - - cnv.int_value = (uint32_t)read_32bitBE(data_offset, streamfile); - result->value.value_float = cnv.float_value; - break; - } -#if 0 - case COLUMN_TYPE_DOUBLE: { - union { - double float_value; - uint64_t int_value; - } cnv; - - if (sizeof(double) != 8) { - VGM_LOG("@UTF: can't convert double\n"); - goto fail; - } - - cnv.int_value = (uint64_t)read_64bitBE(data_offset, streamfile); - result->value.value_float = cnv.float_value; - break; - } -#endif - case COLUMN_TYPE_STRING: { - uint32_t name_offset = read_32bitBE(data_offset, streamfile); - if (name_offset > utf->strings_size) - goto fail; - result->value.value_string = utf->string_table + name_offset; - break; - } - - case COLUMN_TYPE_DATA: - result->value.value_data.offset = read_32bitBE(data_offset + 0x00, streamfile); - result->value.value_data.size = read_32bitBE(data_offset + 0x04, streamfile); - break; - - default: - goto fail; - } - - break; /* column found and read */ - } - - result->valid = 1; - return 1; -fail: - return 0; -} - -//////////////////////////////////////////////////////////// - -static int utf_query_value(STREAMFILE *streamfile, utf_context *utf, int row, const char* column, void* value, int type) { - utf_result result = {0}; - - utf_query(streamfile, utf, row, column, &result); - if (!result.valid || !result.found || result.type != type) - return 0; - - switch(result.type) { - case COLUMN_TYPE_SINT8: (*(int8_t*)value) = result.value.value_s8; break; - case COLUMN_TYPE_UINT8: (*(uint8_t*)value) = result.value.value_u8; break; - case COLUMN_TYPE_SINT16: (*(int16_t*)value) = result.value.value_s16; break; - case COLUMN_TYPE_UINT16: (*(uint16_t*)value) = result.value.value_u16; break; - case COLUMN_TYPE_SINT32: (*(int32_t*)value) = result.value.value_s32; break; - case COLUMN_TYPE_UINT32: (*(uint32_t*)value) = result.value.value_u32; break; - case COLUMN_TYPE_SINT64: (*(int64_t*)value) = result.value.value_s64; break; - //case COLUMN_TYPE_UINT64: (*(uint64_t*)value) = result.value.value_u64; break; - case COLUMN_TYPE_STRING: (*(const char**)value) = result.value.value_string; break; - default: - return 0; - } - - return 1; -} - -/*static*/ int utf_query_s8(STREAMFILE *streamfile, utf_context *utf, int row, const char* column, int8_t* value) { - return utf_query_value(streamfile, utf, row, column, (void*)value, COLUMN_TYPE_SINT8); -} -/*static*/ int utf_query_s16(STREAMFILE *streamfile, utf_context *utf, int row, const char* column, int16_t* value) { - return utf_query_value(streamfile, utf, row, column, (void*)value, COLUMN_TYPE_SINT16); -} -/*static*/ int utf_query_string(STREAMFILE *streamfile, utf_context *utf, int row, const char* column, const char* *value) { - return utf_query_value(streamfile, utf, row, column, (void*)value, COLUMN_TYPE_STRING); -} - -/*static*/ int utf_query_data(STREAMFILE *streamfile, utf_context *utf, int row, const char* column, uint32_t *offset, uint32_t *size) { - utf_result result = {0}; - - utf_query(streamfile, utf, row, column, &result); - if (!result.valid || !result.found || result.type != COLUMN_TYPE_DATA) - return 0; - - if (offset) *offset = utf->table_offset + utf->data_offset + result.value.value_data.offset; - if (size) *size = result.value.value_data.size; - return 1; -} - -#endif /* _ACB_UTF_H_ */ +#include "cri_utf.h" + +/* possibly 3b+5b from clUTF decompilation */ +#define COLUMN_BITMASK_STORAGE 0xf0 +#define COLUMN_BITMASK_TYPE 0x0f + +enum column_storage_t { + COLUMN_STORAGE_ZERO = 0x10, + COLUMN_STORAGE_CONSTANT = 0x30, + COLUMN_STORAGE_ROW = 0x50 + //COLUMN_STORAGE_CONSTANT2 = 0x70 /* from vgmtoolbox */ +}; + +enum column_type_t { + COLUMN_TYPE_SINT8 = 0x00, + COLUMN_TYPE_UINT8 = 0x01, + COLUMN_TYPE_SINT16 = 0x02, + COLUMN_TYPE_UINT16 = 0x03, + COLUMN_TYPE_SINT32 = 0x04, + COLUMN_TYPE_UINT32 = 0x05, + COLUMN_TYPE_SINT64 = 0x06, + //COLUMN_TYPE_UINT64 = 0x07, + COLUMN_TYPE_FLOAT = 0x08, + //COLUMN_TYPE_DOUBLE = 0x09, + COLUMN_TYPE_STRING = 0x0a, + COLUMN_TYPE_DATA = 0x0b +}; + +typedef struct { + int found; + enum column_type_t type; + union { + int8_t value_s8; + uint8_t value_u8; + int16_t value_s16; + uint16_t value_u16; + int32_t value_s32; + uint32_t value_u32; + int64_t value_s64; + uint64_t value_u64; + float value_float; + double value_double; + struct utf_data_t { + uint32_t offset; + uint32_t size; + } value_data; + const char *value_string; + } value; +} utf_result_t; + +struct utf_context { + STREAMFILE *sf; + uint32_t table_offset; + + /* header */ + uint32_t table_size; + uint16_t version; + uint16_t rows_offset; + uint32_t strings_offset; + uint32_t data_offset; + uint32_t name_offset; + uint16_t columns; + uint16_t row_width; + uint32_t rows; + struct utf_column_t { + uint8_t flags; + const char *name; + uint32_t offset; + } *schema; + + /* derived */ + uint32_t schema_offset; + uint32_t strings_size; + char *string_table; + const char *table_name; +}; + + +/* @UTF table context creation */ +utf_context* utf_open(STREAMFILE *sf, uint32_t table_offset, int *p_rows, const char **p_row_name) { + utf_context* utf = NULL; + + + utf = calloc(1, sizeof(utf_context)); + if (!utf) goto fail; + + utf->sf = sf; + utf->table_offset = table_offset; + + /* check header */ + if (read_u32be(table_offset + 0x00, sf) != 0x40555446) /* "@UTF" */ + goto fail; + + /* load table header */ + utf->table_size = read_u32be(table_offset + 0x04, sf) + 0x08; + utf->version = read_u16be(table_offset + 0x08, sf); + utf->rows_offset = read_u16be(table_offset + 0x0a, sf) + 0x08; + utf->strings_offset = read_u32be(table_offset + 0x0c, sf) + 0x08; + utf->data_offset = read_u32be(table_offset + 0x10, sf) + 0x08; + utf->name_offset = read_u32be(table_offset + 0x14, sf); /* within string table */ + utf->columns = read_u16be(table_offset + 0x18, sf); + utf->row_width = read_u16be(table_offset + 0x1a, sf); + utf->rows = read_u32be(table_offset + 0x1c, sf); + + utf->schema_offset = 0x20; + utf->strings_size = utf->data_offset - utf->strings_offset; + + /* 00: early (32b rows_offset?), 01: +2017 (no apparent differences) */ + if (utf->version != 0x00 && utf->version != 0x01) { + VGM_LOG("@UTF: unknown version\n"); + } + if (utf->table_offset + utf->table_size > get_streamfile_size(sf)) + goto fail; + if (utf->rows_offset > utf->table_size || utf->strings_offset > utf->table_size || utf->data_offset > utf->table_size) + goto fail; + if (utf->strings_size <= 0 || utf->name_offset > utf->strings_size) + goto fail; + /* no rows is possible for empty tables (have schema and columns names but no data) [PES 2013 (PC)] */ + if (utf->columns <= 0 /*|| utf->rows <= 0 || utf->rows_width <= 0*/) + goto fail; + + + /* load string table */ + { + size_t read; + + utf->string_table = calloc(utf->strings_size + 1, sizeof(char)); + if (!utf->string_table) goto fail; + + utf->table_name = utf->string_table + utf->name_offset; + + read = read_streamfile((unsigned char*)utf->string_table, utf->table_offset + utf->strings_offset, utf->strings_size, sf); + if (utf->strings_size != read) goto fail; + } + + + /* load column schema */ + { + int i; + uint32_t value_size, column_offset = 0; + uint32_t schema_offset = utf->table_offset + utf->schema_offset; + + + utf->schema = malloc(sizeof(struct utf_column_t) * utf->columns); + if (!utf->schema) goto fail; + + for (i = 0; i < utf->columns; i++) { + uint8_t flags = read_u8(schema_offset + 0x00, sf); + uint32_t name_offset = read_u32be(schema_offset + 0x01, sf); + if (name_offset > utf->strings_size) + goto fail; + + utf->schema[i].flags = flags; + utf->schema[i].name = utf->string_table + name_offset; + schema_offset += 0x01 + 0x04; + + switch (utf->schema[i].flags & COLUMN_BITMASK_TYPE) { + case COLUMN_TYPE_SINT8: + case COLUMN_TYPE_UINT8: + value_size = 0x01; + break; + case COLUMN_TYPE_SINT16: + case COLUMN_TYPE_UINT16: + value_size = 0x02; + break; + case COLUMN_TYPE_SINT32: + case COLUMN_TYPE_UINT32: + case COLUMN_TYPE_FLOAT: + case COLUMN_TYPE_STRING: + value_size = 0x04; + break; + case COLUMN_TYPE_SINT64: + //case COLUMN_TYPE_UINT64: + //case COLUMN_TYPE_DOUBLE: + case COLUMN_TYPE_DATA: + value_size = 0x08; + break; + default: + VGM_LOG("@UTF: unknown column type\n"); + goto fail; + } + + switch (utf->schema[i].flags & COLUMN_BITMASK_STORAGE) { + case COLUMN_STORAGE_ROW: + utf->schema[i].offset = column_offset; + column_offset += value_size; + break; + case COLUMN_STORAGE_CONSTANT: + //case COLUMN_STORAGE_CONSTANT2: + utf->schema[i].offset = schema_offset - (utf->table_offset + utf->schema_offset); /* relative to schema */ + schema_offset += value_size; + break; + case COLUMN_STORAGE_ZERO: + utf->schema[i].offset = 0; /* ? */ + break; + default: + VGM_LOG("@UTF: unknown column storage\n"); + goto fail; + } + } + } + + + /* write info */ + if (p_rows) *p_rows = utf->rows; + if (p_row_name) *p_row_name = utf->string_table + utf->name_offset; + + return utf; +fail: + utf_close(utf); + VGM_LOG("@UTF: fail\n"); + return NULL; +} + +void utf_close(utf_context *utf) { + if (!utf) return; + + free(utf->string_table); + free(utf->schema); + free(utf); +} + + +static int utf_query(utf_context *utf, int row, const char *column, utf_result_t *result) { + int i; + + + result->found = 0; + + if (row >= utf->rows || row < 0) + goto fail; + + /* find target column */ + for (i = 0; i < utf->columns; i++) { + struct utf_column_t *col = &utf->schema[i]; + uint32_t data_offset; + + if (strcmp(col->name, column) != 0) + continue; + + result->found = 1; + result->type = col->flags & COLUMN_BITMASK_TYPE; + + switch (col->flags & COLUMN_BITMASK_STORAGE) { + case COLUMN_STORAGE_ROW: + data_offset = utf->table_offset + utf->rows_offset + row * utf->row_width + col->offset; + break; + case COLUMN_STORAGE_CONSTANT: + //case COLUMN_STORAGE_CONSTANT2: + data_offset = utf->table_offset + utf->schema_offset + col->offset; + break; + case COLUMN_STORAGE_ZERO: + data_offset = 0; + memset(&result->value, 0, sizeof(result->value)); + break; + default: + goto fail; + } + + /* ignore zero value */ + if (!data_offset) + break; + + /* read row/constant value */ + switch (col->flags & COLUMN_BITMASK_TYPE) { + case COLUMN_TYPE_SINT8: + result->value.value_s8 = read_s8(data_offset, utf->sf); + break; + case COLUMN_TYPE_UINT8: + result->value.value_u8 = read_u8(data_offset, utf->sf); + break; + case COLUMN_TYPE_SINT16: + result->value.value_s16 = read_s16be(data_offset, utf->sf); + break; + case COLUMN_TYPE_UINT16: + result->value.value_u16 = read_u16be(data_offset, utf->sf); + break; + case COLUMN_TYPE_SINT32: + result->value.value_s32 = read_s32be(data_offset, utf->sf); + break; + case COLUMN_TYPE_UINT32: + result->value.value_u32 = read_u32be(data_offset, utf->sf); + break; + case COLUMN_TYPE_SINT64: + result->value.value_s64 = read_s64be(data_offset, utf->sf); + break; +#if 0 + case COLUMN_TYPE_UINT64: + result->value.value_u64 = read_u64be(data_offset, utf->sf); + break; +#endif + case COLUMN_TYPE_FLOAT: { + result->value.value_float = read_f32be(data_offset, utf->sf); + break; + } +#if 0 + case COLUMN_TYPE_DOUBLE: { + result->value.value_double = read_d64be(data_offset, utf->sf); + break; + } +#endif + case COLUMN_TYPE_STRING: { + uint32_t name_offset = read_u32be(data_offset, utf->sf); + if (name_offset > utf->strings_size) + goto fail; + result->value.value_string = utf->string_table + name_offset; + break; + } + + case COLUMN_TYPE_DATA: + result->value.value_data.offset = read_u32be(data_offset + 0x00, utf->sf); + result->value.value_data.size = read_u32be(data_offset + 0x04, utf->sf); + break; + + default: + goto fail; + } + + break; /* column found and read */ + } + + return 1; +fail: + return 0; +} + +static int utf_query_value(utf_context *utf, int row, const char *column, void *value, enum column_type_t type) { + utf_result_t result = {0}; + int valid; + + valid = utf_query(utf, row, column, &result); + if (!valid || !result.found || result.type != type) + return 0; + + switch(result.type) { + case COLUMN_TYPE_SINT8: (*(int8_t*)value) = result.value.value_s8; break; + case COLUMN_TYPE_UINT8: (*(uint8_t*)value) = result.value.value_u8; break; + case COLUMN_TYPE_SINT16: (*(int16_t*)value) = result.value.value_s16; break; + case COLUMN_TYPE_UINT16: (*(uint16_t*)value) = result.value.value_u16; break; + case COLUMN_TYPE_SINT32: (*(int32_t*)value) = result.value.value_s32; break; + case COLUMN_TYPE_UINT32: (*(uint32_t*)value) = result.value.value_u32; break; + case COLUMN_TYPE_SINT64: (*(int64_t*)value) = result.value.value_s64; break; + //case COLUMN_TYPE_UINT64: (*(uint64_t*)value) = result.value.value_u64; break; + case COLUMN_TYPE_STRING: (*(const char**)value) = result.value.value_string; break; + default: + return 0; + } + + return 1; +} + +int utf_query_s8(utf_context *utf, int row, const char *column, int8_t *value) { + return utf_query_value(utf, row, column, (void*)value, COLUMN_TYPE_SINT8); +} +int utf_query_s16(utf_context *utf, int row, const char *column, int16_t *value) { + return utf_query_value(utf, row, column, (void*)value, COLUMN_TYPE_SINT16); +} +int utf_query_s32(utf_context *utf, int row, const char *column, int32_t *value) { + return utf_query_value(utf, row, column, (void*)value, COLUMN_TYPE_SINT32); +} +int utf_query_string(utf_context *utf, int row, const char *column, const char **value) { + return utf_query_value(utf, row, column, (void*)value, COLUMN_TYPE_STRING); +} + +int utf_query_data(utf_context *utf, int row, const char *column, uint32_t *p_offset, uint32_t *p_size) { + utf_result_t result = {0}; + int valid; + + valid = utf_query(utf, row, column, &result); + if (!valid || !result.found || result.type != COLUMN_TYPE_DATA) + return 0; + + if (p_offset) *p_offset = utf->table_offset + utf->data_offset + result.value.value_data.offset; + if (p_size) *p_size = result.value.value_data.size; + return 1; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/cri_utf.c b/Frameworks/vgmstream/vgmstream/src/meta/cri_utf.c new file mode 100644 index 000000000..8c7c27d55 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/cri_utf.c @@ -0,0 +1,376 @@ +#include "cri_utf.h" + +/* possibly 3b+5b from clUTF decompilation */ +#define COLUMN_BITMASK_STORAGE 0xf0 +#define COLUMN_BITMASK_TYPE 0x0f + +enum column_storage_t { + COLUMN_STORAGE_ZERO = 0x10, + COLUMN_STORAGE_CONSTANT = 0x30, + COLUMN_STORAGE_ROW = 0x50 + //COLUMN_STORAGE_CONSTANT2 = 0x70 /* from vgmtoolbox */ +}; + +enum column_type_t { + COLUMN_TYPE_SINT8 = 0x00, + COLUMN_TYPE_UINT8 = 0x01, + COLUMN_TYPE_SINT16 = 0x02, + COLUMN_TYPE_UINT16 = 0x03, + COLUMN_TYPE_SINT32 = 0x04, + COLUMN_TYPE_UINT32 = 0x05, + COLUMN_TYPE_SINT64 = 0x06, + //COLUMN_TYPE_UINT64 = 0x07, + COLUMN_TYPE_FLOAT = 0x08, + //COLUMN_TYPE_DOUBLE = 0x09, + COLUMN_TYPE_STRING = 0x0a, + COLUMN_TYPE_DATA = 0x0b +}; + +typedef struct { + int found; + enum column_type_t type; + union { + int8_t value_s8; + uint8_t value_u8; + int16_t value_s16; + uint16_t value_u16; + int32_t value_s32; + uint32_t value_u32; + int64_t value_s64; + uint64_t value_u64; + float value_float; + double value_double; + struct utf_data_t { + uint32_t offset; + uint32_t size; + } value_data; + const char *value_string; + } value; +} utf_result_t; + +struct utf_context { + STREAMFILE *sf; + uint32_t table_offset; + + /* header */ + uint32_t table_size; + uint16_t version; + uint16_t rows_offset; + uint32_t strings_offset; + uint32_t data_offset; + uint32_t name_offset; + uint16_t columns; + uint16_t row_width; + uint32_t rows; + struct utf_column_t { + uint8_t flags; + const char *name; + uint32_t offset; + } *schema; + + /* derived */ + uint32_t schema_offset; + uint32_t strings_size; + char *string_table; + const char *table_name; +}; + + +/* @UTF table context creation */ +utf_context* utf_open(STREAMFILE *sf, uint32_t table_offset, int *p_rows, const char **p_row_name) { + utf_context* utf = NULL; + + + utf = calloc(1, sizeof(utf_context)); + if (!utf) goto fail; + + utf->sf = sf; + utf->table_offset = table_offset; + + /* check header */ + if (read_u32be(table_offset + 0x00, sf) != 0x40555446) /* "@UTF" */ + goto fail; + + /* load table header */ + utf->table_size = read_u32be(table_offset + 0x04, sf) + 0x08; + utf->version = read_u16be(table_offset + 0x08, sf); + utf->rows_offset = read_u16be(table_offset + 0x0a, sf) + 0x08; + utf->strings_offset = read_u32be(table_offset + 0x0c, sf) + 0x08; + utf->data_offset = read_u32be(table_offset + 0x10, sf) + 0x08; + utf->name_offset = read_u32be(table_offset + 0x14, sf); /* within string table */ + utf->columns = read_u16be(table_offset + 0x18, sf); + utf->row_width = read_u16be(table_offset + 0x1a, sf); + utf->rows = read_u32be(table_offset + 0x1c, sf); + + utf->schema_offset = 0x20; + utf->strings_size = utf->data_offset - utf->strings_offset; + + /* 00: early (32b rows_offset?), 01: +2017 (no apparent differences) */ + if (utf->version != 0x00 && utf->version != 0x01) { + VGM_LOG("@UTF: unknown version\n"); + } + if (utf->table_offset + utf->table_size > get_streamfile_size(sf)) + goto fail; + if (utf->rows_offset > utf->table_size || utf->strings_offset > utf->table_size || utf->data_offset > utf->table_size) + goto fail; + if (utf->strings_size <= 0 || utf->name_offset > utf->strings_size) + goto fail; + /* no rows is possible for empty tables (have schema and columns names but no data) [PES 2013 (PC)] */ + if (utf->columns <= 0 /*|| utf->rows <= 0 || utf->rows_width <= 0*/) + goto fail; + + + /* load string table */ + { + size_t read; + + utf->string_table = calloc(utf->strings_size + 1, sizeof(char)); + if (!utf->string_table) goto fail; + + utf->table_name = utf->string_table + utf->name_offset; + + read = read_streamfile((unsigned char*)utf->string_table, utf->table_offset + utf->strings_offset, utf->strings_size, sf); + if (utf->strings_size != read) goto fail; + } + + + /* load column schema */ + { + int i; + uint32_t value_size, column_offset = 0; + uint32_t schema_offset = utf->table_offset + utf->schema_offset; + + + utf->schema = malloc(sizeof(struct utf_column_t) * utf->columns); + if (!utf->schema) goto fail; + + for (i = 0; i < utf->columns; i++) { + uint8_t flags = read_u8(schema_offset + 0x00, sf); + uint32_t name_offset = read_u32be(schema_offset + 0x01, sf); + if (name_offset > utf->strings_size) + goto fail; + + utf->schema[i].flags = flags; + utf->schema[i].name = utf->string_table + name_offset; + schema_offset += 0x01 + 0x04; + + switch (utf->schema[i].flags & COLUMN_BITMASK_TYPE) { + case COLUMN_TYPE_SINT8: + case COLUMN_TYPE_UINT8: + value_size = 0x01; + break; + case COLUMN_TYPE_SINT16: + case COLUMN_TYPE_UINT16: + value_size = 0x02; + break; + case COLUMN_TYPE_SINT32: + case COLUMN_TYPE_UINT32: + case COLUMN_TYPE_FLOAT: + case COLUMN_TYPE_STRING: + value_size = 0x04; + break; + case COLUMN_TYPE_SINT64: + //case COLUMN_TYPE_UINT64: + //case COLUMN_TYPE_DOUBLE: + case COLUMN_TYPE_DATA: + value_size = 0x08; + break; + default: + VGM_LOG("@UTF: unknown column type\n"); + goto fail; + } + + switch (utf->schema[i].flags & COLUMN_BITMASK_STORAGE) { + case COLUMN_STORAGE_ROW: + utf->schema[i].offset = column_offset; + column_offset += value_size; + break; + case COLUMN_STORAGE_CONSTANT: + //case COLUMN_STORAGE_CONSTANT2: + utf->schema[i].offset = schema_offset - (utf->table_offset + utf->schema_offset); /* relative to schema */ + schema_offset += value_size; + break; + case COLUMN_STORAGE_ZERO: + utf->schema[i].offset = 0; /* ? */ + break; + default: + VGM_LOG("@UTF: unknown column storage\n"); + goto fail; + } + } + } + + + /* write info */ + if (p_rows) *p_rows = utf->rows; + if (p_row_name) *p_row_name = utf->string_table + utf->name_offset; + + return utf; +fail: + utf_close(utf); + VGM_LOG("@UTF: fail\n"); + return NULL; +} + +void utf_close(utf_context *utf) { + if (!utf) return; + + free(utf->string_table); + free(utf->schema); + free(utf); +} + + +static int utf_query(utf_context *utf, int row, const char *column, utf_result_t *result) { + int i; + + + result->found = 0; + + if (row >= utf->rows || row < 0) + goto fail; + + /* find target column */ + for (i = 0; i < utf->columns; i++) { + struct utf_column_t *col = &utf->schema[i]; + uint32_t data_offset; + + if (strcmp(col->name, column) != 0) + continue; + + result->found = 1; + result->type = col->flags & COLUMN_BITMASK_TYPE; + + switch (col->flags & COLUMN_BITMASK_STORAGE) { + case COLUMN_STORAGE_ROW: + data_offset = utf->table_offset + utf->rows_offset + row * utf->row_width + col->offset; + break; + case COLUMN_STORAGE_CONSTANT: + //case COLUMN_STORAGE_CONSTANT2: + data_offset = utf->table_offset + utf->schema_offset + col->offset; + break; + case COLUMN_STORAGE_ZERO: + data_offset = 0; + memset(&result->value, 0, sizeof(result->value)); + break; + default: + goto fail; + } + + /* ignore zero value */ + if (!data_offset) + break; + + /* read row/constant value */ + switch (col->flags & COLUMN_BITMASK_TYPE) { + case COLUMN_TYPE_SINT8: + result->value.value_s8 = read_s8(data_offset, utf->sf); + break; + case COLUMN_TYPE_UINT8: + result->value.value_u8 = read_u8(data_offset, utf->sf); + break; + case COLUMN_TYPE_SINT16: + result->value.value_s16 = read_s16be(data_offset, utf->sf); + break; + case COLUMN_TYPE_UINT16: + result->value.value_u16 = read_u16be(data_offset, utf->sf); + break; + case COLUMN_TYPE_SINT32: + result->value.value_s32 = read_s32be(data_offset, utf->sf); + break; + case COLUMN_TYPE_UINT32: + result->value.value_u32 = read_u32be(data_offset, utf->sf); + break; + case COLUMN_TYPE_SINT64: + result->value.value_s64 = read_s64be(data_offset, utf->sf); + break; +#if 0 + case COLUMN_TYPE_UINT64: + result->value.value_u64 = read_u64be(data_offset, utf->sf); + break; +#endif + case COLUMN_TYPE_FLOAT: { + result->value.value_float = read_f32be(data_offset, utf->sf); + break; + } +#if 0 + case COLUMN_TYPE_DOUBLE: { + result->value.value_double = read_d64be(data_offset, utf->sf); + break; + } +#endif + case COLUMN_TYPE_STRING: { + uint32_t name_offset = read_u32be(data_offset, utf->sf); + if (name_offset > utf->strings_size) + goto fail; + result->value.value_string = utf->string_table + name_offset; + break; + } + + case COLUMN_TYPE_DATA: + result->value.value_data.offset = read_u32be(data_offset + 0x00, utf->sf); + result->value.value_data.size = read_u32be(data_offset + 0x04, utf->sf); + break; + + default: + goto fail; + } + + break; /* column found and read */ + } + + return 1; +fail: + return 0; +} + +static int utf_query_value(utf_context *utf, int row, const char *column, void *value, enum column_type_t type) { + utf_result_t result = {0}; + int valid; + + valid = utf_query(utf, row, column, &result); + if (!valid || !result.found || result.type != type) + return 0; + + switch(result.type) { + case COLUMN_TYPE_SINT8: (*(int8_t*)value) = result.value.value_s8; break; + case COLUMN_TYPE_UINT8: (*(uint8_t*)value) = result.value.value_u8; break; + case COLUMN_TYPE_SINT16: (*(int16_t*)value) = result.value.value_s16; break; + case COLUMN_TYPE_UINT16: (*(uint16_t*)value) = result.value.value_u16; break; + case COLUMN_TYPE_SINT32: (*(int32_t*)value) = result.value.value_s32; break; + case COLUMN_TYPE_UINT32: (*(uint32_t*)value) = result.value.value_u32; break; + case COLUMN_TYPE_SINT64: (*(int64_t*)value) = result.value.value_s64; break; + //case COLUMN_TYPE_UINT64: (*(uint64_t*)value) = result.value.value_u64; break; + case COLUMN_TYPE_STRING: (*(const char**)value) = result.value.value_string; break; + default: + return 0; + } + + return 1; +} + +int utf_query_s8(utf_context *utf, int row, const char *column, int8_t *value) { + return utf_query_value(utf, row, column, (void*)value, COLUMN_TYPE_SINT8); +} +int utf_query_s16(utf_context *utf, int row, const char *column, int16_t *value) { + return utf_query_value(utf, row, column, (void*)value, COLUMN_TYPE_SINT16); +} +int utf_query_s32(utf_context *utf, int row, const char *column, int32_t *value) { + return utf_query_value(utf, row, column, (void*)value, COLUMN_TYPE_SINT32); +} +int utf_query_string(utf_context *utf, int row, const char *column, const char **value) { + return utf_query_value(utf, row, column, (void*)value, COLUMN_TYPE_STRING); +} + +int utf_query_data(utf_context *utf, int row, const char *column, uint32_t *p_offset, uint32_t *p_size) { + utf_result_t result = {0}; + int valid; + + valid = utf_query(utf, row, column, &result); + if (!valid || !result.found || result.type != COLUMN_TYPE_DATA) + return 0; + + if (p_offset) *p_offset = utf->table_offset + utf->data_offset + result.value.value_data.offset; + if (p_size) *p_size = result.value.value_data.size; + return 1; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/cri_utf.h b/Frameworks/vgmstream/vgmstream/src/meta/cri_utf.h new file mode 100644 index 000000000..1ceed7c9b --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/cri_utf.h @@ -0,0 +1,32 @@ +#ifndef _CRI_UTF_H_ +#define _CRI_UTF_H_ + +#include "../streamfile.h" + +/* CRI @UTF (Universal Table Format?) is a generic database-like table made of + * rows/columns that contain numbers/strings/binary data, which also can be other tables. + * + * A table starts with "@UTF" and defines some values (row/data/string offsets, counts, etc) + * then DB schema (columns type+name), then rows, string table and binary data. Formats using @UTF + * store and read data by row number + column name. Being a generic table with no fixed schema + * CRI uses it for different purposes (.acf: cues, .cpk: files, .aax: bgm, .usm: video, etc). + * + * (adapted from hcs's code to do multiple querys in the same table) + */ + +//todo move to src/util subdir + +/* opaque struct */ +typedef struct utf_context utf_context; + +/* open a CRI UTF table at offset, returning table name and rows. Passed streamfile is used internally for next calls */ +utf_context* utf_open(STREAMFILE *sf, uint32_t table_offset, int *p_rows, const char **p_row_name); +void utf_close(utf_context *utf); +/* query calls */ +int utf_query_s8(utf_context *utf, int row, const char *column, int8_t *value); +int utf_query_s16(utf_context *utf, int row, const char *column, int16_t *value); +int utf_query_s32(utf_context *utf, int row, const char *column, int32_t *value); +int utf_query_string(utf_context *utf, int row, const char *column, const char **value); +int utf_query_data(utf_context *utf, int row, const char *column, uint32_t *offset, uint32_t *size); + +#endif /* _CRI_UTF_H_ */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/csb.c b/Frameworks/vgmstream/vgmstream/src/meta/csb.c new file mode 100644 index 000000000..bd429694a --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/csb.c @@ -0,0 +1,151 @@ +#include "meta.h" +#include "../coding/coding.h" +#include "cri_utf.h" + + +/* 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; + 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; + int8_t fmt = 0; + const char *stream_name; + + + /* checks */ + if (!check_extensions(streamFile, "csb")) + goto fail; + if (read_32bitBE(0x00,streamFile) != 0x40555446) /* "@UTF" */ + goto fail; + + /* .csb is an early, simpler version of .acb+awk (see acb.c) used until ~2013? + * Can stream from .cpk but this only loads memory data. */ + { + int rows, sdl_rows, sdl_row, i; + const char *name; + const char *row_name; + const char *sdl_name; + uint32_t sdl_offset, sdl_size, offset, size; + uint32_t table_offset = 0x00; + int8_t ttype; + int found = 0; + + + utf = utf_open(streamFile, 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 + * - 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). + */ + 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) + goto fail; + if (!utf_query_s8(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); + if (!utf_sdl) goto fail; + + if (strcmp(sdl_name, "TBLSDL") != 0) + goto fail; + + total_subsongs = 0; + if (target_subsong == 0) target_subsong = 1; + + /* get target subsong */ + for (i = 0; i < sdl_rows; i++) { + int8_t stmflg; + + if (!utf_query_s8(utf_sdl, i, "stmflg", &stmflg)) + goto fail; + + /* only internal data for now (when 1 this refers to a .cpk subfile probably using "name", has size 0) */ + if (stmflg) + continue; + + total_subsongs++; + if (total_subsongs == target_subsong && !found) { + + if (!utf_query_string(utf_sdl, i, "name", &stream_name)) + goto fail; + if (!utf_query_data(utf_sdl, i, "data", &offset, &size)) + goto fail; + if (!utf_query_s8(utf_sdl, i, "fmt", &fmt)) + goto fail; + VGM_LOG("fmt=%i\n", fmt); + /* also nch/sfreq/nsmpl info */ + + found = 1; + } + } + + if (!found) goto fail; + if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail; +VGM_LOG("5: %x, %x\n", sdl_offset, offset); + subfile_offset = /*sdl_offset +*/ offset; + subfile_size = size; + + /* column exists but can be empty */ + if (subfile_size == 0) + goto fail; + } + + ;VGM_LOG("CSB: subfile offset=%lx + %x\n", subfile_offset, subfile_size); + + + temp_sf = setup_subfile_streamfile(streamFile, subfile_offset, subfile_size, "aax"); + if (!temp_sf) goto fail; + + VGM_LOG("7: %i\n", fmt); + + switch(fmt) { + case 0: /* AAX */ + case 6: /* HCA */ + vgmstream = init_vgmstream_aax(temp_sf); + if (!vgmstream) goto fail; + break; + + case 4: /* ADPCM_WII */ + vgmstream = init_vgmstream_utf_dsp(temp_sf); + if (!vgmstream) goto fail; + break; + + default: + VGM_LOG("CSB: unknown format %i\n", fmt); + goto fail; + } + + VGM_LOG("8\n"); + + vgmstream->num_streams = total_subsongs; + strncpy(vgmstream->stream_name, stream_name, STREAM_NAME_SIZE-1); + + utf_close(utf); + utf_close(utf_sdl); + close_streamfile(temp_sf); + return vgmstream; + +fail: + utf_close(utf); + utf_close(utf_sdl); + close_streamfile(temp_sf); + close_vgmstream(vgmstream); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ea_swvr.c b/Frameworks/vgmstream/vgmstream/src/meta/ea_swvr.c index 55ed54d3a..da1f1070f 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ea_swvr.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ea_swvr.c @@ -1,167 +1,167 @@ -#include "meta.h" -#include "../layout/layout.h" -#include "../coding/coding.h" - - -/* SWVR - from EA games, demuxed from .av/trk/mis/etc [Future Cop L.A.P.D. (PS/PC), Freekstyle (PS2/GC), EA Sports Supercross (PS)] */ -VGMSTREAM * init_vgmstream_ea_swvr(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - off_t start_offset; - int loop_flag = 0, channel_count, sample_rate, big_endian; - coding_t coding; - uint32_t block_id; - int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL; - int16_t (*read_16bit)(off_t,STREAMFILE*) = NULL; - int total_subsongs, target_subsong = streamFile->stream_index; - - - /* checks */ - /* .stream: common (found inside files), .str: shortened, probably unnecessary */ - if (!check_extensions(streamFile,"stream,str")) - goto fail; - - /* blocks ids are in machine endianness */ - if (read_32bitBE(0x00,streamFile) == 0x52565753) { /* "RVWS" (PS1/PS2/PC) */ - big_endian = 0; - read_32bit = read_32bitLE; - read_16bit = read_16bitLE; - start_offset = read_32bit(0x04, streamFile); - } - else if (read_32bitBE(0x00,streamFile) == 0x53575652) { /* "SWVR" (GC) */ - big_endian = 1; - read_32bit = read_32bitBE; - read_16bit = read_16bitBE; - start_offset = read_32bit(0x04, streamFile); - } - else if (read_32bitBE(0x00,streamFile) == 0x4D474156) { /* "MGAV", Freekstyle (PS2) raw movies */ - big_endian = 0; - read_32bit = read_32bitLE; - read_16bit = read_16bitLE; - start_offset = 0x00; - } - else if (read_32bitBE(0x00,streamFile) == 0x4453504D) { /* "DSPM", Freekstyle (GC) raw movies */ - big_endian = 1; - read_32bit = read_32bitBE; - read_16bit = read_16bitBE; - start_offset = 0x00; - } - else { - goto fail; - } - - start_offset = read_32bit(0x04, streamFile); - if (read_32bit(start_offset+0x00, streamFile) == 0x50414444) /* "PADD" (Freekstyle) */ - start_offset += read_32bit(start_offset+0x04, streamFile); - else if (read_32bit(start_offset+0x10, streamFile) == 0x53484452) /* "SHDR" (Future Cop PC) */ - start_offset += read_32bit(start_offset+0x04, streamFile); - - if (read_32bit(start_offset+0x00, streamFile) == 0x46494C4C) /* "FILL" (Freekstyle) */ - start_offset += read_32bit(start_offset+0x04, streamFile); - - total_subsongs = 1; - block_id = read_32bit(start_offset, streamFile); - - /* files are basically headerless so we inspect the first block - * Freekstyle uses multiblocks/subsongs (though some subsongs may be clones?) */ - switch(block_id) { - case 0x5641474D: /* "VAGM" */ - coding = coding_PSX; - if (read_16bit(start_offset+0x1a, streamFile) == 0x0024) { - total_subsongs = read_32bit(start_offset+0x0c, streamFile)+1; - sample_rate = 22050; - } - else { - sample_rate = 14008; - } - channel_count = 2; - break; - case 0x56414742: /* "VAGB" */ - coding = coding_PSX; - if (read_16bit(start_offset+0x1a, streamFile) == 0x6400) { - sample_rate = 22050; - } - else { - sample_rate = 14008; - } - channel_count = 1; - break; - case 0x4453504D: /* "DSPM" */ - coding = coding_NGC_DSP; - total_subsongs = read_32bit(start_offset+0x0c, streamFile)+1; - sample_rate = 22050; - channel_count = 2; - break; - case 0x44535042: /* "DSPB" */ - coding = coding_NGC_DSP; - channel_count = 1; - sample_rate = 22050; - break; - case 0x4D534943: /* "MSIC" */ - coding = coding_PCM8_U_int; - channel_count = 2; - sample_rate = 14008; - break; - case 0x53484F43: /* "SHOC" (a generic block but hopefully has PC sounds) */ - coding = coding_PCM8_U_int; - channel_count = 1; - sample_rate = 14008; - break; - default: - VGM_LOG("EA SWVR: unknown block id\n"); - goto fail; - } - - if (target_subsong == 0) target_subsong = 1; - if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail; - - loop_flag = 0;//(channel_count > 1); /* some Future Cop LAPD tracks repeat but other games have fadeouts */ - - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); - if (!vgmstream) goto fail; - - vgmstream->meta_type = meta_EA_SWVR; - vgmstream->sample_rate = sample_rate; - vgmstream->codec_endian = big_endian; - vgmstream->num_streams = total_subsongs; - vgmstream->stream_size = get_streamfile_size(streamFile) / total_subsongs; /* approx... */ - - vgmstream->coding_type = coding; - vgmstream->layout_type = layout_blocked_ea_swvr; - /* DSP coefs are loaded per block */ - /* some files (voices etc) decode with pops but seems a mastering problem */ - - if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) - goto fail; - - /* calc num_samples manually */ - { - int num_samples; - vgmstream->stream_index = target_subsong; /* needed to skip other subsong-blocks */ - vgmstream->next_block_offset = start_offset; - do { - block_update(vgmstream->next_block_offset,vgmstream); - switch(vgmstream->coding_type) { - case coding_PSX: num_samples = ps_bytes_to_samples(vgmstream->current_block_size,1); break; - case coding_NGC_DSP: num_samples = dsp_bytes_to_samples(vgmstream->current_block_size,1); break; - case coding_PCM8_U_int: num_samples = pcm_bytes_to_samples(vgmstream->current_block_size,1,8); break; - default: num_samples = 0; break; - } - vgmstream->num_samples += num_samples; - } - while (vgmstream->next_block_offset < get_streamfile_size(streamFile)); - block_update(start_offset, vgmstream); - } - - if (loop_flag) { - vgmstream->loop_start_sample = 0; - vgmstream->loop_end_sample = vgmstream->num_samples; - } - - return vgmstream; - -fail: - close_vgmstream(vgmstream); - return NULL; -} +#include "meta.h" +#include "../layout/layout.h" +#include "../coding/coding.h" + + +/* SWVR - from EA games, demuxed from .av/trk/mis/etc [Future Cop L.A.P.D. (PS/PC), Freekstyle (PS2/GC), EA Sports Supercross (PS)] */ +VGMSTREAM * init_vgmstream_ea_swvr(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + off_t start_offset; + int loop_flag = 0, channel_count, sample_rate, big_endian; + coding_t coding; + uint32_t block_id; + int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL; + int16_t (*read_16bit)(off_t,STREAMFILE*) = NULL; + int total_subsongs, target_subsong = streamFile->stream_index; + + + /* checks */ + /* .stream: common (found inside files) + * .str: shortened, probably unnecessary */ + if (!check_extensions(streamFile,"stream,str")) + goto fail; + + /* blocks ids are in machine endianness */ + if (read_32bitBE(0x00,streamFile) == 0x52565753) { /* "RVWS" (PS1/PS2/PC) */ + big_endian = 0; + read_32bit = read_32bitLE; + read_16bit = read_16bitLE; + start_offset = read_32bit(0x04, streamFile); + } + else if (read_32bitBE(0x00,streamFile) == 0x53575652) { /* "SWVR" (GC) */ + big_endian = 1; + read_32bit = read_32bitBE; + read_16bit = read_16bitBE; + start_offset = read_32bit(0x04, streamFile); + } + else if (read_32bitBE(0x00,streamFile) == 0x4D474156) { /* "MGAV", Freekstyle (PS2) raw movies */ + big_endian = 0; + read_32bit = read_32bitLE; + read_16bit = read_16bitLE; + start_offset = 0x00; + } + else if (read_32bitBE(0x00,streamFile) == 0x4453504D) { /* "DSPM", Freekstyle (GC) raw movies */ + big_endian = 1; + read_32bit = read_32bitBE; + read_16bit = read_16bitBE; + start_offset = 0x00; + } + else { + goto fail; + } + + if (read_32bit(start_offset+0x00, streamFile) == 0x50414444) /* "PADD" (Freekstyle) */ + start_offset += read_32bit(start_offset+0x04, streamFile); + else if (read_32bit(start_offset+0x10, streamFile) == 0x53484452) /* "SHDR" (Future Cop PC) */ + start_offset += read_32bit(start_offset+0x04, streamFile); + + if (read_32bit(start_offset+0x00, streamFile) == 0x46494C4C) /* "FILL" (Freekstyle) */ + start_offset += read_32bit(start_offset+0x04, streamFile); + + total_subsongs = 1; + block_id = read_32bit(start_offset, streamFile); + + /* files are basically headerless so we inspect the first block + * Freekstyle uses multiblocks/subsongs (though some subsongs may be clones?) */ + switch(block_id) { + case 0x5641474D: /* "VAGM" */ + coding = coding_PSX; + if (read_16bit(start_offset+0x1a, streamFile) == 0x0024) { + total_subsongs = read_32bit(start_offset+0x0c, streamFile)+1; + sample_rate = 22050; + } + else { + sample_rate = 14008; + } + channel_count = 2; + break; + case 0x56414742: /* "VAGB" */ + coding = coding_PSX; + if (read_16bit(start_offset+0x1a, streamFile) == 0x6400) { + sample_rate = 22050; + } + else { + sample_rate = 14008; + } + channel_count = 1; + break; + case 0x4453504D: /* "DSPM" */ + coding = coding_NGC_DSP; + total_subsongs = read_32bit(start_offset+0x0c, streamFile)+1; + sample_rate = 22050; + channel_count = 2; + break; + case 0x44535042: /* "DSPB" */ + coding = coding_NGC_DSP; + channel_count = 1; + sample_rate = 22050; + break; + case 0x4D534943: /* "MSIC" */ + coding = coding_PCM8_U_int; + channel_count = 2; + sample_rate = 14008; + break; + case 0x53484F43: /* "SHOC" (a generic block but hopefully has PC sounds) */ + coding = coding_PCM8_U_int; + channel_count = 1; + sample_rate = 14008; + break; + default: + VGM_LOG("EA SWVR: unknown block id\n"); + goto fail; + } + + if (target_subsong == 0) target_subsong = 1; + if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail; + + loop_flag = 0;//(channel_count > 1); /* some Future Cop LAPD tracks repeat but other games have fadeouts */ + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count,loop_flag); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_EA_SWVR; + vgmstream->sample_rate = sample_rate; + vgmstream->codec_endian = big_endian; + vgmstream->num_streams = total_subsongs; + vgmstream->stream_size = get_streamfile_size(streamFile) / total_subsongs; /* approx... */ + + vgmstream->coding_type = coding; + vgmstream->layout_type = layout_blocked_ea_swvr; + /* DSP coefs are loaded per block */ + /* some files (voices etc) decode with pops but seems a mastering problem */ + + if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + goto fail; + + /* calc num_samples manually */ + { + int num_samples; + vgmstream->stream_index = target_subsong; /* needed to skip other subsong-blocks */ + vgmstream->next_block_offset = start_offset; + do { + block_update(vgmstream->next_block_offset,vgmstream); + switch(vgmstream->coding_type) { + case coding_PSX: num_samples = ps_bytes_to_samples(vgmstream->current_block_size,1); break; + case coding_NGC_DSP: num_samples = dsp_bytes_to_samples(vgmstream->current_block_size,1); break; + case coding_PCM8_U_int: num_samples = pcm_bytes_to_samples(vgmstream->current_block_size,1,8); break; + default: num_samples = 0; break; + } + vgmstream->num_samples += num_samples; + } + while (vgmstream->next_block_offset < get_streamfile_size(streamFile)); + block_update(start_offset, vgmstream); + } + + if (loop_flag) { + vgmstream->loop_start_sample = 0; + vgmstream->loop_end_sample = vgmstream->num_samples; + } + + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ffmpeg.c b/Frameworks/vgmstream/vgmstream/src/meta/ffmpeg.c index 1c6a356ff..25552e369 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ffmpeg.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ffmpeg.c @@ -56,10 +56,13 @@ VGMSTREAM * init_vgmstream_ffmpeg_offset(STREAMFILE *streamFile, uint64_t start, num_samples = aac_get_samples(streamFile, 0x00, get_streamfile_size(streamFile)); } - /* hack for MP3 files (will return 0 samples if not an actual file) */ - if (!num_samples && check_extensions(streamFile, "mp3,lmp3")) { +#ifdef VGM_USE_MPEG + /* hack for MP3 files (will return 0 samples if not an actual file) + * .mus: Marc Ecko's Getting Up (PC) */ + if (!num_samples && check_extensions(streamFile, "mp3,lmp3,mus")) { num_samples = mpeg_get_samples(streamFile, 0x00, get_streamfile_size(streamFile)); } +#endif /* hack for MPC, that seeks/resets incorrectly due to seek table shenanigans */ if (read_32bitBE(0x00, streamFile) == 0x4D502B07 || /* "MP+\7" (Musepack V7) */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/hca_keys.h b/Frameworks/vgmstream/vgmstream/src/meta/hca_keys.h index 661a33597..e5e23eec6 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/hca_keys.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/hca_keys.h @@ -315,6 +315,18 @@ static const hcakey_info hcakey_list[] = { /* Dankira!!! Boys, be DANCING! (Android) */ {3957325206121219506}, // 36EB3E4EE38E05B2 + /* Idola: Phantasy Star Saga (Android) */ + {0xA86BF72B4C852CA7}, // A86BF72B4C852CA7 / 12136065386219383975 + + /* Arca Last (Android) */ + {612310807}, // 00000000247F1F17 / 12136065386219383975 + + /* ArkResona (Android) */ + {564321654321}, // 0000008364311631 + + /* Kemono Friends 3 (Android) */ + {3315495188}, // 00000000C59E7114 + /* Dragalia Lost (iOS/Android) */ {2967411924141, subkeys_dgl, sizeof(subkeys_dgl) / sizeof(subkeys_dgl[0]) }, // 000002B2E7889CAD diff --git a/Frameworks/vgmstream/vgmstream/src/meta/meta.h b/Frameworks/vgmstream/vgmstream/src/meta/meta.h index fa5119555..e9f5aa757 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/meta.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/meta.h @@ -366,7 +366,7 @@ VGMSTREAM * init_vgmstream_sat_baka(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_nds_swav(STREAMFILE *streamFile); -VGMSTREAM * init_vgmstream_ps2_vsf(STREAMFILE *streamFile); +VGMSTREAM * init_vgmstream_vsf(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_nds_rrds(STREAMFILE *streamFile); @@ -873,4 +873,8 @@ VGMSTREAM * init_vgmstream_isb(STREAMFILE * streamFile); VGMSTREAM* init_vgmstream_xssb(STREAMFILE *sf); +VGMSTREAM* init_vgmstream_xma_ue3(STREAMFILE *sf); + +VGMSTREAM* init_vgmstream_csb(STREAMFILE *sf); + #endif /*_META_H*/ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/riff.c b/Frameworks/vgmstream/vgmstream/src/meta/riff.c index e28afa7c4..0519069eb 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/riff.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/riff.c @@ -132,7 +132,7 @@ static int read_fmt(int big_endian, STREAMFILE * streamFile, off_t current_chunk } switch (fmt->codec) { - case 0x00: /* Yamaha AICA ADPCM (raw) [Headhunter (DC), Bomber hehhe (DC)] (unofficial) */ + case 0x00: /* Yamaha AICA ADPCM [Headhunter (DC), Bomber hehhe (DC)] (unofficial) */ if (fmt->bps != 4) goto fail; if (fmt->block_size != 0x02*fmt->channel_count) goto fail; fmt->coding_type = coding_AICA_int; @@ -173,7 +173,7 @@ static int read_fmt(int big_endian, STREAMFILE * streamFile, off_t current_chunk fmt->coding_type = coding_MS_IMA; break; - case 0x20: /* Yamaha AICA ADPCM (raw) [Takuyo/Dynamix/etc DC games] */ + case 0x20: /* Yamaha AICA ADPCM [Takuyo/Dynamix/etc DC games] */ 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 @@ -198,6 +198,15 @@ static int read_fmt(int big_endian, STREAMFILE * streamFile, off_t current_chunk goto fail; break; + case 0x0300: /* IMA ADPCM [Chrono Ma:gia (Android)] (unofficial) */ + if (fmt->bps != 4) goto fail; + if (fmt->block_size != 0x0400*fmt->channel_count) goto fail; + if (fmt->size != 0x14) goto fail; + if (fmt->channel_count != 1) goto fail; /* not seen */ + fmt->coding_type = coding_DVI_IMA; + /* real 0x300 is "Fujitsu FM Towns SND" with block align 0x01 */ + break; + case 0x0555: /* Level-5 0x555 ADPCM (unofficial) */ if (!mwv) goto fail; fmt->coding_type = coding_L5_555; @@ -354,18 +363,20 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { riff_size = read_32bitLE(0x04,streamFile); file_size = get_streamfile_size(streamFile); - /* some of Liar-soft's buggy RIFF+Ogg made with Soundforge [Shikkoku no Sharnoth (PC)] */ - if (riff_size+0x08+0x01 == file_size) - riff_size += 0x01; - /* some Xbox games do this [Dynasty Warriors 3 (Xbox), BloodRayne (Xbox)] */ - if (riff_size == file_size && read_16bitLE(0x14,streamFile)==0x0069) - riff_size -= 0x08; - /* some Dreamcast/Naomi games do this [Headhunter (DC), Bomber hehhe (DC)] */ - if (riff_size + 0x04 == file_size && read_16bitLE(0x14,streamFile)==0x0000) - riff_size -= 0x04; - /* some PC games do this [Halo 2 (PC)] (possibly bad extractor? 'Gravemind Tool') */ - if (riff_size + 0x04 == file_size && read_16bitLE(0x14,streamFile)==0x0069) - riff_size -= 0x04; + /* some games have wonky sizes, selectively fix to catch bad rips and new mutations */ + { + uint16_t codec = read_16bitLE(0x14,streamFile); + if (riff_size+0x08+0x01 == file_size) + riff_size += 0x01; /* [Shikkoku no Sharnoth (PC)] */ + else if (riff_size == file_size && codec == 0x0069) + riff_size -= 0x08; /* [Dynasty Warriors 3 (Xbox), BloodRayne (Xbox)] */ + else if (riff_size + 0x04 == file_size && codec == 0x0000) + riff_size -= 0x04; /* [Headhunter (DC), Bomber hehhe (DC)] */ + else if (riff_size + 0x04 == file_size && codec == 0x0069) + riff_size -= 0x04; /* [Halo 2 (PC)] (possibly bad extractor? 'Gravemind Tool') */ + else if (riff_size == file_size && codec == 0x0300) + riff_size -= 0x08; /* [Chrono Ma:gia (Android)] */ + } /* check for truncated RIFF */ if (file_size < riff_size+0x08) @@ -553,6 +564,7 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { case coding_AICA: case coding_XBOX_IMA: case coding_IMA: + case coding_DVI_IMA: #ifdef VGM_USE_FFMPEG case coding_FFmpeg: #endif @@ -640,6 +652,7 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { break; case coding_IMA: + case coding_DVI_IMA: vgmstream->num_samples = ima_bytes_to_samples(data_size, fmt.channel_count); break; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/vag.c b/Frameworks/vgmstream/vgmstream/src/meta/vag.c index e231679bd..35ddc30c3 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/vag.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/vag.c @@ -238,7 +238,13 @@ VGMSTREAM * init_vgmstream_vag(STREAMFILE *streamFile) { interleave = 0; channel_count = 1; - loop_flag = ps_find_loop_offsets_full(streamFile, start_offset, channel_size*channel_count, channel_count, interleave, &loop_start_sample, &loop_end_sample); + if (version == 0x20 /* hack for repeating full loops that aren't too small */ + && ps_bytes_to_samples(channel_size, 1) > 20 * sample_rate) { + loop_flag = ps_find_loop_offsets_full(streamFile, start_offset, channel_size*channel_count, channel_count, interleave, &loop_start_sample, &loop_end_sample); + } + else { + loop_flag = ps_find_loop_offsets(streamFile, start_offset, channel_size*channel_count, channel_count, interleave, &loop_start_sample, &loop_end_sample); + } allow_dual_stereo = 1; /* often found with external L/R files */ } break; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/vsf.c b/Frameworks/vgmstream/vgmstream/src/meta/vsf.c index 07751bea9..c06b416a4 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/vsf.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/vsf.c @@ -1,52 +1,58 @@ -#include "meta.h" -#include "../util.h" - -/* VSF (from Musashi: Samurai Legend) */ -VGMSTREAM * init_vgmstream_ps2_vsf(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - off_t start_offset; - int loop_flag, channel_count; - - /* check extension, case insensitive */ - if (!check_extensions(streamFile, "vsf")) - goto fail; - - /* check header */ - if (read_32bitBE(0x00,streamFile) != 0x56534600) /* "VSF" */ - goto fail; - - loop_flag = (read_32bitLE(0x1c,streamFile)==0x13); - if(read_8bit(0x1C,streamFile)==0x0) - channel_count = 1; - else - channel_count = 2; - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); - if (!vgmstream) goto fail; - - /* fill in the vital statistics */ - start_offset = 0x800; - vgmstream->channels = channel_count; - vgmstream->sample_rate = 44100; - vgmstream->coding_type = coding_PSX; - vgmstream->num_samples = read_32bitLE(0x10,streamFile)*28; - if (loop_flag) { - vgmstream->loop_start_sample = read_32bitLE(0x18,streamFile)*28; - vgmstream->loop_end_sample = vgmstream->num_samples; - } - - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = 0x400; - vgmstream->meta_type = meta_PS2_VSF; - - /* open the file for reading */ - if (!vgmstream_open_stream(vgmstream, streamFile, start_offset)) - goto fail; - return vgmstream; - - /* clean up anything we may have opened */ -fail: - close_vgmstream(vgmstream); - return NULL; -} +#include "meta.h" +#include "../coding/coding.h" + + +/* VSF - from Square Enix PS2 games between 2004-2006 [Musashi: Samurai Legend (PS2), Front Mission 5 (PS2)] */ +VGMSTREAM * init_vgmstream_vsf(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + off_t start_offset; + int loop_flag, channel_count, flags, pitch; + size_t channel_size, loop_start; + + /* checks */ + /* .vsf: header id and actual extension [Code Age Commanders (PS2)] */ + if (!check_extensions(streamFile, "vsf")) + goto fail; + if (read_32bitBE(0x00,streamFile) != 0x56534600) /* "VSF\0" */ + goto fail; + + /* 0x04: data size */ + /* 0x08: file number? */ + /* 0x0c: version? (always 0x00010000) */ + channel_size = read_32bitLE(0x10,streamFile) * 0x10; + /* 0x14: frame size */ + loop_start = read_32bitLE(0x18,streamFile) * 0x10; /* also in channel size */ + flags = read_32bitLE(0x1c,streamFile); + pitch = read_32bitLE(0x20,streamFile); + /* 0x24: volume? */ + /* 0x28: ? (may be 0) */ + /* rest is 0xFF */ + + channel_count = (flags & (1<<0)) ? 2 : 1; + loop_flag = (flags & (1<<1)); + start_offset = (flags & (1<<8)) ? 0x80 : 0x800; + /* flag (1<<4) is common but no apparent differences, no other flags known */ + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count,loop_flag); + if (!vgmstream) goto fail; + + vgmstream->sample_rate = round10((48000 * pitch) / 4096); + vgmstream->num_samples = ps_bytes_to_samples(channel_size, 1); + vgmstream->loop_start_sample = ps_bytes_to_samples(loop_start, 1); + vgmstream->loop_end_sample = vgmstream->num_samples; + + vgmstream->coding_type = coding_PSX; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x400; + vgmstream->meta_type = meta_VSF; + + if (!vgmstream_open_stream(vgmstream, streamFile, start_offset)) + goto fail; + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/wwise.c b/Frameworks/vgmstream/vgmstream/src/meta/wwise.c index f35718751..04393ebc5 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/wwise.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/wwise.c @@ -179,8 +179,8 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) { case 0xFFFE: ww.codec = PCM; break; /* "PCM for Wwise Authoring" */ case 0xFFFF: ww.codec = VORBIS; break; case 0x3039: ww.codec = OPUSNX; break; /* later renamed from "OPUS" */ -#if 0 case 0x3040: ww.codec = OPUS; break; +#if 0 case 0x8311: ww.codec = PTADPCM; break; #endif default: @@ -593,23 +593,20 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) { break; } -#if 0 - case OPUS: { /* PC/etc */ - ffmpeg_codec_data * ffmpeg_data = NULL; + case OPUS: { /* PC/mobile/etc, rare (most games still use Vorbis) [Girl Cafe Gun (Mobile)] */ if (ww.block_align != 0 || ww.bits_per_sample != 0) goto fail; - /* extra: size 0x12, unknown values, maybe num_samples/etc */ + /* extra: size 0x12 */ + vgmstream->num_samples = read_32bit(ww.fmt_offset + 0x18, streamFile); + /* 0x1c: stream size without OggS? */ + /* 0x20: full samples (without encoder delay) */ - ffmpeg_data = init_ffmpeg_offset(streamFile, ww.data_offset,ww.data_size); - if (!ffmpeg_data) goto fail; - vgmstream->codec_data = ffmpeg_data; + vgmstream->codec_data = init_ffmpeg_offset(streamFile, ww.data_offset,ww.data_size); + if (!vgmstream->codec_data) goto fail; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; - - vgmstream->num_samples = (int32_t)ffmpeg_data->totalSamples; break; } -#endif #endif case HEVAG: /* PSV */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/xma_ue3.c b/Frameworks/vgmstream/vgmstream/src/meta/xma_ue3.c new file mode 100644 index 000000000..197259416 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/xma_ue3.c @@ -0,0 +1,97 @@ +#include "meta.h" +#include "../coding/coding.h" + +/* XMA from Unreal Engine games */ +VGMSTREAM* init_vgmstream_xma_ue3(STREAMFILE *sf) { + VGMSTREAM* vgmstream = NULL; + off_t start_offset, chunk_offset; + int loop_flag, channel_count, sample_rate, is_xma2_old = 0; + int num_samples, loop_start_sample, loop_end_sample; + size_t file_size, fmt_size, seek_size, data_size; + + + /* checks */ + /* .xma: assumed */ + if (!check_extensions(sf, "xma,")) + goto fail; + + /* UE3 uses class-like chunks called "SoundNodeWave" to store info and (rarely multi) raw audio data. Other + * platforms use standard formats (PC=Ogg, PS3=MSF), while X360 has mutant XMA. Extractors transmogrify + * UE3 XMA into RIFF XMA (discarding seek table and changing endianness) but we'll support actual raw + * data for completeness. UE4 has .uexp which are very similar so XBone may use the same XMA. */ + + file_size = get_streamfile_size(sf); + fmt_size = read_u32be(0x00, sf); + seek_size = read_u32be(0x04, sf); + data_size = read_u32be(0x08, sf); + if (0x0c + fmt_size + seek_size + data_size != file_size) + goto fail; + chunk_offset = 0x0c; + + /* parse sample data (always BE unlike real XMA) */ + if (fmt_size != 0x34) { /* old XMA2 [The Last Remnant (X360)] */ + is_xma2_old = 1; + xma2_parse_xma2_chunk(sf, chunk_offset, &channel_count,&sample_rate, &loop_flag, &num_samples, &loop_start_sample, &loop_end_sample); + } + else { /* new XMA2 [Shadows of the Damned (X360)] */ + channel_count = read_s16be(chunk_offset + 0x02, sf); + sample_rate = read_s32be(chunk_offset + 0x04, sf); + xma2_parse_fmt_chunk_extra(sf, chunk_offset, &loop_flag, &num_samples, &loop_start_sample, &loop_end_sample, 1); + } + + start_offset = 0x0c + fmt_size + seek_size; + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count,loop_flag); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_XMA_UE3; + vgmstream->sample_rate = sample_rate; + vgmstream->num_samples = num_samples; + vgmstream->loop_start_sample = loop_start_sample; + vgmstream->loop_end_sample = loop_end_sample; + +#ifdef VGM_USE_FFMPEG + { + uint8_t buf[0x100]; + size_t bytes; + + if (is_xma2_old) { + bytes = ffmpeg_make_riff_xma2_from_xma2_chunk(buf,0x100, chunk_offset,fmt_size, data_size, sf); + } else { + bytes = ffmpeg_make_riff_xma_from_fmt_chunk(buf,0x100, chunk_offset,fmt_size, data_size, sf, 1); + } + vgmstream->codec_data = init_ffmpeg_header_offset(sf, buf,bytes, start_offset,data_size); + if (!vgmstream->codec_data) goto fail; + vgmstream->coding_type = coding_FFmpeg; + vgmstream->layout_type = layout_none; + + xma_fix_raw_samples(vgmstream, sf, start_offset, data_size, chunk_offset, 1,1); + } +#else + goto fail; +#endif + + /* UE3 seems to set full loops for non-looping tracks (real loops do exist though), try to detect */ + { + int full_loop, is_small; + + /* *must* be after xma_fix_raw_samples */ + full_loop = vgmstream->loop_start_sample == 0 && vgmstream->loop_end_sample == vgmstream->num_samples; + is_small = 1; //vgmstream->num_samples < 20 * vgmstream->sample_rate; /* all files */ + + if (full_loop && is_small) { + VGM_LOG("XMA UE3a: disabled unwanted loop\n"); + vgmstream->loop_flag = 0; + } + } + + + 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/vgmstream.c b/Frameworks/vgmstream/vgmstream/src/vgmstream.c index ce1c2486d..22be6fb56 100644 --- a/Frameworks/vgmstream/vgmstream/src/vgmstream.c +++ b/Frameworks/vgmstream/vgmstream/src/vgmstream.c @@ -188,7 +188,7 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_ngc_ffcc_str, init_vgmstream_sat_baka, init_vgmstream_nds_swav, - init_vgmstream_ps2_vsf, + init_vgmstream_vsf, init_vgmstream_nds_rrds, init_vgmstream_ps2_tk5, init_vgmstream_ps2_vsf_tta, @@ -484,6 +484,8 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_opus_sqex, init_vgmstream_isb, init_vgmstream_xssb, + init_vgmstream_xma_ue3, + init_vgmstream_csb, /* 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 e1c0f5c96..4f5ac7ba6 100644 --- a/Frameworks/vgmstream/vgmstream/src/vgmstream.h +++ b/Frameworks/vgmstream/vgmstream/src/vgmstream.h @@ -448,7 +448,7 @@ typedef enum { meta_WII_SNG, /* Excite Trucks */ meta_MUL, meta_SAT_BAKA, /* Crypt Killer */ - meta_PS2_VSF, /* Musashi: Samurai Legend */ + meta_VSF, meta_PS2_VSF_TTA, /* Tiny Toon Adventures: Defenders of the Universe */ meta_ADS, /* Gauntlet Dark Legends (GC) */ meta_PS2_SPS, /* Ape Escape 2 */ @@ -729,6 +729,7 @@ typedef enum { meta_BMP_KONAMI, meta_ISB, meta_XSSB, + meta_XMA_UE3, } meta_t;