Updated VGMStream to r1050-2696-g0a04a738
parent
8e9f8237e3
commit
90ac083705
|
@ -89,6 +89,8 @@
|
|||
8323894B1D22419B00482226 /* clHCA.h in Headers */ = {isa = PBXBuildFile; fileRef = 832389491D22419B00482226 /* clHCA.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
832389501D2246C300482226 /* hca.c in Sources */ = {isa = PBXBuildFile; fileRef = 8323894F1D2246C300482226 /* hca.c */; };
|
||||
832389521D224C0800482226 /* hca_decoder.c in Sources */ = {isa = PBXBuildFile; fileRef = 832389511D224C0800482226 /* hca_decoder.c */; };
|
||||
83269DD22399F5DE00F49FE3 /* nus3bank_streamfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 83269DD02399F5DD00F49FE3 /* nus3bank_streamfile.h */; };
|
||||
83269DD32399F5DE00F49FE3 /* ivag.c in Sources */ = {isa = PBXBuildFile; fileRef = 83269DD12399F5DE00F49FE3 /* ivag.c */; };
|
||||
83299FD01E7660C7003A3242 /* bik.c in Sources */ = {isa = PBXBuildFile; fileRef = 83299FCE1E7660C7003A3242 /* bik.c */; };
|
||||
83299FD11E7660C7003A3242 /* dsp_adx.c in Sources */ = {isa = PBXBuildFile; fileRef = 83299FCF1E7660C7003A3242 /* dsp_adx.c */; };
|
||||
832BF7FF21E050B7006F50F1 /* circus_decoder.c in Sources */ = {isa = PBXBuildFile; fileRef = 832BF7FC21E050B6006F50F1 /* circus_decoder.c */; };
|
||||
|
@ -378,7 +380,6 @@
|
|||
836F700E18BDC2190095E648 /* ps2_xa2.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6ED218BDC2190095E648 /* ps2_xa2.c */; };
|
||||
836F700F18BDC2190095E648 /* ps2_xa30.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6ED318BDC2190095E648 /* ps2_xa30.c */; };
|
||||
836F701118BDC2190095E648 /* ps3_cps.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6ED518BDC2190095E648 /* ps3_cps.c */; };
|
||||
836F701218BDC2190095E648 /* ps3_ivag.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6ED618BDC2190095E648 /* ps3_ivag.c */; };
|
||||
836F701518BDC2190095E648 /* ps3_past.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6ED918BDC2190095E648 /* ps3_past.c */; };
|
||||
836F701E18BDC2190095E648 /* redspark.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EE218BDC2190095E648 /* redspark.c */; };
|
||||
836F701F18BDC2190095E648 /* riff.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EE318BDC2190095E648 /* riff.c */; };
|
||||
|
@ -776,6 +777,8 @@
|
|||
832389491D22419B00482226 /* clHCA.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = clHCA.h; sourceTree = "<group>"; };
|
||||
8323894F1D2246C300482226 /* hca.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = hca.c; sourceTree = "<group>"; };
|
||||
832389511D224C0800482226 /* hca_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = hca_decoder.c; sourceTree = "<group>"; };
|
||||
83269DD02399F5DD00F49FE3 /* nus3bank_streamfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = nus3bank_streamfile.h; sourceTree = "<group>"; };
|
||||
83269DD12399F5DE00F49FE3 /* ivag.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ivag.c; sourceTree = "<group>"; };
|
||||
83299FCE1E7660C7003A3242 /* bik.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = bik.c; sourceTree = "<group>"; };
|
||||
83299FCF1E7660C7003A3242 /* dsp_adx.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = dsp_adx.c; sourceTree = "<group>"; };
|
||||
832BF7FC21E050B6006F50F1 /* circus_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = circus_decoder.c; sourceTree = "<group>"; };
|
||||
|
@ -1065,7 +1068,6 @@
|
|||
836F6ED218BDC2190095E648 /* ps2_xa2.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_xa2.c; sourceTree = "<group>"; };
|
||||
836F6ED318BDC2190095E648 /* ps2_xa30.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_xa30.c; sourceTree = "<group>"; };
|
||||
836F6ED518BDC2190095E648 /* ps3_cps.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps3_cps.c; sourceTree = "<group>"; };
|
||||
836F6ED618BDC2190095E648 /* ps3_ivag.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps3_ivag.c; sourceTree = "<group>"; };
|
||||
836F6ED918BDC2190095E648 /* ps3_past.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps3_past.c; sourceTree = "<group>"; };
|
||||
836F6EE218BDC2190095E648 /* redspark.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = redspark.c; sourceTree = "<group>"; };
|
||||
836F6EE318BDC2190095E648 /* riff.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = riff.c; sourceTree = "<group>"; };
|
||||
|
@ -1647,6 +1649,7 @@
|
|||
836F6E5518BDC2180095E648 /* ios_psnd.c */,
|
||||
83AFABBB23795202002F3947 /* isb.c */,
|
||||
836F6E5618BDC2180095E648 /* ish_isd.c */,
|
||||
83269DD12399F5DE00F49FE3 /* ivag.c */,
|
||||
836F6E5718BDC2180095E648 /* ivaud.c */,
|
||||
836F6E5818BDC2180095E648 /* ivb.c */,
|
||||
837CEAE923487F2B00E62A4A /* jstm_streamfile.h */,
|
||||
|
@ -1715,6 +1718,7 @@
|
|||
83C727FC22BC893900678B4A /* nps.c */,
|
||||
837CEAE223487F2A00E62A4A /* nub.c */,
|
||||
832BF81B21E0514B006F50F1 /* nus3audio.c */,
|
||||
83269DD02399F5DD00F49FE3 /* nus3bank_streamfile.h */,
|
||||
834FE0D1215C79E9000A5D3D /* nus3bank.c */,
|
||||
836F6E8118BDC2180095E648 /* nwa.c */,
|
||||
832BF81421E0514A006F50F1 /* nwav.c */,
|
||||
|
@ -1800,7 +1804,6 @@
|
|||
836F6ED218BDC2190095E648 /* ps2_xa2.c */,
|
||||
836F6ED318BDC2190095E648 /* ps2_xa30.c */,
|
||||
836F6ED518BDC2190095E648 /* ps3_cps.c */,
|
||||
836F6ED618BDC2190095E648 /* ps3_ivag.c */,
|
||||
836F6ED918BDC2190095E648 /* ps3_past.c */,
|
||||
837CEAE823487F2B00E62A4A /* psf.c */,
|
||||
83997F5722D9569E00633184 /* rad.c */,
|
||||
|
@ -2033,6 +2036,7 @@
|
|||
837CEAF323487F2C00E62A4A /* mzrt_streamfile.h in Headers */,
|
||||
8349A91B1FE6258200E26435 /* adx_keys.h in Headers */,
|
||||
836F6F4D18BDC2190095E648 /* layout.h in Headers */,
|
||||
83269DD22399F5DE00F49FE3 /* nus3bank_streamfile.h in Headers */,
|
||||
83AA5D251F6E2F9C0020821C /* hca_keys.h in Headers */,
|
||||
836F6F2318BDC2190095E648 /* coding.h in Headers */,
|
||||
);
|
||||
|
@ -2217,6 +2221,7 @@
|
|||
8301659C1F256BD000CA0941 /* nds_strm_ffta2.c in Sources */,
|
||||
837CEA7923487E2500E62A4A /* ubi_adpcm_decoder.c in Sources */,
|
||||
834FE0EB215C79ED000A5D3D /* str_wav.c in Sources */,
|
||||
83269DD32399F5DE00F49FE3 /* ivag.c in Sources */,
|
||||
8349A8DF1FE6251F00E26435 /* vorbis_custom_utils_vid1.c in Sources */,
|
||||
83A21F8D201D8982000F04B9 /* sqex_sead.c in Sources */,
|
||||
83EED5D3203A8BC7008BEB45 /* ea_swvr.c in Sources */,
|
||||
|
@ -2537,7 +2542,6 @@
|
|||
83A21F8A201D8982000F04B9 /* fsb_encrypted.c in Sources */,
|
||||
8306B0B120984552000302D4 /* blocked_halpst.c in Sources */,
|
||||
836F6FD018BDC2190095E648 /* ps2_b1s.c in Sources */,
|
||||
836F701218BDC2190095E648 /* ps3_ivag.c in Sources */,
|
||||
83AA5D181F6E2F600020821C /* mpeg_custom_utils_awc.c in Sources */,
|
||||
834FE0FE215C79ED000A5D3D /* ps_headerless.c in Sources */,
|
||||
8306B0BB20984552000302D4 /* blocked_gsb.c in Sources */,
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include "libatrac9.h"
|
||||
#endif
|
||||
|
||||
|
||||
/* opaque struct */
|
||||
struct atrac9_codec_data {
|
||||
uint8_t *data_buffer;
|
||||
|
|
|
@ -239,7 +239,7 @@ static int ps_find_loop_offsets_internal(STREAMFILE *streamFile, off_t start_off
|
|||
int detect_full_loops = config & 1;
|
||||
|
||||
|
||||
if (data_size == 0 || channels == 0 || (channels > 0 && interleave == 0))
|
||||
if (data_size == 0 || channels == 0 || (channels > 1 && interleave == 0))
|
||||
return 0;
|
||||
|
||||
while (offset < max_offset) {
|
||||
|
|
|
@ -32,6 +32,7 @@ static const char* extension_list[] = {
|
|||
//"aac", //common
|
||||
"aa3", //FFmpeg/not parsed (ATRAC3/ATRAC3PLUS/MP3/LPCM/WMA)
|
||||
"aax",
|
||||
"abc", //txth/reserved [Find My Own Way (PS2) tech demo]
|
||||
"abk",
|
||||
//"ac3", //common, FFmpeg/not parsed (AC3)
|
||||
"acb",
|
||||
|
@ -326,6 +327,7 @@ static const char* extension_list[] = {
|
|||
"nps",
|
||||
"npsf", //fake extension/header id for .nps (in bigfiles)
|
||||
"nub",
|
||||
"nub2",
|
||||
"nus3audio",
|
||||
"nus3bank",
|
||||
"nwa",
|
||||
|
@ -798,7 +800,7 @@ static const layout_info layout_info_list[] = {
|
|||
{layout_blocked_vs, "blocked (Melbourne House VS)"},
|
||||
{layout_blocked_mul, "blocked (MUL)"},
|
||||
{layout_blocked_gsb, "blocked (GSB)"},
|
||||
{layout_blocked_thp, "blocked (THP Movie Audio)"},
|
||||
{layout_blocked_thp, "blocked (THP)"},
|
||||
{layout_blocked_filp, "blocked (FILP)"},
|
||||
{layout_blocked_ea_swvr, "blocked (EA SWVR)"},
|
||||
{layout_blocked_adm, "blocked (ADM)"},
|
||||
|
@ -987,7 +989,7 @@ static const meta_info meta_info_list[] = {
|
|||
{meta_DCS_WAV, "In Utero DCS+WAV header"},
|
||||
{meta_SMP, "Infernal Engine .smp header"},
|
||||
{meta_MUL, "Crystal Dynamics .MUL header"},
|
||||
{meta_THP, "THP Movie File Format Header"},
|
||||
{meta_THP, "Nintendo THP header"},
|
||||
{meta_STS_WII, "Shikigami no Shiro (WII) Header"},
|
||||
{meta_PS2_P2BT, "Pop'n'Music 7 Header"},
|
||||
{meta_PS2_GBTS, "Pop'n'Music 9 Header"},
|
||||
|
@ -1098,7 +1100,7 @@ static const meta_info meta_info_list[] = {
|
|||
{meta_MN_STR, "Mini Ninjas 'STR' header"},
|
||||
{meta_MSS, "Guerilla MCSS header"},
|
||||
{meta_PS2_HSF, "Lowrider 'HSF' header"},
|
||||
{meta_PS3_IVAG, "PS3 'IVAG' Header"},
|
||||
{meta_IVAG, "Namco IVAG header"},
|
||||
{meta_PS2_2PFS, "Konami 2PFS header"},
|
||||
{meta_UBI_CKD, "Ubisoft CKD RIFF header"},
|
||||
{meta_PS2_VBK, "PS2 VBK Header"},
|
||||
|
@ -1107,7 +1109,7 @@ static const meta_info meta_info_list[] = {
|
|||
{meta_FSTM, "Nintendo FSTM Header"},
|
||||
{meta_KT_WIIBGM, "Koei Tecmo WiiBGM Header"},
|
||||
{meta_KTSS, "Koei Tecmo Nintendo Stream KTSS Header"},
|
||||
{meta_IDSP_NUS3, "Namco NUS3 IDSP header"},
|
||||
{meta_IDSP_NAMCO, "Namco IDSP header"},
|
||||
{meta_WIIU_BTSND, "Nintendo Wii U Menu Boot Sound"},
|
||||
{meta_MCA, "Capcom MCA header"},
|
||||
{meta_XB3D_ADX, "Xenoblade 3D ADX header"},
|
||||
|
|
|
@ -24,7 +24,7 @@ void block_update_ea_sns(off_t block_offset, VGMSTREAM * vgmstream) {
|
|||
/* At 0x00(1): block flag
|
||||
* - in SNS: 0x00=normal block, 0x80=last block (not mandatory)
|
||||
* - in SPS: 0x48=header, 0x44=normal block, 0x45=last block (empty) */
|
||||
block_id = (block_size & 0x00FFFFFF) >> 24;
|
||||
block_id = (block_size & 0xFF000000) >> 24;
|
||||
block_size &= 0x00FFFFFF;
|
||||
|
||||
if (block_id == 0x00 || block_id == 0x80 || block_id == 0x44) {
|
||||
|
|
|
@ -5,27 +5,37 @@
|
|||
void block_update_thp(off_t block_offset, VGMSTREAM *vgmstream) {
|
||||
int i, j;
|
||||
STREAMFILE *streamFile = vgmstream->ch[0].streamfile;
|
||||
off_t start_offset;
|
||||
int32_t nextFrameSize;
|
||||
off_t audio_offset;
|
||||
size_t next_block_size, video_size;
|
||||
|
||||
next_block_size = read_32bitBE(block_offset + 0x00, streamFile);
|
||||
/* 0x04: frame size previous */
|
||||
video_size = read_32bitBE(block_offset + 0x08,streamFile);
|
||||
/* 0x0c: audio size */
|
||||
|
||||
audio_offset = block_offset + 0x10 + video_size;
|
||||
|
||||
vgmstream->current_block_offset = block_offset;
|
||||
nextFrameSize=read_32bitBE(vgmstream->current_block_offset,streamFile);
|
||||
vgmstream->next_block_offset = block_offset + vgmstream->full_block_size;
|
||||
vgmstream->full_block_size = next_block_size;
|
||||
|
||||
vgmstream->next_block_offset = vgmstream->current_block_offset
|
||||
+ vgmstream->full_block_size;
|
||||
vgmstream->full_block_size = nextFrameSize;
|
||||
/* block samples can be smaller than block size, normally in the last block,
|
||||
* but num_samples already takes that into account, so there is no real difference */
|
||||
vgmstream->current_block_size = read_32bitBE(audio_offset + 0x00, streamFile);
|
||||
vgmstream->current_block_samples = read_32bitBE(audio_offset + 0x04, streamFile);
|
||||
|
||||
start_offset=vgmstream->current_block_offset
|
||||
+ read_32bitBE(vgmstream->current_block_offset+0x08,streamFile)+0x10;
|
||||
vgmstream->current_block_size=read_32bitBE(start_offset,streamFile);
|
||||
start_offset+=8;
|
||||
audio_offset += 0x08;
|
||||
|
||||
for (i = 0; i < vgmstream->channels; i++) {
|
||||
off_t coef_offset = audio_offset + i*0x20;
|
||||
off_t hist_offset = audio_offset + vgmstream->channels*0x20 + i*0x04;
|
||||
off_t data_offset = audio_offset + vgmstream->channels*0x24 + i*vgmstream->current_block_size;
|
||||
|
||||
for (j = 0; j < 16; j++) {
|
||||
vgmstream->ch[i].adpcm_coef[j]=read_16bitBE(start_offset+(i*0x20)+(j*2),streamFile);
|
||||
vgmstream->ch[i].adpcm_coef[j] = read_16bitBE(coef_offset + (j*0x02),streamFile);
|
||||
}
|
||||
vgmstream->ch[i].adpcm_history1_16=read_16bitBE(start_offset + (0x20*vgmstream->channels) + (i*4),streamFile);
|
||||
vgmstream->ch[i].adpcm_history2_16=read_16bitBE(start_offset + (0x20*vgmstream->channels) + (i*4) + 2,streamFile);
|
||||
vgmstream->ch[i].offset = start_offset + (0x24*vgmstream->channels)+(i*vgmstream->current_block_size);
|
||||
vgmstream->ch[i].adpcm_history1_16 = read_16bitBE(hist_offset + 0x00,streamFile);
|
||||
vgmstream->ch[i].adpcm_history2_16 = read_16bitBE(hist_offset + 0x02,streamFile);
|
||||
vgmstream->ch[i].offset = data_offset;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -184,6 +184,9 @@ static const adxkey_info adxkey8_list[] = {
|
|||
/* Gintama Gin-san to Issho! Boku no Kabukichou Nikki (PS2) [Bandai Namco?] */
|
||||
{0x67CD,0x5CA7,0x655F, "gt25809",0},
|
||||
|
||||
/* Lucky Star: RAvish Romance (PS2) [Vridge] */
|
||||
{0x5347,0x4FB7,0x6415, "LUCKYSRARPS2",0},
|
||||
|
||||
};
|
||||
|
||||
static const adxkey_info adxkey9_list[] = {
|
||||
|
@ -219,6 +222,9 @@ static const adxkey_info adxkey9_list[] = {
|
|||
/* Mashiro Witch (Android) */
|
||||
{0x2669,0x1495,0x2407, NULL,0x55D11D3349495204}, // 55D11D3349495204
|
||||
|
||||
/* Nogizaka46 Rhythm Festival (Android) */
|
||||
{0x2378,0x5511,0x0201, NULL,5613126134333697}, // 0013F11BC5510101
|
||||
|
||||
};
|
||||
|
||||
static const int adxkey8_list_count = sizeof(adxkey8_list) / sizeof(adxkey8_list[0]);
|
||||
|
|
|
@ -25,8 +25,7 @@
|
|||
|
||||
#define EAAC_TYPE_RAM 0x00
|
||||
#define EAAC_TYPE_STREAM 0x01
|
||||
|
||||
#define EAAC_LOOP_SET 0x01
|
||||
#define EAAC_TYPE_GIGASAMPLE 0x02
|
||||
|
||||
#define EAAC_BLOCKID0_DATA 0x00
|
||||
#define EAAC_BLOCKID0_END 0x80 /* maybe meant to be a bitflag? */
|
||||
|
@ -35,64 +34,32 @@
|
|||
#define EAAC_BLOCKID1_DATA 0x44 /* 'D' */
|
||||
#define EAAC_BLOCKID1_END 0x45 /* 'E' */
|
||||
|
||||
static VGMSTREAM * init_vgmstream_eaaudiocore_header(STREAMFILE * streamHead, STREAMFILE * streamData, off_t header_offset, off_t start_offset, meta_t meta_type);
|
||||
static size_t get_snr_size(STREAMFILE *streamFile, off_t offset);
|
||||
static VGMSTREAM * init_vgmstream_eaaudiocore_header(STREAMFILE * streamHead, STREAMFILE * streamData, off_t header_offset, off_t start_offset, meta_t meta_type, int standalone);
|
||||
static VGMSTREAM *parse_s10a_header(STREAMFILE *streamFile, off_t offset, uint16_t target_index, off_t ast_offset);
|
||||
VGMSTREAM * init_vgmstream_gin_header(STREAMFILE *streamFile, off_t offset);
|
||||
|
||||
|
||||
/* .SNR+SNS - from EA latest games (~2008-2013), v0 header */
|
||||
VGMSTREAM * init_vgmstream_ea_snr_sns(STREAMFILE * streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
STREAMFILE * streamData = NULL;
|
||||
|
||||
/* check extension, case insensitive */
|
||||
if (!check_extensions(streamFile,"snr"))
|
||||
goto fail;
|
||||
|
||||
/* SNR headers normally need an external SNS file, but some have data [Burnout Paradise, NFL2013 (iOS)] */
|
||||
if (get_streamfile_size(streamFile) > 0x10) {
|
||||
off_t start_offset = get_snr_size(streamFile, 0x00);
|
||||
vgmstream = init_vgmstream_eaaudiocore_header(streamFile, streamFile, 0x00, start_offset, meta_EA_SNR_SNS);
|
||||
if (!vgmstream) goto fail;
|
||||
}
|
||||
else {
|
||||
streamData = open_streamfile_by_ext(streamFile,"sns");
|
||||
if (!streamData) goto fail;
|
||||
|
||||
vgmstream = init_vgmstream_eaaudiocore_header(streamFile, streamData, 0x00, 0x00, meta_EA_SNR_SNS);
|
||||
if (!vgmstream) goto fail;
|
||||
}
|
||||
|
||||
close_streamfile(streamData);
|
||||
return vgmstream;
|
||||
return init_vgmstream_eaaudiocore_header(streamFile, NULL, 0x00, 0x00, meta_EA_SNR_SNS, 1);
|
||||
|
||||
fail:
|
||||
close_streamfile(streamData);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* .SPS - from EA latest games (~2014), v1 header */
|
||||
VGMSTREAM * init_vgmstream_ea_sps(STREAMFILE * streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
off_t start_offset;
|
||||
|
||||
/* check extension, case insensitive */
|
||||
if (!check_extensions(streamFile,"sps"))
|
||||
goto fail;
|
||||
|
||||
/* SPS block start: 0x00(1): block flag (header=0x48); 0x01(3): block size (usually 0x0c-0x14) */
|
||||
if (read_8bit(0x00, streamFile) != EAAC_BLOCKID1_HEADER)
|
||||
goto fail;
|
||||
start_offset = read_32bitBE(0x00, streamFile) & 0x00FFFFFF;
|
||||
|
||||
vgmstream = init_vgmstream_eaaudiocore_header(streamFile, streamFile, 0x04, start_offset, meta_EA_SPS);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
return vgmstream;
|
||||
return init_vgmstream_eaaudiocore_header(streamFile, NULL, 0x00, 0x00, meta_EA_SPS, 1);
|
||||
|
||||
fail:
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -125,7 +92,7 @@ VGMSTREAM * init_vgmstream_ea_snu(STREAMFILE *streamFile) {
|
|||
header_offset = 0x10; /* SNR header */
|
||||
start_offset = read_32bit(0x08,streamFile); /* SNS blocks */
|
||||
|
||||
vgmstream = init_vgmstream_eaaudiocore_header(streamFile, streamFile, header_offset, start_offset, meta_EA_SNU);
|
||||
vgmstream = init_vgmstream_eaaudiocore_header(streamFile, streamFile, header_offset, start_offset, meta_EA_SNU, 0);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
return vgmstream;
|
||||
|
@ -259,9 +226,9 @@ fail:
|
|||
}
|
||||
|
||||
/* EA S10A header - seen inside new ABK files. Putting it here in case it's encountered stand-alone. */
|
||||
static VGMSTREAM * parse_s10a_header(STREAMFILE *streamFile, off_t offset, uint16_t target_index, off_t ast_offset) {
|
||||
static VGMSTREAM * parse_s10a_header(STREAMFILE *streamFile, off_t offset, uint16_t target_index, off_t sns_offset) {
|
||||
uint32_t num_sounds;
|
||||
off_t snr_offset, sns_offset;
|
||||
off_t snr_offset;
|
||||
STREAMFILE *astFile = NULL;
|
||||
VGMSTREAM *vgmstream;
|
||||
|
||||
|
@ -279,11 +246,10 @@ static VGMSTREAM * parse_s10a_header(STREAMFILE *streamFile, off_t offset, uint1
|
|||
|
||||
snr_offset = offset + read_32bitBE(offset + 0x0C + 0x04 * target_index, streamFile);
|
||||
|
||||
if (ast_offset == 0xFFFFFFFF) {
|
||||
if (sns_offset == 0xFFFFFFFF) {
|
||||
/* RAM asset */
|
||||
sns_offset = snr_offset + get_snr_size(streamFile, snr_offset);
|
||||
//;VGM_LOG("EA S10A: RAM at sns=%lx, sns=%lx\n", snr_offset, sns_offset);
|
||||
vgmstream = init_vgmstream_eaaudiocore_header(streamFile, streamFile, snr_offset, sns_offset, meta_EA_SNR_SNS);
|
||||
//;VGM_LOG("EA S10A: RAM at snr=%lx", snr_offset);
|
||||
vgmstream = init_vgmstream_eaaudiocore_header(streamFile, NULL, snr_offset, 0x00, meta_EA_SNR_SNS, 0);
|
||||
if (!vgmstream)
|
||||
goto fail;
|
||||
} else {
|
||||
|
@ -295,9 +261,8 @@ static VGMSTREAM * parse_s10a_header(STREAMFILE *streamFile, off_t offset, uint1
|
|||
if (read_32bitBE(0x00, astFile) != 0x53313053) /* "S10S" */
|
||||
goto fail;
|
||||
|
||||
sns_offset = ast_offset;
|
||||
//;VGM_LOG("EA S10A: stream at sns=%lx, sns=%lx\n", snr_offset, sns_offset);
|
||||
vgmstream = init_vgmstream_eaaudiocore_header(streamFile, astFile, snr_offset, sns_offset, meta_EA_SNR_SNS);
|
||||
//;VGM_LOG("EA S10A: stream at snr=%lx, sns=%lx\n", snr_offset, sns_offset);
|
||||
vgmstream = init_vgmstream_eaaudiocore_header(streamFile, astFile, snr_offset, sns_offset, meta_EA_SNR_SNS, 0);
|
||||
if (!vgmstream)
|
||||
goto fail;
|
||||
|
||||
|
@ -373,16 +338,12 @@ VGMSTREAM * init_vgmstream_ea_sbr(STREAMFILE *streamFile) {
|
|||
if (read_32bitBE(0x00, sbsFile) != 0x53424B53) /* "SBKS" */
|
||||
goto fail;
|
||||
|
||||
snr_offset = sns_offset;
|
||||
sns_offset = snr_offset + (read_32bitBE(snr_offset, sbsFile) & 0x00FFFFFF);
|
||||
snr_offset += 0x04;
|
||||
vgmstream = init_vgmstream_eaaudiocore_header(sbsFile, sbsFile, snr_offset, sns_offset, meta_EA_SPS);
|
||||
vgmstream = init_vgmstream_eaaudiocore_header(sbsFile, NULL, sns_offset, 0x00, meta_EA_SPS, 0);
|
||||
if (!vgmstream)
|
||||
goto fail;
|
||||
} else if (sns_offset == 0) {
|
||||
/* RAM asset */
|
||||
sns_offset = snr_offset + get_snr_size(streamFile, snr_offset);
|
||||
vgmstream = init_vgmstream_eaaudiocore_header(streamFile, streamFile, snr_offset, sns_offset, meta_EA_SNR_SNS);
|
||||
vgmstream = init_vgmstream_eaaudiocore_header(streamFile, NULL, snr_offset, 0x00, meta_EA_SNR_SNS, 0);
|
||||
if (!vgmstream)
|
||||
goto fail;
|
||||
} else {
|
||||
|
@ -394,7 +355,7 @@ VGMSTREAM * init_vgmstream_ea_sbr(STREAMFILE *streamFile) {
|
|||
if (read_32bitBE(0x00, sbsFile) != 0x53424B53) /* "SBKS" */
|
||||
goto fail;
|
||||
|
||||
vgmstream = init_vgmstream_eaaudiocore_header(streamFile, sbsFile, snr_offset, sns_offset, meta_EA_SNR_SNS);
|
||||
vgmstream = init_vgmstream_eaaudiocore_header(streamFile, sbsFile, snr_offset, sns_offset, meta_EA_SNR_SNS, 0);
|
||||
if (!vgmstream)
|
||||
goto fail;
|
||||
}
|
||||
|
@ -428,6 +389,9 @@ VGMSTREAM * init_vgmstream_ea_hdr_sth_dat(STREAMFILE *streamFile) {
|
|||
/* 0x0C: zero */
|
||||
/* 0x10: table start */
|
||||
|
||||
if (!check_extensions(streamFile, "hdr"))
|
||||
goto fail;
|
||||
|
||||
if (read_8bit(0x09, streamFile) != 0)
|
||||
goto fail;
|
||||
|
||||
|
@ -520,7 +484,7 @@ VGMSTREAM * init_vgmstream_ea_hdr_sth_dat(STREAMFILE *streamFile) {
|
|||
if (block_id != EAAC_BLOCKID0_DATA && block_id != EAAC_BLOCKID0_END)
|
||||
goto fail;
|
||||
|
||||
vgmstream = init_vgmstream_eaaudiocore_header(sthFile, datFile, snr_offset, sns_offset, meta_EA_SNR_SNS);
|
||||
vgmstream = init_vgmstream_eaaudiocore_header(sthFile, datFile, snr_offset, sns_offset, meta_EA_SNR_SNS, 0);
|
||||
if (!vgmstream)
|
||||
goto fail;
|
||||
|
||||
|
@ -594,7 +558,7 @@ static STREAMFILE *open_mapfile_pair(STREAMFILE *streamFile, int track, int num_
|
|||
continue;
|
||||
}
|
||||
|
||||
strncpy(buf, mus_name, PATH_LIMIT);
|
||||
strncpy(buf, mus_name, PATH_LIMIT - 1);
|
||||
pch = strtok(buf, ","); //TODO: not thread safe in std C
|
||||
for (j = 0; j < track && pch; j++) {
|
||||
pch = strtok(NULL, ",");
|
||||
|
@ -603,9 +567,9 @@ static STREAMFILE *open_mapfile_pair(STREAMFILE *streamFile, int track, int num_
|
|||
|
||||
if (use_mask) {
|
||||
file_name[file_len - map_len] = '\0';
|
||||
strncat(file_name, pch + 1, PATH_LIMIT);
|
||||
strncat(file_name, pch + 1, PATH_LIMIT - 1);
|
||||
} else {
|
||||
strncpy(file_name, pch, PATH_LIMIT);
|
||||
strncpy(file_name, pch, PATH_LIMIT - 1);
|
||||
}
|
||||
|
||||
musFile = open_streamfile_by_filename(streamFile, file_name);
|
||||
|
@ -632,7 +596,7 @@ static STREAMFILE *open_mapfile_pair(STREAMFILE *streamFile, int track, int num_
|
|||
/* EA MPF/MUS combo - used in older 7th gen games for storing interactive music */
|
||||
VGMSTREAM * init_vgmstream_ea_mpf_mus_eaac(STREAMFILE *streamFile) {
|
||||
uint32_t num_tracks, track_start, track_hash, mus_sounds, mus_stream = 0;
|
||||
uint8_t version, sub_version, block_id;
|
||||
uint8_t version, sub_version;
|
||||
off_t tracks_table, samples_table, eof_offset, table_offset, entry_offset, snr_offset, sns_offset;
|
||||
int32_t(*read_32bit)(off_t, STREAMFILE*);
|
||||
STREAMFILE *musFile = NULL;
|
||||
|
@ -716,12 +680,7 @@ VGMSTREAM * init_vgmstream_ea_mpf_mus_eaac(STREAMFILE *streamFile) {
|
|||
table_offset = 0x08;
|
||||
entry_offset = table_offset + mus_stream * 0x0c;
|
||||
snr_offset = read_32bit(entry_offset + 0x04, musFile);
|
||||
|
||||
if (is_ram) {
|
||||
sns_offset = snr_offset + get_snr_size(musFile, snr_offset);
|
||||
} else {
|
||||
sns_offset = read_32bit(entry_offset + 0x08, musFile);
|
||||
}
|
||||
} else if (sub_version == 3) {
|
||||
/* number of files is always little endian */
|
||||
mus_sounds = read_32bitLE(0x04, musFile);
|
||||
|
@ -752,11 +711,7 @@ VGMSTREAM * init_vgmstream_ea_mpf_mus_eaac(STREAMFILE *streamFile) {
|
|||
goto fail;
|
||||
}
|
||||
|
||||
block_id = read_8bit(sns_offset, musFile);
|
||||
if (block_id != EAAC_BLOCKID0_DATA && block_id != EAAC_BLOCKID0_END)
|
||||
goto fail;
|
||||
|
||||
vgmstream = init_vgmstream_eaaudiocore_header(musFile, musFile, snr_offset, sns_offset, meta_EA_SNR_SNS);
|
||||
vgmstream = init_vgmstream_eaaudiocore_header(musFile, musFile, snr_offset, sns_offset, meta_EA_SNR_SNS, 0);
|
||||
if (!vgmstream)
|
||||
goto fail;
|
||||
|
||||
|
@ -773,7 +728,7 @@ fail:
|
|||
/* EA TMX - used for engine sounds in NFS games (2007-present) */
|
||||
VGMSTREAM * init_vgmstream_ea_tmx(STREAMFILE *streamFile) {
|
||||
uint32_t num_sounds, sound_type;
|
||||
off_t table_offset, data_offset, entry_offset, sound_offset, sns_offset;
|
||||
off_t table_offset, data_offset, entry_offset, sound_offset;
|
||||
VGMSTREAM *vgmstream = NULL;
|
||||
int target_stream = streamFile->stream_index;
|
||||
|
||||
|
@ -798,13 +753,11 @@ VGMSTREAM * init_vgmstream_ea_tmx(STREAMFILE *streamFile) {
|
|||
|
||||
switch (sound_type) {
|
||||
case 0x47494E20: /* "GIN " */
|
||||
/* FIXME: need to get GIN size somehow */
|
||||
vgmstream = init_vgmstream_gin_header(streamFile, sound_offset);
|
||||
if (!vgmstream) goto fail;
|
||||
break;
|
||||
case 0x534E5220: /* "SNR " */
|
||||
sns_offset = sound_offset + get_snr_size(streamFile, sound_offset);
|
||||
vgmstream = init_vgmstream_eaaudiocore_header(streamFile, streamFile, sound_offset, sns_offset, meta_EA_SNR_SNS);
|
||||
vgmstream = init_vgmstream_eaaudiocore_header(streamFile, NULL, sound_offset, 0x00, meta_EA_SNR_SNS, 0);
|
||||
if (!vgmstream) goto fail;
|
||||
break;
|
||||
default:
|
||||
|
@ -823,7 +776,7 @@ VGMSTREAM * init_vgmstream_ea_sbr_harmony(STREAMFILE *streamFile) {
|
|||
uint32_t num_dsets, set_sounds, chunk_id;
|
||||
uint32_t i;
|
||||
uint8_t set_type, flag, offset_size;
|
||||
off_t data_offset, table_offset, dset_offset, base_offset, sound_table_offset, sound_offset, header_offset, start_offset;
|
||||
off_t data_offset, table_offset, dset_offset, base_offset, sound_table_offset, sound_offset;
|
||||
STREAMFILE *sbsFile = NULL, *streamData = NULL;
|
||||
VGMSTREAM *vgmstream = NULL;
|
||||
int target_stream = streamFile->stream_index, total_sounds, local_target, is_streamed = 0;
|
||||
|
@ -973,12 +926,7 @@ VGMSTREAM * init_vgmstream_ea_sbr_harmony(STREAMFILE *streamFile) {
|
|||
}
|
||||
}
|
||||
|
||||
if (read_8bit(sound_offset, streamData) != EAAC_BLOCKID1_HEADER)
|
||||
goto fail;
|
||||
|
||||
header_offset = sound_offset + 0x04;
|
||||
start_offset = sound_offset + (read_32bitBE(sound_offset, streamData) & 0x00FFFFFF);
|
||||
vgmstream = init_vgmstream_eaaudiocore_header(streamData, streamData, header_offset, start_offset, meta_EA_SNR_SNS);
|
||||
vgmstream = init_vgmstream_eaaudiocore_header(streamData, NULL, sound_offset, 0x00, meta_EA_SPS, 0);
|
||||
if (!vgmstream)
|
||||
goto fail;
|
||||
|
||||
|
@ -999,7 +947,6 @@ typedef struct {
|
|||
int channel_config;
|
||||
int sample_rate;
|
||||
int type;
|
||||
int loop;
|
||||
|
||||
int streamed;
|
||||
int channels;
|
||||
|
@ -1008,25 +955,37 @@ typedef struct {
|
|||
int loop_start;
|
||||
int loop_end;
|
||||
int loop_flag;
|
||||
int prefetch_samples;
|
||||
|
||||
off_t stream_offset;
|
||||
off_t loop_offset;
|
||||
off_t prefetch_offset;
|
||||
} eaac_header;
|
||||
|
||||
static segmented_layout_data* build_segmented_eaaudiocore_looping(STREAMFILE *sf_data, eaac_header *eaac);
|
||||
static layered_layout_data* build_layered_eaaudiocore(STREAMFILE *streamFile, eaac_header *eaac);
|
||||
static size_t calculate_eaac_size(VGMSTREAM *vgmstream, STREAMFILE *streamFile, eaac_header *eaac, off_t start_offset);
|
||||
static layered_layout_data* build_layered_eaaudiocore(STREAMFILE *streamFile, eaac_header *eaac, off_t start_offset);
|
||||
static STREAMFILE *setup_eaac_streamfile(eaac_header *ea, STREAMFILE *streamHead, STREAMFILE *streamData);
|
||||
static size_t calculate_eaac_size(STREAMFILE *streamFile, eaac_header *ea, uint32_t num_samples, off_t start_offset);
|
||||
|
||||
/* EA newest header from RwAudioCore (RenderWare?) / EAAudioCore library (still generated by sx.exe).
|
||||
* Audio "assets" come in separate RAM headers (.SNR/SPH) and raw blocked streams (.SNS/SPS),
|
||||
* or together in pseudoformats (.SNU, .SBR+.SBS banks, .AEMS, .MUS, etc).
|
||||
* Some .SNR include stream data, while .SPS have headers so .SPH is optional. */
|
||||
static VGMSTREAM * init_vgmstream_eaaudiocore_header(STREAMFILE * streamHead, STREAMFILE * streamData, off_t header_offset, off_t start_offset, meta_t meta_type) {
|
||||
static VGMSTREAM * init_vgmstream_eaaudiocore_header(STREAMFILE * streamHead, STREAMFILE * streamData, off_t header_offset, off_t start_offset, meta_t meta_type, int standalone) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
STREAMFILE* temp_streamFile = NULL;
|
||||
uint32_t header1, header2;
|
||||
STREAMFILE* temp_streamFile = NULL, *streamFile = NULL, *snsFile = NULL;
|
||||
uint32_t header1, header2, header_block_size = 0, header_size;
|
||||
uint8_t header_block_id;
|
||||
eaac_header eaac = {0};
|
||||
|
||||
if (meta_type == meta_EA_SPS) {
|
||||
header_block_id = read_8bit(header_offset, streamHead);
|
||||
header_block_size = read_32bitBE(header_offset, streamHead) & 0x00FFFFFF;
|
||||
if (header_block_id != EAAC_BLOCKID1_HEADER)
|
||||
goto fail;
|
||||
|
||||
header_offset += 0x04;
|
||||
}
|
||||
|
||||
/* EA SNR/SPH header */
|
||||
header1 = (uint32_t)read_32bitBE(header_offset + 0x00, streamHead);
|
||||
|
@ -1036,14 +995,15 @@ static VGMSTREAM * init_vgmstream_eaaudiocore_header(STREAMFILE * streamHead, ST
|
|||
eaac.channel_config = (header1 >> 18) & 0x3F; /* 6 bits */
|
||||
eaac.sample_rate = (header1 >> 0) & 0x03FFFF; /* 18 bits */
|
||||
eaac.type = (header2 >> 30) & 0x03; /* 2 bits */
|
||||
eaac.loop = (header2 >> 29) & 0x01; /* 1 bits */
|
||||
eaac.loop_flag = (header2 >> 29) & 0x01; /* 1 bits */
|
||||
eaac.num_samples = (header2 >> 0) & 0x1FFFFFFF; /* 29 bits */
|
||||
/* rest is optional, depends on used flags and codec (handled below) */
|
||||
eaac.stream_offset = start_offset;
|
||||
|
||||
/* common channel configs are mono/stereo/quad/5.1/7.1 (from debug strings), while others are quite rare
|
||||
* [Battlefield 4 (X360)-EAXMA: 3/5/7ch, Army of Two: The Devil's Cartel (PS3)-EALayer3v2P: 11ch] */
|
||||
eaac.channels = eaac.channel_config + 1;
|
||||
/* EA 6ch channel mapping is L C R BL BR LFE, but may use stereo layers for dynamic music
|
||||
* instead, so we can't re-map automatically (use TXTP) */
|
||||
|
||||
/* V0: SNR+SNS, V1: SPR+SPS (no apparent differences, other than block flags) */
|
||||
if (eaac.version != EAAC_VERSION_V0 && eaac.version != EAAC_VERSION_V1) {
|
||||
|
@ -1057,53 +1017,33 @@ static VGMSTREAM * init_vgmstream_eaaudiocore_header(STREAMFILE * streamHead, ST
|
|||
goto fail;
|
||||
}
|
||||
|
||||
/* catch unknown values (0x02: "gigasample"? some kind of memory+stream thing?) */
|
||||
if (eaac.type != EAAC_TYPE_RAM && eaac.type != EAAC_TYPE_STREAM) {
|
||||
/* catch unknown values */
|
||||
if (eaac.type != EAAC_TYPE_RAM && eaac.type != EAAC_TYPE_STREAM && eaac.type != EAAC_TYPE_GIGASAMPLE) {
|
||||
VGM_LOG("EA EAAC: unknown type 0x%02x\n", eaac.type);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Non-streamed sounds are stored as a single block (may not set block end flags) */
|
||||
eaac.streamed = (eaac.type == EAAC_TYPE_STREAM);
|
||||
eaac.streamed = (eaac.type != EAAC_TYPE_RAM);
|
||||
|
||||
/* get loops (fairly involved due to the multiple layouts and mutant streamfiles)
|
||||
* full loops aren't too uncommon [Dead Space (PC) stream sfx/ambiance, FIFA 98 (PS3) RAM sfx],
|
||||
* full loops aren't too uncommon [Dead Space (PC) stream sfx/ambiance, FIFA 08 (PS3) RAM sfx],
|
||||
* while actual looping is very rare [Need for Speed: World (PC)-EAL3, The Simpsons Game (X360)-EAXMA] */
|
||||
if (eaac.loop == EAAC_LOOP_SET) {
|
||||
eaac.loop_flag = 1;
|
||||
|
||||
/* get optional header values */
|
||||
header_size = 0x08;
|
||||
if (eaac.loop_flag) {
|
||||
header_size += 0x04;
|
||||
eaac.loop_start = read_32bitBE(header_offset + 0x08, streamHead);
|
||||
eaac.loop_end = eaac.num_samples;
|
||||
|
||||
if (eaac.streamed) {
|
||||
eaac.loop_offset = read_32bitBE(header_offset+0x0c, streamHead);
|
||||
|
||||
if (eaac.version == EAAC_VERSION_V0) {
|
||||
/* SNR+SNS are separate so offsets are relative to the data start
|
||||
* (first .SNS block, or extra data before the .SNS block in case of .SNU) */
|
||||
eaac.loop_offset = eaac.stream_offset + eaac.loop_offset;
|
||||
} else {
|
||||
/* SPS have headers+data together so offsets are relative to the file start [ex. FIFA 18 (PC)] */
|
||||
eaac.loop_offset = header_offset - 0x04 + eaac.loop_offset;
|
||||
}
|
||||
}
|
||||
else if (eaac.loop_start > 0) {
|
||||
/* RAM assets have two blocks in case of actual loops */
|
||||
/* find the second block by getting the first block size */
|
||||
eaac.loop_offset = read_32bitBE(eaac.stream_offset, streamData) & 0x00FFFFF;
|
||||
eaac.loop_offset = eaac.stream_offset + eaac.loop_offset;
|
||||
}
|
||||
else {
|
||||
/* RAM assets have only one block in case of full loops */
|
||||
eaac.loop_offset = eaac.stream_offset; /* implicit */
|
||||
}
|
||||
|
||||
//todo EATrax has extra values in header, which would coexist with loop values
|
||||
/* TODO: EATrax has extra values in header, which would coexist with loop values */
|
||||
if (eaac.codec == EAAC_CODEC_EATRAX) {
|
||||
VGM_LOG("EA EAAC: unknown loop header for EATrax\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
//todo need more cases to test how layout/streamfiles react
|
||||
/* TODO: need more cases to test how layout/streamfiles react */
|
||||
if (eaac.loop_start > 0 && !(
|
||||
eaac.codec == EAAC_CODEC_EALAYER3_V1 ||
|
||||
eaac.codec == EAAC_CODEC_EALAYER3_V2_PCM ||
|
||||
|
@ -1115,9 +1055,74 @@ static VGMSTREAM * init_vgmstream_eaaudiocore_header(STREAMFILE * streamHead, ST
|
|||
}
|
||||
}
|
||||
|
||||
/* if type is gigasample there seems to be a field with number of "gigasamples in ram",
|
||||
* that goes after loop_start but before streamed/gigasamples' eaac.loop_offset) */
|
||||
switch (eaac.type) {
|
||||
case EAAC_TYPE_RAM:
|
||||
break;
|
||||
case EAAC_TYPE_STREAM:
|
||||
if (eaac.loop_flag) {
|
||||
header_size += 0x04;
|
||||
eaac.loop_offset = read_32bitBE(header_offset + 0x0c, streamHead);
|
||||
}
|
||||
break;
|
||||
case EAAC_TYPE_GIGASAMPLE: /* rarely seen [Def Jam Icon (X360)] */
|
||||
if (eaac.loop_flag) {
|
||||
VGM_LOG("EAAC: Looped gigasample found.\n");
|
||||
goto fail;
|
||||
}
|
||||
header_size += 0x04;
|
||||
eaac.prefetch_samples = read_32bitBE(header_offset + 0x08, streamHead);
|
||||
break;
|
||||
}
|
||||
|
||||
/* get data offsets */
|
||||
if (eaac.version == EAAC_VERSION_V0) {
|
||||
switch (eaac.type) {
|
||||
case EAAC_TYPE_RAM:
|
||||
eaac.stream_offset = header_offset + header_size;
|
||||
break;
|
||||
case EAAC_TYPE_STREAM:
|
||||
eaac.stream_offset = start_offset;
|
||||
break;
|
||||
case EAAC_TYPE_GIGASAMPLE:
|
||||
eaac.prefetch_offset = header_offset + header_size;
|
||||
eaac.stream_offset = start_offset;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
eaac.stream_offset = header_offset - 0x04 + header_block_size;
|
||||
}
|
||||
|
||||
/* correct loop offsets */
|
||||
if (eaac.loop_flag) {
|
||||
if (eaac.streamed) {
|
||||
/* SNR+SNS are separate so offsets are relative to the data start
|
||||
* (first .SNS block, or extra data before the .SNS block in case of .SNU)
|
||||
* SPS have headers+data together so offsets are relative to the file start [ex. FIFA 18 (PC)] */
|
||||
if (eaac.version == EAAC_VERSION_V1) {
|
||||
eaac.loop_offset -= header_block_size;
|
||||
}
|
||||
} else if (eaac.loop_start > 0) {
|
||||
/* RAM assets have two blocks in case of actual loops */
|
||||
/* find the second block by getting the first block size */
|
||||
eaac.loop_offset = read_32bitBE(eaac.stream_offset, streamHead) & 0x00FFFFFF;
|
||||
} else {
|
||||
/* RAM assets have only one block in case of full loops */
|
||||
eaac.loop_offset = 0x00; /* implicit */
|
||||
}
|
||||
}
|
||||
|
||||
if (eaac.version == EAAC_VERSION_V0 && eaac.streamed) {
|
||||
/* open SNS file if needed */
|
||||
if (standalone) {
|
||||
snsFile = open_streamfile_by_ext(streamHead, "sns");
|
||||
streamData = snsFile;
|
||||
}
|
||||
if (!streamData) goto fail;
|
||||
}
|
||||
|
||||
/* build streamfile with audio data */
|
||||
streamFile = setup_eaac_streamfile(&eaac, streamHead, streamData);
|
||||
if (!streamFile) goto fail;
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(eaac.channels,eaac.loop_flag);
|
||||
|
@ -1128,6 +1133,7 @@ static VGMSTREAM * init_vgmstream_eaaudiocore_header(STREAMFILE * streamHead, ST
|
|||
vgmstream->loop_start_sample = eaac.loop_start;
|
||||
vgmstream->loop_end_sample = eaac.loop_end;
|
||||
vgmstream->meta_type = meta_type;
|
||||
vgmstream->stream_size = get_streamfile_size(streamFile);
|
||||
|
||||
/* EA decoder list and known internal FourCCs */
|
||||
switch(eaac.codec) {
|
||||
|
@ -1143,14 +1149,14 @@ static VGMSTREAM * init_vgmstream_eaaudiocore_header(STREAMFILE * streamHead, ST
|
|||
|
||||
/* special (if hacky) loop handling, see comments */
|
||||
if (eaac.loop_start > 0) {
|
||||
segmented_layout_data *data = build_segmented_eaaudiocore_looping(streamData, &eaac);
|
||||
segmented_layout_data *data = build_segmented_eaaudiocore_looping(streamFile, &eaac);
|
||||
if (!data) goto fail;
|
||||
vgmstream->layout_data = data;
|
||||
vgmstream->coding_type = data->segments[0]->coding_type;
|
||||
vgmstream->layout_type = layout_segmented;
|
||||
}
|
||||
else {
|
||||
vgmstream->layout_data = build_layered_eaaudiocore(streamData, &eaac);
|
||||
vgmstream->layout_data = build_layered_eaaudiocore(streamFile, &eaac, 0x00);
|
||||
if (!vgmstream->layout_data) goto fail;
|
||||
vgmstream->coding_type = coding_FFmpeg;
|
||||
vgmstream->layout_type = layout_layered;
|
||||
|
@ -1164,7 +1170,7 @@ static VGMSTREAM * init_vgmstream_eaaudiocore_header(STREAMFILE * streamHead, ST
|
|||
|
||||
/* special (if hacky) loop handling, see comments */
|
||||
if (eaac.loop_start > 0) {
|
||||
segmented_layout_data *data = build_segmented_eaaudiocore_looping(streamData, &eaac);
|
||||
segmented_layout_data *data = build_segmented_eaaudiocore_looping(streamFile, &eaac);
|
||||
if (!data) goto fail;
|
||||
vgmstream->layout_data = data;
|
||||
vgmstream->coding_type = data->segments[0]->coding_type;
|
||||
|
@ -1187,21 +1193,19 @@ static VGMSTREAM * init_vgmstream_eaaudiocore_header(STREAMFILE * streamHead, ST
|
|||
* and to properly apply discard modes (see ealayer3 decoder)
|
||||
* (otherwise, and after removing discard code, it'd work with layout_blocked_ea_sns) */
|
||||
|
||||
start_offset = 0x00; /* must point to the custom streamfile's beginning */
|
||||
|
||||
/* special (if hacky) loop handling, see comments */
|
||||
if (eaac.loop_start > 0) {
|
||||
segmented_layout_data *data = build_segmented_eaaudiocore_looping(streamData, &eaac);
|
||||
segmented_layout_data *data = build_segmented_eaaudiocore_looping(streamFile, &eaac);
|
||||
if (!data) goto fail;
|
||||
vgmstream->layout_data = data;
|
||||
vgmstream->coding_type = data->segments[0]->coding_type;
|
||||
vgmstream->layout_type = layout_segmented;
|
||||
}
|
||||
else {
|
||||
temp_streamFile = setup_eaac_streamfile(streamData, eaac.version, eaac.codec, eaac.streamed,0,0, eaac.stream_offset);
|
||||
temp_streamFile = setup_eaac_audio_streamfile(streamFile, eaac.version, eaac.codec, eaac.streamed,0,0, 0x00);
|
||||
if (!temp_streamFile) goto fail;
|
||||
|
||||
vgmstream->codec_data = init_mpeg_custom(temp_streamFile, start_offset, &vgmstream->coding_type, vgmstream->channels, type, &cfg);
|
||||
vgmstream->codec_data = init_mpeg_custom(temp_streamFile, 0x00, &vgmstream->coding_type, vgmstream->channels, type, &cfg);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->layout_type = layout_none;
|
||||
}
|
||||
|
@ -1222,8 +1226,6 @@ static VGMSTREAM * init_vgmstream_eaaudiocore_header(STREAMFILE * streamHead, ST
|
|||
|
||||
/* EATrax is "buffered" ATRAC9, uses custom IO since it's kind of complex to add to the decoder */
|
||||
|
||||
start_offset = 0x00; /* must point to the custom streamfile's beginning */
|
||||
|
||||
cfg.channels = eaac.channels;
|
||||
cfg.config_data = read_32bitBE(header_offset + 0x08,streamHead);
|
||||
/* 0x10: frame size? (same as config data?) */
|
||||
|
@ -1235,7 +1237,7 @@ static VGMSTREAM * init_vgmstream_eaaudiocore_header(STREAMFILE * streamHead, ST
|
|||
vgmstream->coding_type = coding_ATRAC9;
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
||||
temp_streamFile = setup_eaac_streamfile(streamData, eaac.version, eaac.codec, eaac.streamed,0,0, eaac.stream_offset);
|
||||
temp_streamFile = setup_eaac_audio_streamfile(streamFile, eaac.version, eaac.codec, eaac.streamed,0,0, 0x00);
|
||||
if (!temp_streamFile) goto fail;
|
||||
|
||||
break;
|
||||
|
@ -1247,12 +1249,10 @@ static VGMSTREAM * init_vgmstream_eaaudiocore_header(STREAMFILE * streamHead, ST
|
|||
case EAAC_CODEC_EAMP3: { /* "EM30"?: EAMP3 [Need for Speed 2015 (PS4)] */
|
||||
mpeg_custom_config cfg = {0};
|
||||
|
||||
start_offset = 0x00; /* must point to the custom streamfile's beginning */
|
||||
|
||||
temp_streamFile = setup_eaac_streamfile(streamData, eaac.version, eaac.codec, eaac.streamed,0,0, eaac.stream_offset);
|
||||
temp_streamFile = setup_eaac_audio_streamfile(streamFile, eaac.version, eaac.codec, eaac.streamed,0,0, 0x00);
|
||||
if (!temp_streamFile) goto fail;
|
||||
|
||||
vgmstream->codec_data = init_mpeg_custom(temp_streamFile, start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_EAMP3, &cfg);
|
||||
vgmstream->codec_data = init_mpeg_custom(temp_streamFile, 0x00, &vgmstream->coding_type, vgmstream->channels, MPEG_EAMP3, &cfg);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
||||
|
@ -1263,7 +1263,7 @@ static VGMSTREAM * init_vgmstream_eaaudiocore_header(STREAMFILE * streamHead, ST
|
|||
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
case EAAC_CODEC_EAOPUS: { /* EAOpus (unknown FourCC) [FIFA 17 (PC), FIFA 19 (Switch)]*/
|
||||
vgmstream->layout_data = build_layered_eaaudiocore(streamData, &eaac);
|
||||
vgmstream->layout_data = build_layered_eaaudiocore(streamFile, &eaac, 0x00);
|
||||
if (!vgmstream->layout_data) goto fail;
|
||||
vgmstream->coding_type = coding_FFmpeg;
|
||||
vgmstream->layout_type = layout_layered;
|
||||
|
@ -1277,73 +1277,162 @@ static VGMSTREAM * init_vgmstream_eaaudiocore_header(STREAMFILE * streamHead, ST
|
|||
goto fail;
|
||||
}
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream, temp_streamFile ? temp_streamFile : streamData, start_offset))
|
||||
if (!vgmstream_open_stream(vgmstream, temp_streamFile ? temp_streamFile : streamFile, 0x00))
|
||||
goto fail;
|
||||
|
||||
if (eaac.loop_start == 0) {
|
||||
vgmstream->stream_size = calculate_eaac_size(vgmstream, temp_streamFile ? temp_streamFile : streamData, &eaac, start_offset);
|
||||
}
|
||||
|
||||
close_streamfile(streamFile);
|
||||
close_streamfile(snsFile);
|
||||
close_streamfile(temp_streamFile);
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_streamfile(streamFile);
|
||||
close_streamfile(snsFile);
|
||||
close_streamfile(temp_streamFile);
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static size_t get_snr_size(STREAMFILE *streamFile, off_t offset) {
|
||||
//const int EAAC_FLAG_LOOPED = 0x02;
|
||||
//const int EAAC_FLAG_STREAMED = 0x04;
|
||||
switch (read_8bit(offset + 0x04, streamFile) >> 4 & 0x0F) { /* flags */ //todo improve
|
||||
case 0x02 | 0x04: return 0x10;
|
||||
case 0x02: return 0x0C;
|
||||
default: return 0x08;
|
||||
}
|
||||
}
|
||||
|
||||
static size_t calculate_eaac_size(VGMSTREAM *vgmstream, STREAMFILE *streamFile, eaac_header *eaac, off_t start_offset) {
|
||||
uint32_t total_samples;
|
||||
static size_t calculate_eaac_size(STREAMFILE *streamFile, eaac_header *ea, uint32_t num_samples, off_t start_offset) {
|
||||
uint32_t samples_parsed, block_size, block_samples;
|
||||
uint8_t block_id;
|
||||
size_t stream_size, file_size;
|
||||
off_t block_offset;
|
||||
|
||||
if (streamFile == NULL)
|
||||
return 0;
|
||||
|
||||
switch (eaac->codec) {
|
||||
case EAAC_CODEC_EAXMA:
|
||||
case EAAC_CODEC_EALAYER3_V1:
|
||||
case EAAC_CODEC_EALAYER3_V2_PCM:
|
||||
case EAAC_CODEC_EALAYER3_V2_SPIKE:
|
||||
case EAAC_CODEC_EATRAX:
|
||||
case EAAC_CODEC_EAMP3:
|
||||
case EAAC_CODEC_EAOPUS:
|
||||
stream_size = get_streamfile_size(streamFile);
|
||||
break;
|
||||
default:
|
||||
stream_size = 0;
|
||||
total_samples = 0;
|
||||
file_size = get_streamfile_size(streamFile);
|
||||
vgmstream->next_block_offset = start_offset;
|
||||
block_offset = start_offset;
|
||||
stream_size = 0, samples_parsed = 0;
|
||||
|
||||
while (vgmstream->next_block_offset < file_size && total_samples != vgmstream->num_samples) {
|
||||
block_update_ea_sns(vgmstream->next_block_offset, vgmstream);
|
||||
if (vgmstream->current_block_samples == 0)
|
||||
continue;
|
||||
|
||||
/* stream size is almost never provided in bank files so we have to calc it manually */
|
||||
stream_size += vgmstream->next_block_offset - vgmstream->ch[0].offset;
|
||||
total_samples += vgmstream->current_block_samples;
|
||||
}
|
||||
|
||||
/* reset once we're done */
|
||||
block_update(start_offset, vgmstream);
|
||||
break;
|
||||
}
|
||||
while (block_offset < file_size && samples_parsed < num_samples) {
|
||||
block_id = read_8bit(block_offset, streamFile);
|
||||
block_size = read_32bitBE(block_offset, streamFile) & 0x00FFFFFF;
|
||||
|
||||
if (ea->version == EAAC_VERSION_V0) {
|
||||
if (block_id != EAAC_BLOCKID0_DATA && block_id != EAAC_BLOCKID0_END)
|
||||
goto fail;
|
||||
} else {
|
||||
if (block_id != EAAC_BLOCKID1_DATA) {
|
||||
if (block_id == EAAC_BLOCKID1_END && ea->codec == EAAC_CODEC_EATRAX) {
|
||||
/* number of samples in block header is wrong for EATrax so stop when we reach end marker */
|
||||
return stream_size;
|
||||
}
|
||||
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
block_samples = read_32bitBE(block_offset + 0x04, streamFile);
|
||||
|
||||
stream_size += block_size;
|
||||
samples_parsed += block_samples;
|
||||
block_offset += block_size;
|
||||
}
|
||||
|
||||
if (samples_parsed != num_samples)
|
||||
goto fail;
|
||||
|
||||
return stream_size;
|
||||
|
||||
fail:
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static STREAMFILE *setup_eaac_streamfile(eaac_header *ea, STREAMFILE *streamHead, STREAMFILE *streamData) {
|
||||
size_t data_size;
|
||||
STREAMFILE *new_streamFile = NULL;
|
||||
STREAMFILE *temp_streamFile = NULL;
|
||||
STREAMFILE *stream_segments[2] = { 0 };
|
||||
|
||||
if (ea->version == EAAC_VERSION_V0) {
|
||||
switch (ea->type) {
|
||||
case EAAC_TYPE_RAM:
|
||||
/* both header and data in SNR */
|
||||
data_size = calculate_eaac_size(streamHead, ea, ea->num_samples, ea->stream_offset);
|
||||
if (data_size == 0) goto fail;
|
||||
|
||||
new_streamFile = open_wrap_streamfile(streamHead);
|
||||
if (!new_streamFile) goto fail;
|
||||
temp_streamFile = new_streamFile;
|
||||
|
||||
new_streamFile = open_clamp_streamfile(temp_streamFile, ea->stream_offset, data_size);
|
||||
if (!new_streamFile) goto fail;
|
||||
temp_streamFile = new_streamFile;
|
||||
break;
|
||||
case EAAC_TYPE_STREAM:
|
||||
/* header in SNR, data in SNS */
|
||||
data_size = calculate_eaac_size(streamData, ea, ea->num_samples, ea->stream_offset);
|
||||
if (data_size == 0) goto fail;
|
||||
|
||||
new_streamFile = open_wrap_streamfile(streamData);
|
||||
if (!new_streamFile) goto fail;
|
||||
temp_streamFile = new_streamFile;
|
||||
|
||||
new_streamFile = open_clamp_streamfile(temp_streamFile, ea->stream_offset, data_size);
|
||||
if (!new_streamFile) goto fail;
|
||||
temp_streamFile = new_streamFile;
|
||||
break;
|
||||
case EAAC_TYPE_GIGASAMPLE:
|
||||
/* header and prefetched data in SNR, rest of data in SNS */
|
||||
/* open prefetched data */
|
||||
data_size = calculate_eaac_size(streamHead, ea, ea->prefetch_samples, ea->prefetch_offset);
|
||||
if (data_size == 0) goto fail;
|
||||
|
||||
new_streamFile = open_wrap_streamfile(streamHead);
|
||||
if (!new_streamFile) goto fail;
|
||||
stream_segments[0] = new_streamFile;
|
||||
|
||||
new_streamFile = open_clamp_streamfile(stream_segments[0], ea->prefetch_offset, data_size);
|
||||
if (!new_streamFile) goto fail;
|
||||
stream_segments[0] = new_streamFile;
|
||||
|
||||
/* open main data */
|
||||
data_size = calculate_eaac_size(streamData, ea, ea->num_samples - ea->prefetch_samples, ea->stream_offset);
|
||||
if (data_size == 0) goto fail;
|
||||
|
||||
new_streamFile = open_wrap_streamfile(streamData);
|
||||
if (!new_streamFile) goto fail;
|
||||
stream_segments[1] = new_streamFile;
|
||||
|
||||
new_streamFile = open_clamp_streamfile(stream_segments[1], ea->stream_offset, data_size);
|
||||
if (!new_streamFile) goto fail;
|
||||
stream_segments[1] = new_streamFile;
|
||||
|
||||
new_streamFile = open_multifile_streamfile(stream_segments, 2);
|
||||
if (!new_streamFile) goto fail;
|
||||
temp_streamFile = new_streamFile;
|
||||
stream_segments[0] = NULL;
|
||||
stream_segments[1] = NULL;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if (ea->type == EAAC_TYPE_GIGASAMPLE) {
|
||||
/* not seen so far, need samples */
|
||||
VGM_LOG("EAAC: Found SPS gigasample\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
data_size = calculate_eaac_size(streamHead, ea, ea->num_samples, ea->stream_offset);
|
||||
if (data_size == 0) goto fail;
|
||||
|
||||
new_streamFile = open_wrap_streamfile(streamHead);
|
||||
if (!new_streamFile) goto fail;
|
||||
temp_streamFile = new_streamFile;
|
||||
|
||||
new_streamFile = open_clamp_streamfile(temp_streamFile, ea->stream_offset, data_size);
|
||||
if (!new_streamFile) goto fail;
|
||||
temp_streamFile = new_streamFile;
|
||||
}
|
||||
|
||||
return temp_streamFile;
|
||||
|
||||
fail:
|
||||
close_streamfile(stream_segments[0]);
|
||||
close_streamfile(stream_segments[1]);
|
||||
close_streamfile(temp_streamFile);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Actual looping uses 2 block sections, separated by a block end flag *and* padded.
|
||||
*
|
||||
|
@ -1353,7 +1442,7 @@ static size_t calculate_eaac_size(VGMSTREAM *vgmstream, STREAMFILE *streamFile,
|
|||
static segmented_layout_data* build_segmented_eaaudiocore_looping(STREAMFILE *sf_data, eaac_header *eaac) {
|
||||
segmented_layout_data *data = NULL;
|
||||
STREAMFILE* temp_sf = NULL;
|
||||
off_t offsets[2] = { eaac->stream_offset, eaac->loop_offset };
|
||||
off_t offsets[2] = { 0x00, eaac->loop_offset };
|
||||
off_t start_offset;
|
||||
int num_samples[2] = { eaac->loop_start, eaac->num_samples - eaac->loop_start};
|
||||
int segment_count = 2; /* intro/loop */
|
||||
|
@ -1377,12 +1466,11 @@ static segmented_layout_data* build_segmented_eaaudiocore_looping(STREAMFILE *sf
|
|||
eaac_header temp_eaac = *eaac; /* equivalent to memcpy... I think */
|
||||
temp_eaac.loop_flag = 0;
|
||||
temp_eaac.num_samples = num_samples[i];
|
||||
temp_eaac.stream_offset = offsets[i];
|
||||
|
||||
start_offset = 0x00; /* must point to the custom streamfile's beginning */
|
||||
|
||||
/* layers inside segments, how trippy */
|
||||
data->segments[i]->layout_data = build_layered_eaaudiocore(sf_data, &temp_eaac);
|
||||
data->segments[i]->layout_data = build_layered_eaaudiocore(sf_data, &temp_eaac, offsets[i]);
|
||||
if (!data->segments[i]->layout_data) goto fail;
|
||||
data->segments[i]->coding_type = coding_FFmpeg;
|
||||
data->segments[i]->layout_type = layout_layered;
|
||||
|
@ -1407,7 +1495,7 @@ static segmented_layout_data* build_segmented_eaaudiocore_looping(STREAMFILE *sf
|
|||
|
||||
start_offset = 0x00; /* must point to the custom streamfile's beginning */
|
||||
|
||||
temp_sf = setup_eaac_streamfile(sf_data, eaac->version,eaac->codec,eaac->streamed,0,0, offsets[i]);
|
||||
temp_sf = setup_eaac_audio_streamfile(sf_data, eaac->version,eaac->codec,eaac->streamed,0,0, offsets[i]);
|
||||
if (!temp_sf) goto fail;
|
||||
|
||||
data->segments[i]->codec_data = init_mpeg_custom(temp_sf, 0x00, &data->segments[i]->coding_type, eaac->channels, type, &cfg);
|
||||
|
@ -1425,9 +1513,6 @@ static segmented_layout_data* build_segmented_eaaudiocore_looping(STREAMFILE *sf
|
|||
|
||||
close_streamfile(temp_sf);
|
||||
temp_sf = NULL;
|
||||
|
||||
//todo temp_streamFile doesn't contain EAXMA's streamfile
|
||||
data->segments[i]->stream_size = calculate_eaac_size(data->segments[i], temp_sf, eaac, start_offset);
|
||||
}
|
||||
|
||||
if (!setup_layout_segmented(data))
|
||||
|
@ -1440,7 +1525,7 @@ fail:
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static layered_layout_data* build_layered_eaaudiocore(STREAMFILE *sf_data, eaac_header *eaac) {
|
||||
static layered_layout_data* build_layered_eaaudiocore(STREAMFILE *sf_data, eaac_header *eaac, off_t start_offset) {
|
||||
layered_layout_data* data = NULL;
|
||||
STREAMFILE* temp_sf = NULL;
|
||||
int i, layers = (eaac->channels+1) / 2;
|
||||
|
@ -1472,7 +1557,7 @@ static layered_layout_data* build_layered_eaaudiocore(STREAMFILE *sf_data, eaac_
|
|||
int bytes, block_size, block_count;
|
||||
size_t stream_size;
|
||||
|
||||
temp_sf = setup_eaac_streamfile(sf_data, eaac->version, eaac->codec, eaac->streamed,i,layers, eaac->stream_offset);
|
||||
temp_sf = setup_eaac_audio_streamfile(sf_data, eaac->version, eaac->codec, eaac->streamed,i,layers, start_offset);
|
||||
if (!temp_sf) goto fail;
|
||||
|
||||
stream_size = get_streamfile_size(temp_sf);
|
||||
|
@ -1497,7 +1582,7 @@ static layered_layout_data* build_layered_eaaudiocore(STREAMFILE *sf_data, eaac_
|
|||
size_t data_size;
|
||||
|
||||
/* We'll remove EA blocks and pass raw data to FFmpeg Opus decoder */
|
||||
temp_sf = setup_eaac_streamfile(sf_data, eaac->version, eaac->codec, eaac->streamed,i,layers, eaac->stream_offset);
|
||||
temp_sf = setup_eaac_audio_streamfile(sf_data, eaac->version, eaac->codec, eaac->streamed,i,layers, start_offset);
|
||||
if (!temp_sf) goto fail;
|
||||
|
||||
skip = ea_opus_get_encoder_delay(0x00, temp_sf);
|
||||
|
@ -1507,8 +1592,6 @@ static layered_layout_data* build_layered_eaaudiocore(STREAMFILE *sf_data, eaac_
|
|||
if (!data->layers[i]->codec_data) goto fail;
|
||||
data->layers[i]->coding_type = coding_FFmpeg;
|
||||
data->layers[i]->layout_type = layout_none;
|
||||
|
||||
//TODO: 6ch channel layout seems L C R BL BR LFE, not sure about other EAAC
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
@ -245,7 +245,7 @@ static size_t eaac_io_size(STREAMFILE *streamfile, eaac_io_data* data) {
|
|||
* - EATrax: ATRAC9 frames can be split between blooks
|
||||
* - EAOpus: multiple Opus packets of frame size + Opus data per block
|
||||
*/
|
||||
static STREAMFILE* setup_eaac_streamfile(STREAMFILE *sf, int version, int codec, int streamed, int stream_number, int stream_count, off_t stream_offset) {
|
||||
static STREAMFILE* setup_eaac_audio_streamfile(STREAMFILE *sf, int version, int codec, int streamed, int stream_number, int stream_count, off_t stream_offset) {
|
||||
STREAMFILE *new_sf = NULL;
|
||||
eaac_io_data io_data = {0};
|
||||
|
||||
|
|
|
@ -690,7 +690,7 @@ static STREAMFILE* open_mapfile_pair(STREAMFILE *streamFile, int track, int num_
|
|||
continue;
|
||||
}
|
||||
|
||||
strncpy(buf, mus_name, PATH_LIMIT);
|
||||
strncpy(buf, mus_name, PATH_LIMIT - 1);
|
||||
pch = strtok(buf, ","); //TODO: not thread safe in std C
|
||||
for (j = 0; j < track && pch; j++) {
|
||||
pch = strtok(NULL, ",");
|
||||
|
@ -699,9 +699,9 @@ static STREAMFILE* open_mapfile_pair(STREAMFILE *streamFile, int track, int num_
|
|||
|
||||
if (use_mask) {
|
||||
file_name[file_len - map_len] = '\0';
|
||||
strncat(file_name, pch + 1, PATH_LIMIT);
|
||||
strncat(file_name, pch + 1, PATH_LIMIT - 1);
|
||||
} else {
|
||||
strncpy(file_name, pch, PATH_LIMIT);
|
||||
strncpy(file_name, pch, PATH_LIMIT - 1);
|
||||
}
|
||||
|
||||
musFile = open_streamfile_by_filename(streamFile, file_name);
|
||||
|
|
|
@ -5,16 +5,10 @@ VGMSTREAM * init_vgmstream_gin_header(STREAMFILE *streamFile, off_t offset);
|
|||
|
||||
/* .gin - EA engine sounds [Need for Speed: Most Wanted (multi)] */
|
||||
VGMSTREAM * init_vgmstream_gin(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
|
||||
if (!check_extensions(streamFile, "gin"))
|
||||
goto fail;
|
||||
|
||||
vgmstream = init_vgmstream_gin_header(streamFile, 0x00);
|
||||
if (!vgmstream)
|
||||
goto fail;
|
||||
|
||||
return vgmstream;
|
||||
return init_vgmstream_gin_header(streamFile, 0x00);
|
||||
|
||||
fail:
|
||||
return NULL;
|
||||
|
|
|
@ -15,7 +15,7 @@ typedef struct {
|
|||
* CRI's tools expect an unsigned 64 bit number string, but keys are commonly found online in hex form.
|
||||
* Keys only use 56 bits though, so the upper 8 bits can be ignored.
|
||||
*
|
||||
* ACB+AWB after mid 2018 use a user seed key + a scramble subkey in the AWB (normally 16b LE at 0x0e)
|
||||
* Some ACB+AWB after mid 2018 use a user seed key + a scramble subkey in the AWB (normally 16b LE at 0x0e)
|
||||
* to create the final HCA key, which means there is one key per AWB (so most HCA have a unique key).
|
||||
* vgmstream derives the key if subkey table is provided.
|
||||
*/
|
||||
|
@ -303,7 +303,19 @@ static const hcakey_info hcakey_list[] = {
|
|||
/* BLACKSTAR -Theater Starless- (Android) */
|
||||
{121837007188}, // 0000001C5E0D3154
|
||||
|
||||
/* Dragalia Lost (Cygames) [iOS/Android] */
|
||||
/* Nogizaka46 Rhythm Festival (Android) */
|
||||
{5613126134333697}, // 0013F11BC5510101
|
||||
|
||||
/* IDOLiSH7 (Android) */
|
||||
{8548758374946935437}, // 76A34A72E15B928D
|
||||
|
||||
/* Phantom of the Kill (Android) */
|
||||
{33624594140214547}, // 00777563E571B513
|
||||
|
||||
/* Dankira!!! Boys, be DANCING! (Android) */
|
||||
{3957325206121219506}, // 36EB3E4EE38E05B2
|
||||
|
||||
/* Dragalia Lost (iOS/Android) */
|
||||
{2967411924141, subkeys_dgl, sizeof(subkeys_dgl) / sizeof(subkeys_dgl[0]) }, // 000002B2E7889CAD
|
||||
|
||||
};
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
#include "meta.h"
|
||||
|
||||
|
||||
/* IVAG - Namco header (from NUS3) [THE iDOLM@STER 2 (PS3), THE iDOLM@STER: Gravure For You! (PS3)] */
|
||||
VGMSTREAM * init_vgmstream_ivag(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
off_t start_offset;
|
||||
|
||||
int loop_flag = 0;
|
||||
int channel_count;
|
||||
|
||||
/* checks */
|
||||
/* .ivag: header id (since format can't be found outside NUS3) */
|
||||
if (!check_extensions(streamFile, "ivag"))
|
||||
goto fail;
|
||||
|
||||
if (read_32bitBE(0x00,streamFile) != 0x49564147) /* "IVAG" */
|
||||
goto fail;
|
||||
|
||||
/* 0x04: null */
|
||||
channel_count = read_32bitBE(0x08, streamFile);
|
||||
loop_flag = (read_32bitBE(0x18, streamFile) != 0);
|
||||
|
||||
/* skip VAGp headers per channel (size 0x40) */
|
||||
start_offset = 0x40 + (0x40 * channel_count);
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channel_count,loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->meta_type = meta_IVAG;
|
||||
|
||||
vgmstream->sample_rate = read_32bitBE(0x0C,streamFile);
|
||||
vgmstream->num_samples = read_32bitBE(0x10,streamFile);
|
||||
vgmstream->loop_start_sample = read_32bitBE(0x14,streamFile);
|
||||
vgmstream->loop_end_sample = read_32bitBE(0x18,streamFile);
|
||||
|
||||
vgmstream->coding_type = coding_PSX;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = read_32bitBE(0x1C,streamFile);
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
|
||||
goto fail;
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
|
@ -28,7 +28,7 @@ VGMSTREAM * init_vgmstream_ngc_mdsp_std(STREAMFILE *streamFile);
|
|||
VGMSTREAM * init_vgmstream_ngc_dsp_stm(STREAMFILE *streamFile);
|
||||
VGMSTREAM * init_vgmstream_ngc_mpdsp(STREAMFILE *streamFile);
|
||||
VGMSTREAM * init_vgmstream_ngc_dsp_std_int(STREAMFILE *streamFile);
|
||||
VGMSTREAM * init_vgmstream_idsp_nus3(STREAMFILE *streamFile);
|
||||
VGMSTREAM * init_vgmstream_idsp_namco(STREAMFILE *streamFile);
|
||||
VGMSTREAM * init_vgmstream_sadb(STREAMFILE *streamFile);
|
||||
VGMSTREAM * init_vgmstream_sadf(STREAMFILE *streamFile);
|
||||
VGMSTREAM * init_vgmstream_idsp_tt(STREAMFILE *streamFile);
|
||||
|
@ -542,7 +542,7 @@ VGMSTREAM * init_vgmstream_mss(STREAMFILE* streamFile);
|
|||
|
||||
VGMSTREAM * init_vgmstream_ps2_hsf(STREAMFILE* streamFile);
|
||||
|
||||
VGMSTREAM * init_vgmstream_ps3_ivag(STREAMFILE* streamFile);
|
||||
VGMSTREAM * init_vgmstream_ivag(STREAMFILE* streamFile);
|
||||
|
||||
VGMSTREAM * init_vgmstream_ps2_2pfs(STREAMFILE* streamFile);
|
||||
|
||||
|
@ -746,6 +746,7 @@ VGMSTREAM * init_vgmstream_hd3_bd3(STREAMFILE *streamFile);
|
|||
VGMSTREAM * init_vgmstream_bnk_sony(STREAMFILE *streamFile);
|
||||
|
||||
VGMSTREAM * init_vgmstream_nus3bank(STREAMFILE *streamFile);
|
||||
VGMSTREAM * init_vgmstream_nus3bank_encrypted(STREAMFILE *streamFile);
|
||||
|
||||
VGMSTREAM * init_vgmstream_scd_sscf(STREAMFILE *streamFile);
|
||||
|
||||
|
|
|
@ -228,6 +228,12 @@ static int parse_musx_stream(STREAMFILE *streamFile, musx_header *musx) {
|
|||
musx->codec = DAT;
|
||||
break;
|
||||
|
||||
case 0x50435F5F: /* "PC__" */
|
||||
default_channels = 2;
|
||||
default_sample_rate = 44100;
|
||||
musx->codec = DAT;
|
||||
break;
|
||||
|
||||
default:
|
||||
VGM_LOG("MUSX: unknown platform %x\n", musx->platform);
|
||||
goto fail;
|
||||
|
@ -294,18 +300,26 @@ static int parse_musx_stream(STREAMFILE *streamFile, musx_header *musx) {
|
|||
musx->loop_flag = (musx->loop_start_sample >= 0);
|
||||
}
|
||||
|
||||
/* fix some v10 sizes */
|
||||
/* fix some v10 platform (like PSP) sizes */
|
||||
if (musx->stream_size == 0) {
|
||||
off_t offset;
|
||||
musx->stream_size = musx->file_size - musx->stream_offset;
|
||||
|
||||
/* remove padding */
|
||||
offset = musx->stream_offset + musx->stream_size - 0x04;
|
||||
while (offset > 0) {
|
||||
if (read_32bit(offset, streamFile) != 0xABABABAB)
|
||||
/* always padded to nearest 0x800 sector */
|
||||
if (musx->stream_size > 0x800) {
|
||||
uint8_t buf[0x800];
|
||||
int pos;
|
||||
off_t offset = musx->stream_offset + musx->stream_size - 0x800;
|
||||
|
||||
if (read_streamfile(buf, offset, sizeof(buf), streamFile) != 0x800)
|
||||
goto fail;
|
||||
|
||||
pos = 0x800 - 0x04;
|
||||
while (pos > 0) {
|
||||
if (get_u32be(buf + pos) != 0xABABABAB)
|
||||
break;
|
||||
musx->stream_size -= 0x04;
|
||||
offset -= 0x04;
|
||||
pos -= 0x04;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -334,9 +348,9 @@ static int parse_musx(STREAMFILE *streamFile, musx_header *musx) {
|
|||
musx->is_old = 1;
|
||||
break;
|
||||
|
||||
case 4: /* Spyro: A Hero's Tail (PS2/Xbox/GC) */
|
||||
case 4: /* Spyro: A Hero's Tail (PS2/Xbox/GC), Athens 2004 (PC) */
|
||||
case 5: /* Predator: Concrete Jungle (PS2/Xbox), Robots (GC) */
|
||||
case 6: /* Batman Begins (GC), Ice Age 2 (PS2) */
|
||||
case 6: /* Batman Begins (GC), Ice Age 2 (PS2/PC) */
|
||||
musx->platform = read_32bitBE(0x10,streamFile);
|
||||
/* 0x14: some id/hash? */
|
||||
/* 0x18: platform number? */
|
||||
|
@ -352,7 +366,10 @@ static int parse_musx(STREAMFILE *streamFile, musx_header *musx) {
|
|||
/* 0x1c: null */
|
||||
/* 0x20: hash */
|
||||
musx->tables_offset = 0; /* no tables */
|
||||
musx->big_endian = (musx->platform == 0x5749495F || musx->platform == 0x5053335F); /* "GC__" / "PS3_" (only after header) */
|
||||
musx->big_endian = (musx->platform == 0x47435F5F || /* "GC__" */
|
||||
musx->platform == 0x58455F5F || /* "XE__" */
|
||||
musx->platform == 0x5053335F || /* "PS3_" */
|
||||
musx->platform == 0x5749495F); /* "WII_" */
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -438,6 +455,7 @@ static int parse_musx(STREAMFILE *streamFile, musx_header *musx) {
|
|||
uint32_t miniheader = read_32bitBE(0x40, streamFile);
|
||||
switch(miniheader) {
|
||||
case 0x44415434: /* "DAT4" */
|
||||
case 0x44415435: /* "DAT5" */
|
||||
case 0x44415438: /* "DAT8" */
|
||||
/* found on PS3/Wii (but not always?) */
|
||||
musx->stream_size = read_32bitLE(0x44, streamFile);
|
||||
|
@ -446,7 +464,14 @@ static int parse_musx(STREAMFILE *streamFile, musx_header *musx) {
|
|||
musx->loops_offset = 0x50;
|
||||
break;
|
||||
default:
|
||||
if (read_32bitBE(0x30, streamFile) == 0x00 &&
|
||||
read_32bitBE(0x34, streamFile) == 0x00) {
|
||||
/* no subheader [Spider-Man 4 (Wii)] */
|
||||
musx->loops_offset = 0x00;
|
||||
} else {
|
||||
/* loop info only [G-Force (PS3)] */
|
||||
musx->loops_offset = 0x30;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -529,9 +554,6 @@ static int parse_musx(STREAMFILE *streamFile, musx_header *musx) {
|
|||
musx->channels = 1;
|
||||
}
|
||||
|
||||
|
||||
VGM_LOG("to=%lx, %lx, %x\n", target_offset, musx->stream_offset, musx->stream_size);
|
||||
|
||||
if (coef_size == 0)
|
||||
musx->coefs_offset = 0; /* disable for later detection */
|
||||
|
||||
|
|
|
@ -597,8 +597,8 @@ fail:
|
|||
return NULL;
|
||||
}
|
||||
|
||||
/* IDSP - Namco header (from NUS3) + interleaved dsp [SSB4 (3DS), Tekken Tag Tournament 2 (WiiU)] */
|
||||
VGMSTREAM * init_vgmstream_idsp_nus3(STREAMFILE *streamFile) {
|
||||
/* IDSP - Namco header (from NUB/NUS3) + interleaved dsp [SSB4 (3DS), Tekken Tag Tournament 2 (WiiU)] */
|
||||
VGMSTREAM * init_vgmstream_idsp_namco(STREAMFILE *streamFile) {
|
||||
dsp_meta dspm = {0};
|
||||
|
||||
/* checks */
|
||||
|
@ -606,21 +606,26 @@ VGMSTREAM * init_vgmstream_idsp_nus3(STREAMFILE *streamFile) {
|
|||
goto fail;
|
||||
if (read_32bitBE(0x00,streamFile) != 0x49445350) /* "IDSP" */
|
||||
goto fail;
|
||||
/* 0x0c: sample rate, 0x10: num_samples, 0x14: loop_start_sample, 0x18: loop_start_sample */
|
||||
|
||||
dspm.channel_count = read_32bitBE(0x08, streamFile);
|
||||
dspm.max_channels = 8;
|
||||
/* games do adjust loop_end if bigger than num_samples (only happens in user-created IDSPs) */
|
||||
dspm.fix_looping = 1;
|
||||
|
||||
/* 0x04: null */
|
||||
dspm.channel_count = read_32bitBE(0x08, streamFile);
|
||||
/* 0x0c: sample rate */
|
||||
/* 0x10: num_samples */
|
||||
/* 0x14: loop start */
|
||||
/* 0x18: loop end */
|
||||
dspm.interleave = read_32bitBE(0x1c,streamFile); /* usually 0x10 */
|
||||
dspm.header_offset = read_32bitBE(0x20,streamFile);
|
||||
dspm.header_spacing = read_32bitBE(0x24,streamFile);
|
||||
dspm.start_offset = read_32bitBE(0x28,streamFile);
|
||||
dspm.interleave = read_32bitBE(0x1c,streamFile); /* usually 0x10 */
|
||||
if (dspm.interleave == 0) /* Taiko no Tatsujin: Atsumete Tomodachi Daisakusen (WiiU) */
|
||||
dspm.interleave = read_32bitBE(0x2c,streamFile); /* half interleave, use channel size */
|
||||
/* Soul Calibur: Broken destiny (PSP), Taiko no Tatsujin: Atsumete Tomodachi Daisakusen (WiiU) */
|
||||
if (dspm.interleave == 0) /* half interleave (happens sometimes), use channel size */
|
||||
dspm.interleave = read_32bitBE(0x2c,streamFile);
|
||||
|
||||
dspm.meta_type = meta_IDSP_NUS3;
|
||||
dspm.meta_type = meta_IDSP_NAMCO;
|
||||
return init_vgmstream_dsp_common(streamFile, &dspm);
|
||||
fail:
|
||||
return NULL;
|
||||
|
|
|
@ -2,14 +2,14 @@
|
|||
#include "../coding/coding.h"
|
||||
|
||||
|
||||
static void load_name(char *name, size_t name_size, STREAMFILE *sf, int big_endian, int total_subsongs, int target_subsong);
|
||||
static STREAMFILE* setup_nub_streamfile(STREAMFILE *sf, off_t header_offset, size_t header_size, off_t stream_offset, size_t stream_size, const char *fake_ext);
|
||||
|
||||
/* .nub - Namco's nu Sound v2 audio container */
|
||||
/* .nub - Namco's nuSound2 audio container */
|
||||
VGMSTREAM * init_vgmstream_nub(STREAMFILE *streamFile) {
|
||||
VGMSTREAM *vgmstream = NULL;
|
||||
STREAMFILE *temp_sf = NULL;
|
||||
off_t name_offset = 0;
|
||||
size_t name_size = 0;
|
||||
int big_endian;
|
||||
int total_subsongs, target_subsong = streamFile->stream_index;
|
||||
uint32_t version, codec;
|
||||
const char* fake_ext;
|
||||
|
@ -25,13 +25,14 @@ VGMSTREAM * init_vgmstream_nub(STREAMFILE *streamFile) {
|
|||
version = read_32bitBE(0x00,streamFile);
|
||||
if (version != 0x00020000 && /* v2.0 (rare, ex. Ridge Race 6 (X360)) */
|
||||
version != 0x00020100 && /* v2.1 (common) */
|
||||
version != 0x01020100) /* same but LE? (seen in PSP games, not PS4) */
|
||||
version != 0x01020100) /* same but LE (seen in PSP/PC games, except PS4) */
|
||||
goto fail;
|
||||
if (read_32bitBE(0x04,streamFile) != 0x00000000) /* null */
|
||||
goto fail;
|
||||
|
||||
/* sometimes LE [Soul Calibur: Broken Destiny (PSP), Tales of Vesperia (PS4) */
|
||||
if (guess_endianness32bit(0x10, streamFile)) {
|
||||
big_endian = guess_endianness32bit(0x10, streamFile);
|
||||
if (big_endian) {
|
||||
read_32bit = read_32bitBE;
|
||||
} else{
|
||||
read_32bit = read_32bitLE;
|
||||
|
@ -44,8 +45,8 @@ VGMSTREAM * init_vgmstream_nub(STREAMFILE *streamFile) {
|
|||
size_t header_size, subheader_size, stream_size;
|
||||
|
||||
/* - base header */
|
||||
/* 0x08: file id/number (can be 0 = first) */
|
||||
total_subsongs = read_32bit(0x0c, streamFile); /* .nub with 0 files do exist */
|
||||
/* 0x08: file id (0 = first) */
|
||||
total_subsongs = read_32bit(0x0c, streamFile); /* .nub with 0 files do exist, and with 1 song only too */
|
||||
data_start = read_32bit(0x10, streamFile); /* exists even with 0 files */
|
||||
/* 0x14: data end (may have padding) */
|
||||
header_start = read_32bit(0x18, streamFile);
|
||||
|
@ -63,16 +64,17 @@ VGMSTREAM * init_vgmstream_nub(STREAMFILE *streamFile) {
|
|||
|
||||
/* .nus have all headers first then all data, but extractors often just paste them together,
|
||||
* so we'll combine header+data on the fly to make them playable with existing parsers.
|
||||
* Formats inside .nub don't exist as external files, so they could be extracted in various
|
||||
* ways that we'll try to match (though BNSF can be found as header+data in some bigfiles too). */
|
||||
* Formats inside .nub normally don't exist as external files, so they could be extracted in various
|
||||
* ways that we'll try to match (though IDSP/BNSF can be found as header+data in some bigfiles).
|
||||
* Headers seem to be called "tones" and data "streams" in debug strings. */
|
||||
|
||||
header_offset = offset;
|
||||
|
||||
/* - extension (as referenced in companion files with internal filenames, ex. "BGM_MovingDemo1.is14" > "is14") */
|
||||
if (version != 0x00020000)
|
||||
offset += 0x04; /* skip but not in v2.0 */
|
||||
offset += 0x04; /* skip, not found in v2.0 */
|
||||
|
||||
/* - file header */
|
||||
/* - tone header */
|
||||
/* 0x00: config? */
|
||||
/* 0x04: header id/number */
|
||||
codec = (uint32_t)read_32bit(offset + 0x08, streamFile);
|
||||
|
@ -80,7 +82,8 @@ VGMSTREAM * init_vgmstream_nub(STREAMFILE *streamFile) {
|
|||
stream_size = read_32bit(offset + 0x10, streamFile); /* 0x10 aligned */
|
||||
stream_offset = read_32bit(offset + 0x14, streamFile) + data_start;
|
||||
subheader_size = read_32bit(offset + 0x18, streamFile);
|
||||
/* rest looks like config/volumes/etc */
|
||||
/* 0x1c: extra info, size 0x10 (meaning varies but usually loop points) */
|
||||
/* rest until sub-header start looks like config/volumes/pan/etc in various floats */
|
||||
|
||||
if (version == 0x00020000)
|
||||
subheader_start = 0xAC;
|
||||
|
@ -137,33 +140,10 @@ VGMSTREAM * init_vgmstream_nub(STREAMFILE *streamFile) {
|
|||
if (!temp_sf) goto fail;
|
||||
}
|
||||
|
||||
/* get names */
|
||||
{
|
||||
/* file names are in a companion file, rarely [Noby Noby Boy (PS3)] */
|
||||
STREAMFILE *nameFile = NULL;
|
||||
char filename[PATH_LIMIT];
|
||||
char basename[255];
|
||||
|
||||
get_streamfile_basename(streamFile, basename, sizeof(basename));
|
||||
snprintf(filename,sizeof(filename), "nuSound2ToneStr%s.bin", basename);
|
||||
/* get names from companion file, rarely [Noby Noby Boy (PS3), Wangan Midnight Maximum Tune (AC)] */
|
||||
load_name(name, sizeof(name), streamFile, big_endian, total_subsongs, target_subsong);
|
||||
|
||||
nameFile = open_streamfile_by_filename(streamFile, filename);
|
||||
if (nameFile && read_32bit(0x08, nameFile) == total_subsongs) {
|
||||
off_t header_start = 0x40; /* first name is bank name */
|
||||
char name1[0x20+1] = {0};
|
||||
char name2[0x20+1] = {0};
|
||||
|
||||
name_size = 0x20;
|
||||
name_offset = header_start + (target_subsong-1)*(name_size*2);
|
||||
|
||||
read_string(name1,name_size, name_offset + 0x00, nameFile); /* internal name */
|
||||
read_string(name2,name_size, name_offset + 0x20, nameFile); /* file name */
|
||||
//todo some filenames use shift-jis, not sure what to do
|
||||
|
||||
snprintf(name,sizeof(name), "%s/%s", name1,name2);
|
||||
}
|
||||
close_streamfile(nameFile);
|
||||
}
|
||||
|
||||
/* init the VGMSTREAM */
|
||||
vgmstream = init_vgmstream_function(temp_sf);
|
||||
|
@ -185,6 +165,50 @@ fail:
|
|||
|
||||
/* *********************************************************** */
|
||||
|
||||
static void load_name(char *name, size_t name_size, STREAMFILE *sf, int big_endian, int total_subsongs, int target_subsong) {
|
||||
STREAMFILE *sf_names = NULL;
|
||||
char filename[PATH_LIMIT];
|
||||
char basename[255];
|
||||
char name1[0x40+1] = {0};
|
||||
char name2[0x40+1] = {0};
|
||||
int count;
|
||||
off_t offset;
|
||||
size_t name1_size, name2_size;
|
||||
uint32_t (*read_u32)(off_t,STREAMFILE*) = big_endian ? read_u32be : read_u32le;
|
||||
|
||||
get_streamfile_basename(sf, basename, sizeof(basename));
|
||||
snprintf(filename, sizeof(filename), "nuSound2ToneStr%s.bin", basename);
|
||||
|
||||
sf_names = open_streamfile_by_filename(sf, filename);
|
||||
if (!sf_names) goto done;
|
||||
|
||||
/* 0x00: version/endianness? (0=NNB, 1=WMMT5) */
|
||||
/* 0x04: version/endianness? (1=NNB, 0=WMMT5) */
|
||||
count = read_u32(0x08, sf_names);
|
||||
/* 0x0c: file size */
|
||||
name1_size = read_u32(0x10, sf_names);
|
||||
name2_size = read_u32(0x14, sf_names);
|
||||
/* 0x18/1c: null */
|
||||
/* 0x20: bank name (size 0x20) */
|
||||
|
||||
if (count != total_subsongs)
|
||||
goto done;
|
||||
if (name1_size >= sizeof(name1) || name2_size >= sizeof(name2))
|
||||
goto done;
|
||||
|
||||
offset = 0x40 + (target_subsong - 1) * (name1_size + name2_size);
|
||||
|
||||
read_string(name1, name1_size, offset + 0x00, sf_names); /* internal name */
|
||||
read_string(name2, name2_size, offset + name1_size, sf_names); /* file name */
|
||||
//todo some filenames use shift-jis, not sure what to do
|
||||
|
||||
snprintf(name, name_size, "%s/%s", name1, name2);
|
||||
|
||||
done:
|
||||
close_streamfile(sf_names);
|
||||
}
|
||||
|
||||
|
||||
static STREAMFILE* setup_nub_streamfile(STREAMFILE *sf, off_t header_offset, size_t header_size, off_t stream_offset, size_t stream_size, const char *fake_ext) {
|
||||
STREAMFILE *new_sf = NULL;
|
||||
STREAMFILE *multi_sf[2] = {0};
|
||||
|
@ -206,7 +230,10 @@ static STREAMFILE* setup_nub_streamfile(STREAMFILE *sf, off_t header_offset, siz
|
|||
VGMSTREAM * init_vgmstream_nub_wav(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
off_t start_offset;
|
||||
int loop_flag, channel_count;
|
||||
int loop_flag, channel_count, sample_rate;
|
||||
size_t data_size, loop_start, loop_length;
|
||||
int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL;
|
||||
int16_t (*read_16bit)(off_t,STREAMFILE*) = NULL;
|
||||
|
||||
|
||||
/* checks */
|
||||
|
@ -215,24 +242,43 @@ VGMSTREAM * init_vgmstream_nub_wav(STREAMFILE *streamFile) {
|
|||
if (read_32bitBE(0x00,streamFile) != 0x77617600) /* "wav\0" "*/
|
||||
goto fail;
|
||||
|
||||
if (read_16bitBE(0xBC+0x00,streamFile) != 0x0001) /* mini "fmt" chunk */
|
||||
goto fail;
|
||||
if (guess_endianness32bit(0x1c, streamFile)) {
|
||||
read_32bit = read_32bitBE;
|
||||
read_16bit = read_16bitBE;
|
||||
} else {
|
||||
read_32bit = read_32bitLE;
|
||||
read_16bit = read_16bitLE;
|
||||
}
|
||||
|
||||
data_size = read_32bit(0x14,streamFile);
|
||||
/* info header */
|
||||
loop_start = read_32bit(0x20,streamFile);
|
||||
loop_length = read_32bit(0x24,streamFile);
|
||||
loop_flag = read_32bit(0x28,streamFile);
|
||||
/* 0x2c: null */
|
||||
|
||||
/* format header: mini "fmt" chunk */
|
||||
if (read_16bit(0xBC + 0x00, streamFile) != 0x0001)
|
||||
goto fail;
|
||||
channel_count = read_16bit(0xBC + 0x02,streamFile);
|
||||
sample_rate = read_32bit(0xBC + 0x04,streamFile);
|
||||
/* 0x08: bitrate */
|
||||
/* 0x0c: block align/bps */
|
||||
|
||||
loop_flag = read_32bitBE(0x24,streamFile);
|
||||
channel_count = read_16bitBE(0xBC+0x02,streamFile);
|
||||
start_offset = 0xD0;
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channel_count,loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->meta_type = meta_NUB;
|
||||
vgmstream->sample_rate = read_32bitBE(0xBC+0x04,streamFile);
|
||||
vgmstream->num_samples = pcm_bytes_to_samples(read_32bitBE(0x14,streamFile), channel_count, 16);
|
||||
vgmstream->loop_start_sample = pcm_bytes_to_samples(read_32bitBE(0x20,streamFile), channel_count, 16);
|
||||
vgmstream->loop_end_sample = pcm_bytes_to_samples(read_32bitBE(0x24,streamFile), channel_count, 16);
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
vgmstream->num_samples = pcm_bytes_to_samples(data_size, channel_count, 16);
|
||||
vgmstream->loop_start_sample = pcm_bytes_to_samples(loop_start, channel_count, 16);
|
||||
vgmstream->loop_end_sample = pcm_bytes_to_samples(loop_start + loop_length, channel_count, 16);
|
||||
|
||||
vgmstream->coding_type = coding_PCM16BE;
|
||||
vgmstream->coding_type = coding_PCM16BE; /* always BE */
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = 0x02;
|
||||
|
||||
|
@ -249,7 +295,9 @@ fail:
|
|||
VGMSTREAM * init_vgmstream_nub_vag(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
off_t start_offset;
|
||||
int loop_flag, channel_count;
|
||||
int loop_flag, channel_count, sample_rate;
|
||||
size_t data_size, loop_start, loop_length;
|
||||
int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL;
|
||||
|
||||
|
||||
/* checks */
|
||||
|
@ -258,19 +306,35 @@ VGMSTREAM * init_vgmstream_nub_vag(STREAMFILE *streamFile) {
|
|||
if (read_32bitBE(0x00,streamFile) != 0x76616700) /* "vag\0" */
|
||||
goto fail;
|
||||
|
||||
loop_flag = read_32bitBE(0x24,streamFile);
|
||||
channel_count = 1; /* dual file stereo */
|
||||
if (guess_endianness32bit(0x1c, streamFile)) {
|
||||
read_32bit = read_32bitBE;
|
||||
} else {
|
||||
read_32bit = read_32bitLE;
|
||||
}
|
||||
|
||||
data_size = read_32bit(0x14,streamFile);
|
||||
/* info header */
|
||||
loop_start = read_32bit(0x20,streamFile);
|
||||
loop_length = read_32bit(0x24,streamFile);
|
||||
loop_flag = read_32bit(0x28,streamFile);
|
||||
/* 0x2c: null */
|
||||
|
||||
/* format header */
|
||||
sample_rate = read_32bit(0xBC + 0x00,streamFile);
|
||||
|
||||
channel_count = 1;
|
||||
start_offset = 0xC0;
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channel_count,loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->meta_type = meta_NUB;
|
||||
vgmstream->sample_rate = read_32bitBE(0xBC,streamFile);
|
||||
vgmstream->num_samples = ps_bytes_to_samples(read_32bitBE(0x14,streamFile), channel_count);
|
||||
vgmstream->loop_start_sample = ps_bytes_to_samples(read_32bitBE(0x20,streamFile), channel_count);
|
||||
vgmstream->loop_end_sample = ps_bytes_to_samples(read_32bitBE(0x24,streamFile), channel_count);
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
vgmstream->num_samples = ps_bytes_to_samples(data_size, channel_count);
|
||||
vgmstream->loop_start_sample = ps_bytes_to_samples(loop_start, channel_count);
|
||||
vgmstream->loop_end_sample = ps_bytes_to_samples(loop_start + loop_length, channel_count);
|
||||
|
||||
vgmstream->coding_type = coding_PSX;
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
@ -299,7 +363,15 @@ VGMSTREAM * init_vgmstream_nub_at3(STREAMFILE *streamFile) {
|
|||
if (read_32bitBE(0x00,streamFile) != 0x61743300) /* "at3\0" */
|
||||
goto fail;
|
||||
|
||||
/* mini fmt+fact header, we can use RIFF anyway */
|
||||
/* info header */
|
||||
/* 0x20: loop start (in samples) */
|
||||
/* 0x24: loop length (in samples) */
|
||||
/* 0x28: loop flag */
|
||||
/* 0x2c: null */
|
||||
|
||||
/* format header: mini fmt (WAVEFORMATEX) + fact chunks LE (clone of RIFF's) */
|
||||
/* we can just ignore and use RIFF at data start since it has the same info */
|
||||
|
||||
subfile_offset = 0x100;
|
||||
subfile_size = read_32bitLE(subfile_offset + 0x04, streamFile) + 0x08; /* RIFF size */
|
||||
|
||||
|
@ -337,7 +409,12 @@ VGMSTREAM * init_vgmstream_nub_xma(STREAMFILE *streamFile) {
|
|||
data_size = read_32bitBE(0x14,streamFile);
|
||||
header_size = read_32bitBE(0x1c,streamFile);
|
||||
chunk_offset = 0xBC;
|
||||
|
||||
/* info header */
|
||||
/* 0x20: null */
|
||||
chunk_size = read_32bitBE(0x24,streamFile);
|
||||
/* 0x24: loop flag */
|
||||
/* 0x20: null */
|
||||
}
|
||||
else if (read_32bitBE(0x08,streamFile) == 0 && read_32bitBE(0x0c,streamFile) == 0) {
|
||||
/* nub v2.0 from Ridge Racer 6 */
|
||||
|
@ -345,6 +422,7 @@ VGMSTREAM * init_vgmstream_nub_xma(STREAMFILE *streamFile) {
|
|||
data_size = read_32bitBE(0x10,streamFile);
|
||||
header_size = read_32bitBE(0x18,streamFile);
|
||||
chunk_offset = 0xAC;
|
||||
|
||||
chunk_size = header_size;
|
||||
}
|
||||
else {
|
||||
|
@ -435,51 +513,44 @@ fail:
|
|||
/* .nub idsp - from Namco NUB archives [Soul Calibur Legends (Wii), Sky Crawlers: Innocent Aces (Wii)] */
|
||||
VGMSTREAM * init_vgmstream_nub_idsp(STREAMFILE *streamFile) {
|
||||
VGMSTREAM *vgmstream = NULL;
|
||||
off_t start_offset;
|
||||
int loop_flag, channel_count;
|
||||
STREAMFILE *temp_sf = NULL;
|
||||
off_t header_offset, stream_offset;
|
||||
size_t header_size, stream_size;
|
||||
|
||||
|
||||
/* checks */
|
||||
if (!check_extensions(streamFile,"idsp"))
|
||||
goto fail;
|
||||
|
||||
if (read_32bitBE(0x00,streamFile) != 0x69647370) /* "idsp" */
|
||||
goto fail;
|
||||
if (read_32bitBE(0xBC,streamFile) != 0x49445350) /* "IDSP" (actual header) */
|
||||
goto fail;
|
||||
|
||||
loop_flag = read_32bitBE(0x20,streamFile);
|
||||
channel_count = read_32bitBE(0xC4,streamFile);
|
||||
if (channel_count > 8) goto fail;
|
||||
start_offset = 0x100 + (channel_count * 0x60);
|
||||
/* info header */
|
||||
/* 0x20: loop start (in samples) */
|
||||
/* 0x24: loop length (in samples) */
|
||||
/* 0x28: loop flag */
|
||||
/* 0x2c: null */
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channel_count,loop_flag);
|
||||
/* paste header+data together and pass to meta, which has loop info too */
|
||||
header_offset = 0xBC;
|
||||
stream_size = read_32bitBE(0x14, streamFile);
|
||||
header_size = read_32bitBE(0x1c, streamFile);
|
||||
stream_offset = align_size_to_block(header_offset + header_size, 0x10);
|
||||
|
||||
temp_sf = setup_nub_streamfile(streamFile, header_offset, header_size, stream_offset, stream_size, "idsp");
|
||||
if (!temp_sf) goto fail;
|
||||
|
||||
vgmstream = init_vgmstream_idsp_namco(temp_sf);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->meta_type = meta_NUB;
|
||||
vgmstream->sample_rate = read_32bitBE(0xC8,streamFile);
|
||||
vgmstream->num_samples = dsp_bytes_to_samples(read_32bitBE(0x14,streamFile),channel_count);
|
||||
vgmstream->loop_start_sample = read_32bitBE(0xD0,streamFile);
|
||||
vgmstream->loop_end_sample = read_32bitBE(0xD4,streamFile);
|
||||
|
||||
vgmstream->coding_type = coding_NGC_DSP;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = read_32bitBE(0xD8,streamFile);
|
||||
if (vgmstream->interleave_block_size == 0)
|
||||
vgmstream->interleave_block_size = read_32bitBE(0x14,streamFile) / channel_count;
|
||||
|
||||
dsp_read_coefs_be(vgmstream,streamFile,0x118,0x60);
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream, streamFile, start_offset))
|
||||
goto fail;
|
||||
close_streamfile(temp_sf);
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_streamfile(temp_sf);
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* .nub is14 - from Namco NUB archives [Tales of Vesperia (PS3)] */
|
||||
/* .nub is14 - from Namco NUB archives [Tales of Vesperia (PS3), Mojipittan (Wii)] */
|
||||
VGMSTREAM * init_vgmstream_nub_is14(STREAMFILE *streamFile) {
|
||||
VGMSTREAM *vgmstream = NULL;
|
||||
STREAMFILE *temp_sf = NULL;
|
||||
|
@ -500,6 +571,7 @@ VGMSTREAM * init_vgmstream_nub_is14(STREAMFILE *streamFile) {
|
|||
read_32bit = read_32bitLE;
|
||||
}
|
||||
|
||||
/* info header: null (even when BSNF loops) */
|
||||
|
||||
/* paste header+data together and pass to meta */
|
||||
header_offset = 0xBC;
|
||||
|
|
|
@ -92,7 +92,7 @@ VGMSTREAM * init_vgmstream_nus3audio(STREAMFILE *streamFile) {
|
|||
/* init the VGMSTREAM */
|
||||
switch(codec) {
|
||||
case IDSP:
|
||||
vgmstream = init_vgmstream_idsp_nus3(temp_streamFile);
|
||||
vgmstream = init_vgmstream_idsp_namco(temp_streamFile);
|
||||
if (!vgmstream) goto fail;
|
||||
break;
|
||||
case OPUS:
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
|
||||
typedef enum { /*XMA_RAW, ATRAC3,*/ IDSP, ATRAC9, OPUS, BNSF, /*PCM, XMA_RIFF*/ } nus3bank_codec;
|
||||
#include "nus3bank_streamfile.h"
|
||||
|
||||
typedef enum { IDSP, IVAG, BNSF, RIFF, OPUS, RIFF_ENC, } nus3bank_codec;
|
||||
|
||||
/* .nus3bank - Namco's newest audio container [Super Smash Bros (Wii U), idolmaster (PS4))] */
|
||||
VGMSTREAM * init_vgmstream_nus3bank(STREAMFILE *streamFile) {
|
||||
VGMSTREAM *vgmstream = NULL;
|
||||
STREAMFILE *temp_streamFile = NULL;
|
||||
STREAMFILE *temp_sf = NULL;
|
||||
off_t tone_offset = 0, pack_offset = 0, name_offset = 0, subfile_offset = 0;
|
||||
size_t name_size = 0, subfile_size = 0;
|
||||
nus3bank_codec codec;
|
||||
|
@ -14,7 +16,9 @@ VGMSTREAM * init_vgmstream_nus3bank(STREAMFILE *streamFile) {
|
|||
int total_subsongs, target_subsong = streamFile->stream_index;
|
||||
|
||||
/* checks */
|
||||
if (!check_extensions(streamFile, "nus3bank"))
|
||||
/* .nub2: early [THE iDOLM@STER 2 (PS3/X360)]
|
||||
* .nus3bank: standard */
|
||||
if (!check_extensions(streamFile, "nub2,nus3bank"))
|
||||
goto fail;
|
||||
if (read_32bitBE(0x00,streamFile) != 0x4E555333) /* "NUS3" */
|
||||
goto fail;
|
||||
|
@ -23,6 +27,8 @@ VGMSTREAM * init_vgmstream_nus3bank(STREAMFILE *streamFile) {
|
|||
if (read_32bitBE(0x0c,streamFile) != 0x544F4320) /* "TOC\0" */
|
||||
goto fail;
|
||||
|
||||
/* header is always LE, while contained files may use another endianness */
|
||||
|
||||
/* parse TOC with all existing chunks and sizes (offsets must be derived) */
|
||||
{
|
||||
int i;
|
||||
|
@ -43,8 +49,8 @@ VGMSTREAM * init_vgmstream_nus3bank(STREAMFILE *streamFile) {
|
|||
|
||||
case 0x50524F50: /* "PROP": project info */
|
||||
case 0x42494E46: /* "BINF": bank info (filename) */
|
||||
case 0x47525020: /* "GRP ": ? */
|
||||
case 0x44544F4E: /* "DTON": ? */
|
||||
case 0x47525020: /* "GRP ": cues/events with names? */
|
||||
case 0x44544F4E: /* "DTON": related to GRP? */
|
||||
case 0x4D41524B: /* "MARK": ? */
|
||||
case 0x4A554E4B: /* "JUNK": padding */
|
||||
default:
|
||||
|
@ -72,69 +78,108 @@ VGMSTREAM * init_vgmstream_nus3bank(STREAMFILE *streamFile) {
|
|||
if (target_subsong == 0) target_subsong = 1;
|
||||
|
||||
for (i = 0; i < entries; i++) {
|
||||
off_t header_offset, header_suboffset, stream_name_offset, stream_offset;
|
||||
size_t stream_name_size, stream_size;
|
||||
off_t tone_header_offset = read_32bitLE(tone_offset+0x04+(i*0x08)+0x00, streamFile);
|
||||
size_t tone_header_size = read_32bitLE(tone_offset+0x04+(i*0x08)+0x04, streamFile);
|
||||
off_t offset, tone_header_offset, stream_name_offset, stream_offset;
|
||||
size_t tone_header_size, stream_name_size, stream_size;
|
||||
uint8_t flags2;
|
||||
|
||||
tone_header_offset = read_32bitLE(tone_offset+0x04+(i*0x08)+0x00, streamFile);
|
||||
tone_header_size = read_32bitLE(tone_offset+0x04+(i*0x08)+0x04, streamFile);
|
||||
|
||||
offset = tone_offset + tone_header_offset;
|
||||
//;VGM_LOG("NUS3BANK: tone at %lx, size %x\n", tone_offset + tone_header_offset, tone_header_size);
|
||||
|
||||
if (tone_header_size <= 0x0c) {
|
||||
//VGM_LOG("NUS3BANK: bad tone at %lx, size %x\n", tone_offset + tone_header_offset, tone_header_size);
|
||||
continue; /* ignore non-sounds */
|
||||
}
|
||||
|
||||
header_offset = tone_offset + tone_header_offset;
|
||||
/* 0x00: type? normally 0x00 and rarely 0x09 */
|
||||
/* 0x04: usually -1, found when tone is not a stream (most flags are off too) */
|
||||
/* 0x06: flags1 */
|
||||
flags2 = read_8bit(offset + 0x07, streamFile);
|
||||
offset += 0x08;
|
||||
|
||||
stream_name_size = read_8bit(header_offset+0x0c, streamFile); /* includes null */
|
||||
stream_name_offset = header_offset+0x0d;
|
||||
header_suboffset = 0x0c + 0x01 + stream_name_size;
|
||||
if (header_suboffset % 0x04) /* padded */
|
||||
header_suboffset += (0x04 - (header_suboffset % 0x04));
|
||||
if (read_32bitLE(header_offset+header_suboffset+0x04, streamFile) != 0x08) {
|
||||
continue; /* ignore non-sounds, too */
|
||||
/* flags3-6 (early .nub2 and some odd non-stream don't have them) */
|
||||
if (flags2 & 0x80) {
|
||||
offset += 0x04;
|
||||
}
|
||||
|
||||
stream_offset = read_32bitLE(header_offset+header_suboffset+0x08, streamFile) + pack_offset;
|
||||
stream_size = read_32bitLE(header_offset+header_suboffset+0x0c, streamFile);
|
||||
stream_name_size = read_8bit(offset + 0x00, streamFile); /* includes null */
|
||||
stream_name_offset = offset + 0x01;
|
||||
offset += align_size_to_block(0x01 + stream_name_size, 0x04); /* padded if needed */
|
||||
|
||||
/* 0x00: subtype? should be 0 */
|
||||
if (read_32bitLE(offset + 0x04, streamFile) != 0x08) { /* flag? */
|
||||
//;VGM_LOG("NUS3BANK: bad tone type at %lx, size %x\n", tone_offset + tone_header_offset, tone_header_size);
|
||||
continue;
|
||||
}
|
||||
|
||||
stream_offset = read_32bitLE(offset + 0x08, streamFile) + pack_offset;
|
||||
stream_size = read_32bitLE(offset + 0x0c, streamFile);
|
||||
//;VGM_LOG("NUS3BANK: so=%lx, ss=%x\n", stream_offset, stream_size);
|
||||
|
||||
/* Beyond are a bunch of sub-chunks of unknown size with floats and stuff, that seemingly
|
||||
* appear depending on flags1-6. One is a small stream header, which contains basic
|
||||
* sample rate/channels/loops/etc, but it's actually optional and maybe controlled by
|
||||
* flags 3-6 (ex. not found in .nub2) */
|
||||
|
||||
/* happens in some sfx packs (ex. Taiko no Tatsujin Switch's se_minigame) */
|
||||
if (stream_size == 0) {
|
||||
continue; /* happens in some sfx packs */
|
||||
//;VGM_LOG("NUS3BANK: bad tone stream size at %lx, size %x\n", tone_offset + tone_header_offset, tone_header_size);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
total_subsongs++;
|
||||
if (total_subsongs == target_subsong) {
|
||||
//;VGM_LOG("NUS3BANK: subsong header offset %lx\n", header_offset);
|
||||
//;VGM_LOG("NUS3BANK: subsong header offset %lx\n", offset);
|
||||
subfile_offset = stream_offset;
|
||||
subfile_size = stream_size;
|
||||
name_size = stream_name_size;
|
||||
name_offset = stream_name_offset;
|
||||
//todo improve, codec may be related to header values at 0x00/0x06/0x08
|
||||
codec_id = read_32bitBE(subfile_offset, streamFile);
|
||||
}
|
||||
/* continue counting subsongs */
|
||||
}
|
||||
|
||||
if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail;
|
||||
if (subfile_offset == 0 || codec_id == 0) {
|
||||
if (subfile_offset == 0) {
|
||||
VGM_LOG("NUS3BANK: subsong not found\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
//todo improve, codec may be in one of the tone sub-chunks (or other chunk? one bank seems to use one codec)
|
||||
codec_id = read_32bitBE(subfile_offset, streamFile);
|
||||
switch(codec_id) {
|
||||
case 0x49445350: /* "IDSP" [Super Smash Bros. for 3DS (3DS)] */
|
||||
codec = IDSP;
|
||||
fake_ext = "idsp";
|
||||
break;
|
||||
case 0x52494646: /* "RIFF" [idolm@ster: Platinum Stars (PS4)] */
|
||||
//todo this can be standard PCM RIFF too, ex. Mario Kart Arcade GP DX (PC) (works but should have better detection)
|
||||
codec = ATRAC9;
|
||||
fake_ext = "at9";
|
||||
|
||||
case 0x52494646: /* "RIFF" [THE iDOLM@STER 2 (PS3), Mario Kart Arcade GP DX (PC), idolm@ster: Platinum Stars (PS4)] */
|
||||
codec = RIFF;
|
||||
fake_ext = "wav"; //TODO: works but should have better detection
|
||||
break;
|
||||
|
||||
case 0x4F505553: /* "OPUS" [Taiko no Tatsujin (Switch)] */
|
||||
codec = OPUS;
|
||||
fake_ext = "opus";
|
||||
break;
|
||||
|
||||
case 0x424E5346: /* "BNSF" [Naruto Shippuden Ultimate Ninja Storm 4 (PC)] */
|
||||
codec = BNSF;
|
||||
fake_ext = "bnsf";
|
||||
break;
|
||||
|
||||
case 0x49564147: /* "IVAG" [THE iDOLM@STER 2 (PS3), THE iDOLM@STER: Gravure For You! (PS3)] */
|
||||
codec = IVAG;
|
||||
fake_ext = "ivag";
|
||||
break;
|
||||
|
||||
case 0x552AAF17: /* "RIFF" with encrypted header (not data) [THE iDOLM@STER 2 (X360)] */
|
||||
codec = RIFF_ENC;
|
||||
fake_ext = "xma";
|
||||
break;
|
||||
|
||||
default:
|
||||
VGM_LOG("NUS3BANK: unknown codec %x\n", codec_id);
|
||||
goto fail;
|
||||
|
@ -143,27 +188,41 @@ VGMSTREAM * init_vgmstream_nus3bank(STREAMFILE *streamFile) {
|
|||
|
||||
//;VGM_LOG("NUS3BANK: subfile=%lx, size=%x\n", subfile_offset, subfile_size);
|
||||
|
||||
temp_streamFile = setup_subfile_streamfile(streamFile, subfile_offset,subfile_size, fake_ext);
|
||||
if (!temp_streamFile) goto fail;
|
||||
temp_sf = setup_subfile_streamfile(streamFile, subfile_offset, subfile_size, fake_ext);
|
||||
if (!temp_sf) goto fail;
|
||||
|
||||
/* init the VGMSTREAM */
|
||||
switch(codec) {
|
||||
case IDSP:
|
||||
vgmstream = init_vgmstream_idsp_nus3(temp_streamFile);
|
||||
vgmstream = init_vgmstream_idsp_namco(temp_sf);
|
||||
if (!vgmstream) goto fail;
|
||||
break;
|
||||
|
||||
case OPUS:
|
||||
vgmstream = init_vgmstream_opus_nus3(temp_streamFile);
|
||||
vgmstream = init_vgmstream_opus_nus3(temp_sf);
|
||||
if (!vgmstream) goto fail;
|
||||
break;
|
||||
case ATRAC9:
|
||||
vgmstream = init_vgmstream_riff(temp_streamFile);
|
||||
|
||||
case RIFF:
|
||||
vgmstream = init_vgmstream_riff(temp_sf);
|
||||
if (!vgmstream) goto fail;
|
||||
break;
|
||||
|
||||
case BNSF:
|
||||
vgmstream = init_vgmstream_bnsf(temp_streamFile);
|
||||
vgmstream = init_vgmstream_bnsf(temp_sf);
|
||||
if (!vgmstream) goto fail;
|
||||
break;
|
||||
|
||||
case IVAG:
|
||||
vgmstream = init_vgmstream_ivag(temp_sf);
|
||||
if (!vgmstream) goto fail;
|
||||
break;
|
||||
|
||||
case RIFF_ENC:
|
||||
vgmstream = init_vgmstream_nus3bank_encrypted(temp_sf);
|
||||
if (!vgmstream) goto fail;
|
||||
break;
|
||||
|
||||
default:
|
||||
goto fail;
|
||||
}
|
||||
|
@ -173,11 +232,38 @@ VGMSTREAM * init_vgmstream_nus3bank(STREAMFILE *streamFile) {
|
|||
read_string(vgmstream->stream_name,name_size, name_offset,streamFile);
|
||||
|
||||
|
||||
close_streamfile(temp_streamFile);
|
||||
close_streamfile(temp_sf);
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_streamfile(temp_streamFile);
|
||||
close_streamfile(temp_sf);
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* encrypted RIFF from the above, in case kids try to extract and play single files */
|
||||
VGMSTREAM* init_vgmstream_nus3bank_encrypted(STREAMFILE *sf) {
|
||||
VGMSTREAM *vgmstream = NULL;
|
||||
STREAMFILE *temp_sf = NULL;
|
||||
|
||||
|
||||
/* checks */
|
||||
if (!check_extensions(sf, "nus3bank,xma"))
|
||||
goto fail;
|
||||
if (read_u32be(0x00, sf) != 0x552AAF17) /* "RIFF" encrypted */
|
||||
goto fail;
|
||||
|
||||
temp_sf = setup_nus3bank_streamfile(sf, 0x00);
|
||||
if (!temp_sf) goto fail;
|
||||
|
||||
vgmstream = init_vgmstream_xma(temp_sf);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
close_streamfile(temp_sf);
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_streamfile(temp_sf);
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,126 @@
|
|||
#ifndef _NUS3BANK_STREAMFILE_H_
|
||||
#define _NUS3BANK_STREAMFILE_H_
|
||||
#include "../streamfile.h"
|
||||
|
||||
|
||||
static uint32_t swap_endian32(uint32_t v) {
|
||||
return ((v & 0xff000000) >> 24u) |
|
||||
((v & 0x00ff0000) >> 8u) |
|
||||
((v & 0x0000ff00) << 8u) |
|
||||
((v & 0x000000ff) << 24u);
|
||||
}
|
||||
|
||||
|
||||
#define KEY_MAX_SIZE 0x1000
|
||||
|
||||
typedef struct {
|
||||
uint8_t key[KEY_MAX_SIZE];
|
||||
int key_len;
|
||||
} io_data_t;
|
||||
|
||||
static size_t io_read(STREAMFILE *sf, uint8_t *dest, off_t offset, size_t length, io_data_t *data) {
|
||||
int i;
|
||||
size_t bytes = read_streamfile(dest, offset, length, sf);
|
||||
|
||||
/* decrypt data (xor) */
|
||||
if (offset < data->key_len) {
|
||||
for (i = 0; i < bytes; i++) {
|
||||
if (offset + i < data->key_len)
|
||||
dest[i] ^= data->key[offset + i];
|
||||
}
|
||||
}
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
/* decrypts RIFF streams in NUS3BANK */
|
||||
static STREAMFILE* setup_nus3bank_streamfile(STREAMFILE *sf, off_t start) {
|
||||
STREAMFILE *new_sf = NULL;
|
||||
io_data_t io_data = {0};
|
||||
|
||||
/* setup key */
|
||||
{
|
||||
uint32_t base_key, chunk_key;
|
||||
uint8_t buf[KEY_MAX_SIZE];
|
||||
uint32_t chunk_type, chunk_size;
|
||||
int pos, data_pos, bytes;
|
||||
|
||||
/* Header is XORed with a base key and a derived chunk type/size key, while chunk data is XORed with
|
||||
* unencrypted data itself, so we need to find where "data" starts then do another pass to properly set key.
|
||||
* Original code handles RIFF's "data" and also BNSF's "sdat" too, encrypted BNSF aren't known though. */
|
||||
|
||||
bytes = read_streamfile(buf, start, sizeof(buf), sf);
|
||||
if (bytes < 0x800) goto fail; /* files of 1 XMA block do exist, but not less */
|
||||
|
||||
base_key = 0x0763E951;
|
||||
chunk_type = get_u32be(buf + 0x00) ^ base_key;
|
||||
chunk_size = get_u32be(buf + 0x04) ^ base_key;
|
||||
if (chunk_type != 0x52494646) /* "RIFF" */
|
||||
goto fail;
|
||||
|
||||
chunk_key = base_key ^ (((chunk_size >> 16u) & 0x0000FFFF) | ((chunk_size << 16u) & 0xFFFF0000)); /* ROTr 16 size */
|
||||
|
||||
/* find "data" */
|
||||
pos = 0x0c;
|
||||
while(pos < sizeof(buf)) {
|
||||
chunk_type = get_u32be(buf + pos + 0x00) ^ chunk_key;
|
||||
chunk_size = get_u32be(buf + pos + 0x04) ^ chunk_key;
|
||||
chunk_size = swap_endian32(chunk_size);
|
||||
pos += 0x08;
|
||||
|
||||
if (chunk_type == 0x64617461) { /* "data" */
|
||||
data_pos = pos;
|
||||
break;
|
||||
}
|
||||
|
||||
if (pos + chunk_size > sizeof(buf) - 0x08) {
|
||||
VGM_LOG("NUS3 SF: header too big\n");
|
||||
goto fail; /* max around 0x400 */
|
||||
}
|
||||
|
||||
pos += chunk_size;
|
||||
}
|
||||
|
||||
|
||||
/* setup key */
|
||||
put_u32be(io_data.key + 0x00, base_key);
|
||||
put_u32be(io_data.key + 0x04, base_key);
|
||||
put_u32be(io_data.key + 0x08, chunk_key);
|
||||
pos = 0x0c; /* after WAVE */
|
||||
|
||||
while (pos < data_pos) {
|
||||
chunk_type = get_u32be(buf + pos + 0x00) ^ chunk_key;
|
||||
chunk_size = get_u32be(buf + pos + 0x04) ^ chunk_key;
|
||||
chunk_size = swap_endian32(chunk_size);
|
||||
|
||||
put_u32be(io_data.key + pos + 0x00, chunk_key);
|
||||
put_u32be(io_data.key + pos + 0x04, chunk_key);
|
||||
pos += 0x08;
|
||||
|
||||
if (pos >= data_pos)
|
||||
break;
|
||||
|
||||
/* buf must contain data of at least chunk_size */
|
||||
if (data_pos + chunk_size >= sizeof(buf)) {
|
||||
VGM_LOG("NUS3 SF: chunk too big\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
memcpy(io_data.key + pos, buf + data_pos, chunk_size);
|
||||
|
||||
pos += chunk_size;
|
||||
}
|
||||
|
||||
io_data.key_len = data_pos;
|
||||
}
|
||||
|
||||
|
||||
new_sf = open_wrap_streamfile(sf);
|
||||
new_sf = open_io_streamfile(new_sf, &io_data, sizeof(io_data_t), io_read, NULL);
|
||||
return new_sf;
|
||||
fail:
|
||||
close_streamfile(sf);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif /* _NUS3BANK_STREAMFILE_H_ */
|
|
@ -1,84 +0,0 @@
|
|||
#include "meta.h"
|
||||
#include "../util.h"
|
||||
|
||||
/* IVAG
|
||||
- The Idolm@ster: Gravure For You! Vol. 3 (PS3)
|
||||
|
||||
Appears to be two VAGp streams interleaved.
|
||||
*/
|
||||
VGMSTREAM * init_vgmstream_ps3_ivag(STREAMFILE *streamFile)
|
||||
{
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
char filename[PATH_LIMIT];
|
||||
|
||||
off_t start_offset;
|
||||
|
||||
int loop_flag = 0;
|
||||
int channel_count;
|
||||
|
||||
/* check extension, case insensitive */
|
||||
streamFile->get_name(streamFile,filename,sizeof(filename));
|
||||
if (strcasecmp("ivag",filename_extension(filename))) goto fail;
|
||||
|
||||
/* check header */
|
||||
if (read_32bitBE(0x00,streamFile) != 0x49564147) // "IVAG"
|
||||
goto fail;
|
||||
|
||||
// channel count
|
||||
channel_count = read_32bitBE(0x08, streamFile);
|
||||
|
||||
// header size
|
||||
start_offset = 0x40 + (0x40 * channel_count);
|
||||
|
||||
// loop flag
|
||||
if ((read_32bitBE(0x14, streamFile) != 0 ||
|
||||
(read_32bitBE(0x18, streamFile) != 0)))
|
||||
{
|
||||
loop_flag = 1;
|
||||
}
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channel_count,loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
/* fill in the vital statistics */
|
||||
vgmstream->channels = channel_count;
|
||||
vgmstream->sample_rate = read_32bitBE(0x0C,streamFile);
|
||||
vgmstream->coding_type = coding_PSX;
|
||||
vgmstream->num_samples = read_32bitBE(0x10,streamFile);
|
||||
|
||||
if (loop_flag)
|
||||
{
|
||||
vgmstream->loop_start_sample = read_32bitBE(0x14,streamFile);
|
||||
vgmstream->loop_end_sample = read_32bitBE(0x18,streamFile);
|
||||
}
|
||||
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = read_32bitBE(0x1C,streamFile);
|
||||
vgmstream->meta_type = meta_PS3_IVAG;
|
||||
|
||||
/* open the file for reading */
|
||||
{
|
||||
int i;
|
||||
STREAMFILE * file;
|
||||
file = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE);
|
||||
if (!file) goto fail;
|
||||
|
||||
for (i=0;i<channel_count;i++)
|
||||
{
|
||||
vgmstream->ch[i].streamfile = file;
|
||||
|
||||
vgmstream->ch[i].channel_start_offset=
|
||||
vgmstream->ch[i].offset=start_offset + (vgmstream->interleave_block_size * i);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return vgmstream;
|
||||
|
||||
/* clean up anything we may have opened */
|
||||
fail:
|
||||
if (vgmstream) close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
|
@ -465,20 +465,23 @@ static void parse_sead_mab_name(sead_header *sead, STREAMFILE *sf) {
|
|||
static void parse_sead_sab_name(sead_header *sead, STREAMFILE *sf) {
|
||||
int32_t (*read_32bit)(off_t,STREAMFILE*) = sead->big_endian ? read_32bitBE : read_32bitLE;
|
||||
int16_t (*read_16bit)(off_t,STREAMFILE*) = sead->big_endian ? read_16bitBE : read_16bitLE;
|
||||
int i, entries, snd_id, wave_id, snd_found = 0;
|
||||
int i, snd_entries, trk_entries, snd_id, wave_id, snd_found = 0;
|
||||
size_t size;
|
||||
off_t entry_offset;
|
||||
|
||||
|
||||
//todo looks mostly correct for many subsongs but in rare cases wave_ids aren't referenced
|
||||
// or maybe id needs another jump (seq?) (ex. DQB se_break_soil, FFXV aircraftzeroone)
|
||||
// or maybe id needs another jump (seq?) (ex. DQB se_break_soil, FFXV aircraftzeroone, FFXV 03bt100031pc00)
|
||||
|
||||
snd_entries = read_16bit(sead->snd_offset + 0x04, sf);
|
||||
trk_entries = read_16bit(sead->trk_offset + 0x04, sf);
|
||||
|
||||
/* parse "trk" (track info) */
|
||||
entries = read_16bit(sead->trk_offset + 0x04, sf);
|
||||
for (i = 0; i < entries; i++) {
|
||||
for (i = 0; i < trk_entries; i++) {
|
||||
entry_offset = sead->trk_offset + read_32bit(sead->trk_offset + 0x10 + i*0x04, sf);
|
||||
|
||||
/* 0x00: type/count? */
|
||||
/* 0x00: type? */
|
||||
/* 0x01: subtype? */
|
||||
size = read_16bit(entry_offset + 0x02, sf); /* bigger if 'type=03' */
|
||||
/* 0x04: trk id? */
|
||||
/* 0x04: some id? */
|
||||
|
@ -499,16 +502,21 @@ static void parse_sead_sab_name(sead_header *sead, STREAMFILE *sf) {
|
|||
}
|
||||
}
|
||||
|
||||
if (snd_found && snd_id >= snd_entries) {
|
||||
VGM_LOG("SEAD: bad snd_id found\n");
|
||||
snd_found = 0;
|
||||
}
|
||||
|
||||
if (!snd_found) {
|
||||
if (sead->total_subsongs == 1) {
|
||||
if (sead->total_subsongs == 1 || snd_entries == 1) {
|
||||
snd_id = 0; /* meh */
|
||||
VGM_LOG("SEAD: snd_id not found, using first\n");
|
||||
} else {
|
||||
VGM_LOG("SEAD: snd_id not found, subsongs=%i, snd=%i, trk=%i\n", sead->total_subsongs, snd_entries, trk_entries);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* parse "snd " (sound info) */
|
||||
{
|
||||
off_t entry_offset = sead->snd_offset + read_32bit(sead->snd_offset + 0x10 + snd_id*0x04, sf);
|
||||
|
|
|
@ -1,103 +1,100 @@
|
|||
#include "meta.h"
|
||||
#include "../layout/layout.h"
|
||||
#include "../util.h"
|
||||
|
||||
/* THP (Just play audio from .thp movie file)
|
||||
by fastelbja */
|
||||
|
||||
/* THP - Nintendo movie format found in GC/Wii games */
|
||||
VGMSTREAM* init_vgmstream_thp(STREAMFILE *streamFile) {
|
||||
|
||||
VGMSTREAM *vgmstream = NULL;
|
||||
|
||||
char filename[PATH_LIMIT];
|
||||
off_t start_offset;
|
||||
|
||||
uint32_t maxAudioSize=0;
|
||||
|
||||
uint32_t numComponents;
|
||||
off_t componentTypeOffset;
|
||||
off_t componentDataOffset;
|
||||
|
||||
char thpVersion;
|
||||
|
||||
int loop_flag;
|
||||
int channel_count=-1;
|
||||
off_t start_offset, component_type_offset, component_data_offset;
|
||||
uint32_t version, max_audio_size;
|
||||
int num_components;
|
||||
int loop_flag, channel_count;
|
||||
int i;
|
||||
|
||||
/* check extension, case insensitive */
|
||||
streamFile->get_name(streamFile,filename,sizeof(filename));
|
||||
if (strcasecmp("thp",filename_extension(filename)) &&
|
||||
strcasecmp("dsp",filename_extension(filename))) goto fail;
|
||||
|
||||
/* check header */
|
||||
if (read_32bitBE(0x00,streamFile) != 0x54485000)
|
||||
/* checks */
|
||||
/* .thp: actual extension
|
||||
* .dsp: fake extension?
|
||||
* (extensionless): Fragile (Wii) */
|
||||
if (!check_extensions(streamFile, "thp,dsp,"))
|
||||
goto fail;
|
||||
if (read_32bitBE(0x00,streamFile) != 0x54485000) /* "THP\0" */
|
||||
goto fail;
|
||||
|
||||
maxAudioSize = read_32bitBE(0x0C,streamFile);
|
||||
thpVersion = read_8bit(0x06,streamFile);
|
||||
version = read_32bitBE(0x04,streamFile); /* 16b+16b major/minor */
|
||||
/* 0x08: max buffer size */
|
||||
max_audio_size = read_32bitBE(0x0C,streamFile);
|
||||
/* 0x10: fps in float */
|
||||
/* 0x14: block count */
|
||||
/* 0x18: first block size */
|
||||
/* 0x1c: data size */
|
||||
|
||||
if(maxAudioSize==0) // no sound
|
||||
if (version != 0x00010000 && version != 0x00011000) /* v1.0 (~2002) or v1.1 (rest) */
|
||||
goto fail;
|
||||
if (max_audio_size == 0) /* no sound */
|
||||
goto fail;
|
||||
|
||||
loop_flag = 0; // allways unloop
|
||||
|
||||
/* fill in the vital statistics */
|
||||
if(thpVersion==0x10) {
|
||||
start_offset = read_32bitBE(0x24,streamFile);
|
||||
/* No idea what's up with this */
|
||||
if (start_offset == 0)
|
||||
start_offset = read_32bitBE(0x28,streamFile);
|
||||
} else
|
||||
component_type_offset = read_32bitBE(0x20,streamFile);
|
||||
/* 0x24: block offsets table offset (optional, for seeking) */
|
||||
start_offset = read_32bitBE(0x28,streamFile);
|
||||
/* 0x2c: last block offset */
|
||||
|
||||
// Get info from the first block
|
||||
componentTypeOffset = read_32bitBE(0x20,streamFile);
|
||||
numComponents = read_32bitBE(componentTypeOffset ,streamFile);
|
||||
componentDataOffset=componentTypeOffset+0x14;
|
||||
componentTypeOffset+=4;
|
||||
/* first component "type" x16 then component headers */
|
||||
num_components = read_32bitBE(component_type_offset,streamFile);
|
||||
component_type_offset += 0x04;
|
||||
component_data_offset = component_type_offset + 0x10;
|
||||
|
||||
/* parse "component" (data that goes into blocks) */
|
||||
for (i = 0; i < num_components; i++) {
|
||||
int type = read_8bit(component_type_offset + i,streamFile);
|
||||
|
||||
if (type == 0x00) { /* video */
|
||||
if (version == 0x00010000)
|
||||
component_data_offset += 0x08; /* width + height */
|
||||
else
|
||||
component_data_offset += 0x0c; /* width + height + format? */
|
||||
}
|
||||
else if (type == 0x01) { /* audio */
|
||||
/* parse below */
|
||||
#if 0
|
||||
if (version == 0x00010000)
|
||||
component_data_offset += 0x0c; /* channels + sample rate + samples */
|
||||
else
|
||||
component_data_offset += 0x10; /* channels + sample rate + samples + format? */
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
else { /* 0xFF / no data (reserved as THP is meant to be extensible) */
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
/* official docs remark original's audio is adjusted to match GC's hardware rate
|
||||
* (48000 > 48043 / 32000 > 32028), not sure if that means ouput sample rate should
|
||||
* adjusted, but we can't detect Wii (non adjusted) .thp tho */
|
||||
|
||||
loop_flag = 0;
|
||||
channel_count = read_32bitBE(component_data_offset + 0x00,streamFile);
|
||||
|
||||
for(i=0;i<numComponents;i++) {
|
||||
if(read_8bit(componentTypeOffset+i,streamFile)==1) {
|
||||
channel_count=read_32bitBE(componentDataOffset,streamFile);
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channel_count,loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->channels=channel_count;
|
||||
vgmstream->sample_rate=read_32bitBE(componentDataOffset+4,streamFile);
|
||||
vgmstream->num_samples=read_32bitBE(componentDataOffset+8,streamFile);
|
||||
break;
|
||||
} else {
|
||||
if(thpVersion==0x10)
|
||||
componentDataOffset+=0x0c;
|
||||
else
|
||||
componentDataOffset+=0x08;
|
||||
}
|
||||
}
|
||||
|
||||
/* open the file for reading */
|
||||
{
|
||||
int i;
|
||||
STREAMFILE * file;
|
||||
file = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE);
|
||||
if (!file) goto fail;
|
||||
for (i=0;i<channel_count;i++) {
|
||||
vgmstream->ch[i].streamfile = file;
|
||||
}
|
||||
}
|
||||
|
||||
vgmstream->full_block_size = read_32bitBE(0x18,streamFile); /* block size of current block, changes every time */
|
||||
block_update_thp(start_offset,vgmstream);
|
||||
vgmstream->sample_rate = read_32bitBE(component_data_offset + 0x04,streamFile);
|
||||
vgmstream->num_samples = read_32bitBE(component_data_offset + 0x08,streamFile);
|
||||
|
||||
vgmstream->meta_type = meta_THP;
|
||||
vgmstream->coding_type = coding_NGC_DSP;
|
||||
vgmstream->layout_type = layout_blocked_thp;
|
||||
vgmstream->meta_type = meta_THP;
|
||||
/* coefs are in every block */
|
||||
|
||||
vgmstream->full_block_size = read_32bitBE(0x18,streamFile); /* next block size */
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
|
||||
goto fail;
|
||||
return vgmstream;
|
||||
|
||||
/* clean up anything we may have opened */
|
||||
fail:
|
||||
if (vgmstream) close_vgmstream(vgmstream);
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
|
|
|
@ -123,6 +123,7 @@ typedef struct {
|
|||
uint32_t loop_start_segment;
|
||||
uint32_t loop_end_segment;
|
||||
int is_loop_keep;
|
||||
int is_loop_auto;
|
||||
|
||||
txtp_entry default_entry;
|
||||
int default_entry_set;
|
||||
|
@ -188,7 +189,7 @@ VGMSTREAM * init_vgmstream_txtp(STREAMFILE *streamFile) {
|
|||
txtp->vgmstream[i] = init_vgmstream_from_STREAMFILE(temp_streamFile);
|
||||
close_streamfile(temp_streamFile);
|
||||
if (!txtp->vgmstream[i]) {
|
||||
VGM_LOG("TXTP: cannot open vgmstream for %s\n", txtp->entry[i].filename);
|
||||
VGM_LOG("TXTP: cannot open vgmstream for %s#%i\n", txtp->entry[i].filename, txtp->entry[i].subsong);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
|
@ -313,8 +314,13 @@ static int make_group_segment(txtp_header* txtp, int position, int count) {
|
|||
|
||||
/* loop settings only make sense if this group becomes final vgmstream */
|
||||
if (position == 0 && txtp->vgmstream_count == count) {
|
||||
if (txtp->loop_start_segment && !txtp->loop_end_segment)
|
||||
if (txtp->loop_start_segment && !txtp->loop_end_segment) {
|
||||
txtp->loop_end_segment = count;
|
||||
}
|
||||
else if (txtp->is_loop_auto) { /* auto set to last segment */
|
||||
txtp->loop_start_segment = count;
|
||||
txtp->loop_end_segment = count;
|
||||
}
|
||||
loop_flag = (txtp->loop_start_segment > 0 && txtp->loop_start_segment <= count);
|
||||
}
|
||||
|
||||
|
@ -611,7 +617,7 @@ static int get_int(const char * config, int *value) {
|
|||
int n,m;
|
||||
int temp;
|
||||
|
||||
m = sscanf(config, " %i%n", &temp,&n);
|
||||
m = sscanf(config, " %d%n", &temp,&n);
|
||||
if (m != 1 || temp < 0)
|
||||
return 0;
|
||||
|
||||
|
@ -645,7 +651,7 @@ static int get_time(const char * config, double *value_f, int32_t *value_i) {
|
|||
char temp_c;
|
||||
|
||||
/* test if format is hour: N:N(.n) or N_N(.n) */
|
||||
m = sscanf(config, " %i%c%i%n", &temp_i1,&temp_c,&temp_i2,&n);
|
||||
m = sscanf(config, " %d%c%d%n", &temp_i1,&temp_c,&temp_i2,&n);
|
||||
if (m == 3 && (temp_c == ':' || temp_c == '_')) {
|
||||
m = sscanf(config, " %lf%c%lf%n", &temp_f1,&temp_c,&temp_f2,&n);
|
||||
if (m != 3 || /*temp_f1 < 0.0 ||*/ temp_f1 >= 60.0 || temp_f2 < 0.0 || temp_f2 >= 60.0)
|
||||
|
@ -656,7 +662,7 @@ static int get_time(const char * config, double *value_f, int32_t *value_i) {
|
|||
}
|
||||
|
||||
/* test if format is seconds: N.n */
|
||||
m = sscanf(config, " %i.%i%n", &temp_i1,&temp_i2,&n);
|
||||
m = sscanf(config, " %d.%d%n", &temp_i1,&temp_i2,&n);
|
||||
if (m == 2) {
|
||||
m = sscanf(config, " %lf%n", &temp_f1,&n);
|
||||
if (m != 1 /*|| temp_f1 < 0.0*/)
|
||||
|
@ -677,7 +683,7 @@ static int get_time(const char * config, double *value_f, int32_t *value_i) {
|
|||
}
|
||||
|
||||
/* assume format is samples: N */
|
||||
m = sscanf(config, " %i%n", &temp_i1,&n);
|
||||
m = sscanf(config, " %d%n", &temp_i1,&n);
|
||||
if (m == 1) {
|
||||
/* allow negative samples for special meanings */
|
||||
//if (temp_i1 < 0)
|
||||
|
@ -1389,6 +1395,9 @@ static int parse_keyval(txtp_header * txtp, const char * key, const char * val)
|
|||
if (is_substring(val,"keep")) {
|
||||
txtp->is_loop_keep = 1;
|
||||
}
|
||||
else if (is_substring(val,"auto")) {
|
||||
txtp->is_loop_auto = 1;
|
||||
}
|
||||
else {
|
||||
goto fail;
|
||||
}
|
||||
|
|
|
@ -1525,7 +1525,7 @@ static int parse_stream_codec(ubi_sb_header * sb) {
|
|||
break;
|
||||
|
||||
default:
|
||||
VGM_LOG("Unknown stream_type %02x for version %08x\n", sb->stream_type, sb->version);
|
||||
VGM_LOG("UBI SB: Unknown stream_type %02x for version %08x\n", sb->stream_type, sb->version);
|
||||
goto fail;
|
||||
}
|
||||
} else if (sb->version < 0x000A0000) {
|
||||
|
@ -1539,7 +1539,7 @@ static int parse_stream_codec(ubi_sb_header * sb) {
|
|||
break;
|
||||
|
||||
default:
|
||||
VGM_LOG("Unknown stream_type %02x for version %08x\n", sb->stream_type, sb->version);
|
||||
VGM_LOG("UBI SB: Unknown stream_type %02x for version %08x\n", sb->stream_type, sb->version);
|
||||
goto fail;
|
||||
}
|
||||
} else {
|
||||
|
@ -1555,7 +1555,7 @@ static int parse_stream_codec(ubi_sb_header * sb) {
|
|||
break;
|
||||
case UBI_PSP:
|
||||
/* TODO: IMA using Ubisoft ADPCM frame layout [Splinter Cell: Essentials (PSP)] */
|
||||
VGM_LOG("Unimplemented custom IMA codec.\n");
|
||||
VGM_LOG("UBI SB: Unimplemented custom IMA codec.\n");
|
||||
goto fail;
|
||||
default:
|
||||
sb->codec = UBI_ADPCM;
|
||||
|
@ -1581,7 +1581,7 @@ static int parse_stream_codec(ubi_sb_header * sb) {
|
|||
sb->codec = FMT_AT3;
|
||||
break;
|
||||
default:
|
||||
VGM_LOG("UBI SB: unknown codec for stream_type %x\n", sb->stream_type);
|
||||
VGM_LOG("UBI SB: unknown codec for stream_type %02x\n", sb->stream_type);
|
||||
goto fail;
|
||||
}
|
||||
break;
|
||||
|
@ -1599,7 +1599,7 @@ static int parse_stream_codec(ubi_sb_header * sb) {
|
|||
break;
|
||||
|
||||
default:
|
||||
VGM_LOG("Unknown stream_type %02x\n", sb->stream_type);
|
||||
VGM_LOG("UBI SB: Unknown stream_type %02x\n", sb->stream_type);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
@ -2364,7 +2364,7 @@ static int config_sb_version(ubi_sb_header * sb, STREAMFILE *streamFile) {
|
|||
return 1;
|
||||
}
|
||||
|
||||
/* Rainbow Six 3 (2003)(PC)-bank 0x0000000B */
|
||||
/* Tom Clancy's Rainbow Six 3: Raven Shield + addons (2003)(PC)-bank 0x0000000B */
|
||||
if (sb->version == 0x0000000B && sb->platform == UBI_PC) {
|
||||
config_sb_entry(sb, 0x5c, 0x7c);
|
||||
|
||||
|
@ -2519,7 +2519,7 @@ static int config_sb_version(ubi_sb_header * sb, STREAMFILE *streamFile) {
|
|||
|
||||
/* Batman: Rise of Sin Tzu (2003)(GC)-map 0x000A0002 */
|
||||
/* Prince of Persia: The Sands of Time (2003)(GC)-bank 0x000A0004 / 0x000A0002 (POP1 port) */
|
||||
/* Tom Clancy's Rainbow Six 3 (2003)(Xbox)-bank 0x000A0007 */
|
||||
/* Tom Clancy's Rainbow Six 3 (2003)(GC)-bank 0x000A0007 */
|
||||
if ((sb->version == 0x000A0002 && sb->platform == UBI_GC) ||
|
||||
(sb->version == 0x000A0004 && sb->platform == UBI_GC) ||
|
||||
(sb->version == 0x000A0007 && sb->platform == UBI_GC)) {
|
||||
|
|
|
@ -199,13 +199,15 @@ VGMSTREAM * init_vgmstream_vag(STREAMFILE *streamFile) {
|
|||
interleave = 0x800;
|
||||
loop_flag = 0;
|
||||
}
|
||||
else if (read_32bitBE(0x24, streamFile) == 0x56414778) { /* VAGx" */
|
||||
else if (read_32bitBE(0x24, streamFile) == 0x56414778) { /* "VAGx" */
|
||||
/* Need for Speed: Hot Pursuit 2 (PS2) */
|
||||
start_offset = 0x30;
|
||||
channel_count = read_32bitBE(0x2c, streamFile);
|
||||
channel_size = channel_size / channel_count;
|
||||
loop_flag = 0;
|
||||
|
||||
if (file_size % 0x10 != 0) goto fail;
|
||||
|
||||
/* detect interleave using end markers */
|
||||
interleave = 0;
|
||||
|
||||
|
@ -214,7 +216,7 @@ VGMSTREAM * init_vgmstream_vag(STREAMFILE *streamFile) {
|
|||
off_t end_off = 0;
|
||||
uint8_t flag;
|
||||
|
||||
while (1) {
|
||||
while (offset > start_offset) {
|
||||
offset -= 0x10;
|
||||
flag = read_8bit(offset + 0x01, streamFile);
|
||||
if (flag == 0x01) {
|
||||
|
@ -225,9 +227,9 @@ VGMSTREAM * init_vgmstream_vag(STREAMFILE *streamFile) {
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (offset == start_offset) goto fail;
|
||||
}
|
||||
|
||||
if (!interleave) goto fail;
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
|
|
@ -201,7 +201,9 @@ int vgmstream_tags_next_tag(VGMSTREAM_TAGS* tags, STREAMFILE* tagfile) {
|
|||
|
||||
if (tags->section_found) {
|
||||
/* find possible file tag */
|
||||
ok = sscanf(line, "# %%%[^ \t] %[^\r\n] ", tags->key,tags->val);
|
||||
ok = sscanf(line, "# %%%[^%%]%% %[^\r\n] ", tags->key,tags->val); /* key with spaces */
|
||||
if (ok != 2)
|
||||
ok = sscanf(line, "# %%%[^ \t] %[^\r\n] ", tags->key,tags->val); /* key without */
|
||||
if (ok == 2) {
|
||||
tags_clean(tags);
|
||||
return 1;
|
||||
|
@ -224,7 +226,9 @@ int vgmstream_tags_next_tag(VGMSTREAM_TAGS* tags, STREAMFILE* tagfile) {
|
|||
}
|
||||
|
||||
/* find possible global tag */
|
||||
ok = sscanf(line, "# @%[^ \t] %[^\r\n]", tags->key,tags->val);
|
||||
ok = sscanf(line, "# @%[^@]@ %[^\r\n]", tags->key,tags->val); /* key with spaces */
|
||||
if (ok != 2)
|
||||
ok = sscanf(line, "# @%[^ \t] %[^\r\n]", tags->key,tags->val); /* key without */
|
||||
if (ok == 2) {
|
||||
tags_clean(tags);
|
||||
return 1;
|
||||
|
|
|
@ -59,6 +59,14 @@ void put_16bitBE(uint8_t * buf, int16_t i);
|
|||
|
||||
void put_32bitBE(uint8_t * buf, int32_t i);
|
||||
|
||||
/* alias of the above */ //TODO: improve
|
||||
#define put_u8 put_8bit
|
||||
#define put_u16le put_16bitLE
|
||||
#define put_u32le put_32bitLE
|
||||
#define put_u16be put_16bitBE
|
||||
#define put_u32be put_32bitBE
|
||||
|
||||
|
||||
/* signed nibbles come up a lot */
|
||||
static int nibble_to_int[16] = {0,1,2,3,4,5,6,7,-8,-7,-6,-5,-4,-3,-2,-1};
|
||||
|
||||
|
|
|
@ -285,14 +285,14 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = {
|
|||
init_vgmstream_mn_str,
|
||||
init_vgmstream_mss,
|
||||
init_vgmstream_ps2_hsf,
|
||||
init_vgmstream_ps3_ivag,
|
||||
init_vgmstream_ivag,
|
||||
init_vgmstream_ps2_2pfs,
|
||||
init_vgmstream_xnb,
|
||||
init_vgmstream_ubi_ckd,
|
||||
init_vgmstream_ps2_vbk,
|
||||
init_vgmstream_otm,
|
||||
init_vgmstream_bcstm,
|
||||
init_vgmstream_idsp_nus3,
|
||||
init_vgmstream_idsp_namco,
|
||||
init_vgmstream_kt_g1l,
|
||||
init_vgmstream_kt_wiibgm,
|
||||
init_vgmstream_ktss,
|
||||
|
@ -405,6 +405,7 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = {
|
|||
init_vgmstream_hd3_bd3,
|
||||
init_vgmstream_bnk_sony,
|
||||
init_vgmstream_nus3bank,
|
||||
init_vgmstream_nus3bank_encrypted,
|
||||
init_vgmstream_scd_sscf,
|
||||
init_vgmstream_dsp_sps_n1,
|
||||
init_vgmstream_dsp_itl_ch,
|
||||
|
@ -513,8 +514,8 @@ static VGMSTREAM * init_vgmstream_internal(STREAMFILE *streamFile) {
|
|||
if (!vgmstream)
|
||||
continue;
|
||||
|
||||
/* fail if there is nothing to play (without this check vgmstream can generate empty files) */
|
||||
if (vgmstream->num_samples <= 0) {
|
||||
/* fail if there is nothing/too much to play (<=0 generates empty files, >N writes GBs of garbage) */
|
||||
if (vgmstream->num_samples <= 0 || vgmstream->num_samples > VGMSTREAM_MAX_NUM_SAMPLES) {
|
||||
VGM_LOG("VGMSTREAM: wrong num_samples %i\n", vgmstream->num_samples);
|
||||
close_vgmstream(vgmstream);
|
||||
continue;
|
||||
|
|
|
@ -12,6 +12,7 @@ enum { VGMSTREAM_MAX_CHANNELS = 64 };
|
|||
enum { VGMSTREAM_MIN_SAMPLE_RATE = 300 }; /* 300 is Wwise min */
|
||||
enum { VGMSTREAM_MAX_SAMPLE_RATE = 192000 }; /* found in some FSB5 */
|
||||
enum { VGMSTREAM_MAX_SUBSONGS = 65535 };
|
||||
enum { VGMSTREAM_MAX_NUM_SAMPLES = 1000000000 }; /* no ~5h vgm hopefully */
|
||||
|
||||
#include "streamfile.h"
|
||||
|
||||
|
@ -582,13 +583,13 @@ typedef enum {
|
|||
meta_MN_STR, /* Mini Ninjas (PC/PS3/WII) */
|
||||
meta_MSS, /* Guerilla: ShellShock Nam '67 (PS2/Xbox), Killzone (PS2) */
|
||||
meta_PS2_HSF, /* Lowrider (PS2) */
|
||||
meta_PS3_IVAG, /* Interleaved VAG files (PS3) */
|
||||
meta_IVAG,
|
||||
meta_PS2_2PFS, /* Konami: Mahoromatic: Moetto - KiraKira Maid-San, GANTZ (PS2) */
|
||||
meta_PS2_VBK, /* Disney's Stitch - Experiment 626 */
|
||||
meta_OTM, /* Otomedius (Arcade) */
|
||||
meta_CSTM, /* Nintendo 3DS CSTM (Century Stream) */
|
||||
meta_FSTM, /* Nintendo Wii U FSTM (caFe? Stream) */
|
||||
meta_IDSP_NUS3, /* Namco 3DS/Wii U IDSP */
|
||||
meta_IDSP_NAMCO,
|
||||
meta_KT_WIIBGM, /* Koei Tecmo WiiBGM */
|
||||
meta_KTSS, /* Koei Tecmo Nintendo Stream (KNS) */
|
||||
meta_MCA, /* Capcom MCA "MADP" */
|
||||
|
|
Loading…
Reference in New Issue