Updated VGMStream to r1050-2726-g94dac3ec
parent
37b75bf6e7
commit
485b81fe04
|
@ -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 = "<group>"; };
|
||||
8306B0C62098458D000302D4 /* ubi_lyn_ogg_streamfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ubi_lyn_ogg_streamfile.h; sourceTree = "<group>"; };
|
||||
8306B0C72098458D000302D4 /* smv.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = smv.c; sourceTree = "<group>"; };
|
||||
8306B0C82098458D000302D4 /* aax_utf.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = aax_utf.h; sourceTree = "<group>"; };
|
||||
8306B0C92098458E000302D4 /* wave.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = wave.c; sourceTree = "<group>"; };
|
||||
8306B0CA2098458E000302D4 /* ubi_lyn.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ubi_lyn.c; sourceTree = "<group>"; };
|
||||
8306B0CB2098458E000302D4 /* msb_msh.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = msb_msh.c; sourceTree = "<group>"; };
|
||||
|
@ -1253,6 +1255,10 @@
|
|||
83F412871E932F9A002E37D0 /* Vorbis.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Vorbis.xcodeproj; path = ../Vorbis/macosx/Vorbis.xcodeproj; sourceTree = "<group>"; };
|
||||
83F5F8821908D0A400C8E65F /* fsb5.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = fsb5.c; sourceTree = "<group>"; };
|
||||
83FBD502235D31F700D35BCD /* riff_ogg_streamfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = riff_ogg_streamfile.h; sourceTree = "<group>"; };
|
||||
83FC176A23AC58D100E1025F /* xma_ue3.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xma_ue3.c; sourceTree = "<group>"; };
|
||||
83FC176B23AC58D100E1025F /* csb.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = csb.c; sourceTree = "<group>"; };
|
||||
83FC176C23AC58D100E1025F /* cri_utf.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cri_utf.h; sourceTree = "<group>"; };
|
||||
83FC177023AC59A800E1025F /* cri_utf.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = cri_utf.c; sourceTree = "<group>"; };
|
||||
83FF0EBB1E93282100C58054 /* wwise.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = wwise.c; sourceTree = "<group>"; };
|
||||
/* 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 */,
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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"},
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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_ */
|
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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_ */
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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) */
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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*/
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
Loading…
Reference in New Issue