mirror of
https://github.com/vgmstream/vgmstream.git
synced 2025-01-29 19:37:30 +01:00
commit
cd2248c49f
@ -149,7 +149,6 @@ void block_update_ea_schl(off_t block_offset, VGMSTREAM * vgmstream) {
|
||||
for (i = 0; i < vgmstream->channels; i++) {
|
||||
off_t channel_start = read_32bit(block_offset + 0x0C + (0x04*i),streamFile);
|
||||
vgmstream->ch[i].offset = block_offset + 0x0C + (0x04*vgmstream->channels) + channel_start;
|
||||
VGM_LOG("ch=%x, off=%lx\n", i, vgmstream->ch[i].offset);
|
||||
}
|
||||
|
||||
/* read ADPCM history before each channel if needed (not actually read in sx.exe) */
|
||||
|
@ -7,6 +7,8 @@ void block_update_ea_sns(off_t block_offset, VGMSTREAM * vgmstream) {
|
||||
STREAMFILE* streamFile = vgmstream->ch[0].streamfile;
|
||||
uint32_t block_size, block_samples;
|
||||
size_t file_size = get_streamfile_size(streamFile);
|
||||
off_t channel_start;
|
||||
size_t channel_interleave;
|
||||
int i;
|
||||
|
||||
/* always BE */
|
||||
@ -26,9 +28,26 @@ void block_update_ea_sns(off_t block_offset, VGMSTREAM * vgmstream) {
|
||||
* - in SPS: 0x48=header, 0x44=normal block, 0x45=last block (empty) */
|
||||
block_size &= 0x00FFFFFF;
|
||||
|
||||
switch(vgmstream->coding_type) {
|
||||
case coding_NGC_DSP:
|
||||
/* 0x04: unknown (0x00/02), 0x08: some size?, 0x34: null? */
|
||||
channel_start = read_32bitBE(block_offset+0x08+0x00,streamFile);
|
||||
channel_interleave = read_32bitBE(block_offset+0x08+0x0c,streamFile);
|
||||
/* guessed as all known EA DSP only have one block with subheader (maybe changes coefs every block?) */
|
||||
if (channel_start >= 0x40) {
|
||||
dsp_read_coefs_be(vgmstream,streamFile, block_offset+0x08+0x10,0x28);
|
||||
dsp_read_hist_be (vgmstream,streamFile, block_offset+0x08+0x30,0x28);//todo guessed and doesn't fix clicks in full loops
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
channel_start = 0x00;
|
||||
channel_interleave = 0x00;
|
||||
break;
|
||||
}
|
||||
|
||||
for (i = 0; i < vgmstream->channels; i++) {
|
||||
off_t channel_start = 0x00;
|
||||
vgmstream->ch[i].offset = block_offset + 0x08 + channel_start;
|
||||
vgmstream->ch[i].offset = block_offset + 0x08 + channel_start + i*channel_interleave;
|
||||
|
||||
/* also fix first offset (for EALayer3) */
|
||||
if (block_offset == vgmstream->ch[i].channel_start_offset) {
|
||||
|
@ -42,7 +42,7 @@ static STREAMFILE* setup_atx_streamfile(STREAMFILE *streamFile) {
|
||||
size_t filename_len;
|
||||
int i, num_segments = 0;
|
||||
size_t riff_size;
|
||||
VGM_LOG("1\n");
|
||||
|
||||
|
||||
if (read_16bitLE(0x1c,streamFile) != 0) goto fail; /* this must be first segment */
|
||||
if (read_16bitLE(0x1e,streamFile) < 1 || read_16bitLE(0x1e,streamFile) > ATX_MAX_SEGMENTS) goto fail;
|
||||
@ -57,7 +57,7 @@ VGM_LOG("1\n");
|
||||
for (i = 0; i < num_segments; i++) {
|
||||
off_t subfile_offset;
|
||||
size_t subfile_size;
|
||||
VGM_LOG("loop\n");
|
||||
|
||||
filename[filename_len - 5] = ('0'+i+1); /* ghetto digit conversion */
|
||||
new_streamFile = open_stream_name(streamFile, filename);
|
||||
if (!new_streamFile) goto fail;
|
||||
@ -69,7 +69,7 @@ VGM_LOG("loop\n");
|
||||
/* parse block/segment header (other Media.Vision's files use it too) */
|
||||
subfile_offset = read_32bitLE(0x08,segment_streamFiles[i]); /* header size */
|
||||
subfile_size = read_32bitLE(0x14,segment_streamFiles[i]); /* can be 0 in other containers */
|
||||
VGM_LOG("subfile: %lx, %x\n", subfile_offset, subfile_size);
|
||||
|
||||
if (read_16bitLE(0x1c,segment_streamFiles[i]) != i)
|
||||
goto fail; /* segment sequence */
|
||||
/* 0x04: block size (should match subfile_size in .ATX) */
|
||||
|
@ -15,10 +15,15 @@ VGMSTREAM * init_vgmstream_ea_snr_sns(STREAMFILE * streamFile) {
|
||||
if (!check_extensions(streamFile,"snr"))
|
||||
goto fail;
|
||||
|
||||
/* SNR headers normally need an external SNS file, but some have data */
|
||||
/* SNR headers normally need an external SNS file, but some have data [Burnout Paradise, NFL2013 (iOS)] */
|
||||
if (get_streamfile_size(streamFile) > 0x10) {
|
||||
/* SNR with data (flag 0x40 not set), seen in Burnout Paradise, NFL2013 iOS */
|
||||
off_t start_offset = (read_32bitBE(0x08, streamFile) == 0) ? 0x0c : 0x08;
|
||||
off_t start_offset;
|
||||
|
||||
switch(read_8bit(0x04,streamFile)) { /* flags */
|
||||
case 0x60: start_offset = 0x10; break;
|
||||
case 0x20: start_offset = 0x0c; break;
|
||||
default: start_offset = 0x08; break;
|
||||
}
|
||||
|
||||
vgmstream = init_vgmstream_eaaudiocore_header(streamFile, streamFile, 0x00, start_offset, meta_EA_SNR_SNS);
|
||||
if (!vgmstream) goto fail;
|
||||
@ -31,11 +36,11 @@ VGMSTREAM * init_vgmstream_ea_snr_sns(STREAMFILE * streamFile) {
|
||||
if (!vgmstream) goto fail;
|
||||
}
|
||||
|
||||
if (streamData) close_streamfile(streamData);
|
||||
close_streamfile(streamData);
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
if (streamData) close_streamfile(streamData);
|
||||
close_streamfile(streamData);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -119,7 +124,8 @@ static VGMSTREAM * init_vgmstream_eaaudiocore_header(STREAMFILE * streamHead, ST
|
||||
sample_rate = (uint16_t)read_16bitBE(header_offset + 0x02,streamHead);
|
||||
flags = (uint8_t)read_8bit(header_offset + 0x04,streamHead); /* upper nibble only? */
|
||||
num_samples = (uint32_t)read_32bitBE(header_offset + 0x04,streamHead) & 0x00FFFFFF;
|
||||
/* optional, in some headers: 0x08: null? 0x0c: varies (ex. null, full size) */
|
||||
/* rest is optional, depends on flags header used (ex. SNU and SPS may have bigger headers):
|
||||
* &0x20: 1 int (usually 0x00), &0x00/40: nothing, &0x60: 2 ints (usually 0x00 and 0x14) */
|
||||
|
||||
/* V0: SNR+SNS, V1: SPR+SPS (not apparent differences) */
|
||||
if (version != 0 && version != 1) {
|
||||
@ -166,14 +172,14 @@ static VGMSTREAM * init_vgmstream_eaaudiocore_header(STREAMFILE * streamHead, ST
|
||||
/* EA decoder list and known internal FourCCs */
|
||||
switch(codec) {
|
||||
|
||||
case 0x02: /* "P6B0": PCM16BE (NBA Jam Wii) */
|
||||
case 0x02: /* "P6B0": PCM16BE [NBA Jam (Wii)] */
|
||||
vgmstream->coding_type = coding_PCM16_int;
|
||||
vgmstream->codec_endian = 1;
|
||||
vgmstream->layout_type = layout_blocked_ea_sns;
|
||||
break;
|
||||
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
case 0x03: { /* "EXm0": EA-XMA (Dante's Inferno X360) */
|
||||
case 0x03: { /* "EXm0": EA-XMA [Dante's Inferno (X360)] */
|
||||
uint8_t buf[0x100];
|
||||
int bytes, block_size, block_count;
|
||||
size_t stream_size, virtual_size;
|
||||
@ -200,15 +206,15 @@ static VGMSTREAM * init_vgmstream_eaaudiocore_header(STREAMFILE * streamHead, ST
|
||||
}
|
||||
#endif
|
||||
|
||||
case 0x04: /* "Xas1": EA-XAS (Dead Space PC/PS3) */
|
||||
case 0x04: /* "Xas1": EA-XAS [Dead Space (PC/PS3)] */
|
||||
vgmstream->coding_type = coding_EA_XAS;
|
||||
vgmstream->layout_type = layout_blocked_ea_sns;
|
||||
break;
|
||||
|
||||
#ifdef VGM_USE_MPEG
|
||||
case 0x05: /* "EL31": EALayer3 v1 (Need for Speed: Hot Pursuit PS3) */
|
||||
case 0x06: /* "L32P": EALayer3 v2 "PCM" (Battlefield 1943 PS3) */
|
||||
case 0x07: { /* "L32S": EALayer3 v2 "Spike" (Dante's Inferno PS3) */
|
||||
case 0x05: /* "EL31": EALayer3 v1 [Need for Speed: Hot Pursuit (PS3)] */
|
||||
case 0x06: /* "L32P": EALayer3 v2 "PCM" [Battlefield 1943 (PS3)] */
|
||||
case 0x07: { /* "L32S": EALayer3 v2 "Spike" [Dante's Inferno (PS3)] */
|
||||
mpeg_custom_config cfg = {0};
|
||||
off_t mpeg_start_offset = start_offset + 0x08;
|
||||
mpeg_custom_t type = (codec == 0x05 ? MPEG_EAL31b : (codec == 0x06) ? MPEG_EAL32P : MPEG_EAL32S);
|
||||
@ -222,7 +228,13 @@ static VGMSTREAM * init_vgmstream_eaaudiocore_header(STREAMFILE * streamHead, ST
|
||||
}
|
||||
#endif
|
||||
|
||||
#if 0 //todo unknown variation
|
||||
case 0x08: /* "Gca0"?: DSP [Need for Speed: Nitro sfx (Wii)] */
|
||||
vgmstream->coding_type = coding_NGC_DSP;
|
||||
vgmstream->layout_type = layout_blocked_ea_sns;
|
||||
/* DSP coefs are read in the blocks */
|
||||
break;
|
||||
|
||||
#if 0 //todo buffered ATRAC9
|
||||
#ifdef VGM_USE_ATRAC9
|
||||
case 0x0a: { /* EATrax */
|
||||
atrac9_config cfg = {0};
|
||||
@ -241,8 +253,7 @@ static VGMSTREAM * init_vgmstream_eaaudiocore_header(STREAMFILE * streamHead, ST
|
||||
#endif
|
||||
|
||||
case 0x00: /* "NONE" (internal 'codec not set' flag) */
|
||||
case 0x01: /* not used/reserved? Gca0/MP30/P6L0/P2B0/P2L0/P8S0/P8U0/PFN0? */
|
||||
case 0x08: /* ? */
|
||||
case 0x01: /* not used/reserved? /MP30/P6L0/P2B0/P2L0/P8S0/P8U0/PFN0? */
|
||||
case 0x09: /* EASpeex (libspeex variant, base versions vary: 1.0.5, 1.2beta3) */
|
||||
case 0x0b: /* ? */
|
||||
case 0x0c: /* EAOpus (inside each SNS/SPS block is 16b frame size + standard? Opus packet) */
|
||||
|
@ -342,7 +342,6 @@ static VGMSTREAM * init_vgmstream_ea_variable_header(STREAMFILE *streamFile, ea_
|
||||
case EA_CODEC2_MT10: /* MicroTalk (10:1 compression) */
|
||||
case EA_CODEC2_MT5: /* MicroTalk (5:1 compression) */
|
||||
vgmstream->coding_type = coding_EA_MT;
|
||||
VGM_LOG("mt: codec=%x, cv=%x, v=%x\n", ea->codec2, ea->codec_version, ea->version);
|
||||
vgmstream->codec_data = init_ea_mt(vgmstream->channels, ea->version == EA_VERSION_V3);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
break;
|
||||
|
@ -12,12 +12,12 @@ static void scd_ogg_v3_decryption_callback(void *ptr, size_t size, size_t nmemb,
|
||||
VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
char filename[PATH_LIMIT];
|
||||
off_t start_offset, tables_offset, meta_offset, post_meta_offset;
|
||||
off_t start_offset, tables_offset, meta_offset, post_meta_offset, name_offset = 0;
|
||||
int32_t stream_size, subheader_size, loop_start, loop_end;
|
||||
|
||||
int total_subsongs, target_subsong = streamFile->stream_index;
|
||||
int loop_flag = 0, channel_count, codec, sample_rate;
|
||||
int aux_chunk_count;
|
||||
int version, target_entry, aux_chunk_count;
|
||||
int total_subsongs, target_subsong = streamFile->stream_index;
|
||||
|
||||
int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL;
|
||||
int16_t (*read_16bit)(off_t,STREAMFILE*) = NULL;
|
||||
@ -33,24 +33,25 @@ VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE *streamFile) {
|
||||
read_32bitBE(0x04,streamFile) != 0x53534346) /* "SSCF" */
|
||||
goto fail;
|
||||
|
||||
if (read_32bitBE(0x08,streamFile) == 2 || /* version 2 BE, as seen in FFXIII demo for PS3 */
|
||||
read_32bitBE(0x08,streamFile) == 3) { /* version 3 BE, as seen in FFXIII for PS3 */
|
||||
if (read_8bit(0x0c,streamFile) == 0x01) { /* big endian flag */
|
||||
//size_offset = 0x14;
|
||||
read_32bit = read_32bitBE;
|
||||
read_16bit = read_16bitBE;
|
||||
}
|
||||
else if (read_32bitLE(0x08,streamFile) == 2 || /* version 2/3 LE, as seen in FFXIV for PC (and others) */
|
||||
read_32bitLE(0x08,streamFile) == 3) {
|
||||
} else {
|
||||
//size_offset = 0x10;
|
||||
read_32bit = read_32bitLE;
|
||||
read_16bit = read_16bitLE;
|
||||
}
|
||||
else {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* 0x0c: probably 0=LE, 1=BE */
|
||||
/* 0x0d: unknown (always 0x04) */
|
||||
/* SSCF version? (older SSCFs from Crisis Core/FFXI X360 seem to be V3/2) */
|
||||
if (read_8bit(0x0d,streamFile) != 0x04)
|
||||
goto fail;
|
||||
|
||||
/* v2: FFXIII demo (PS3), FFT0 test files (PC); v3: common; v4: Kingdom Hearts 2.8 (PS4) */
|
||||
version = read_32bit(0x08,streamFile);
|
||||
if (version != 2 && version != 3 && version != 4)
|
||||
goto fail;
|
||||
|
||||
tables_offset = read_16bit(0x0e,streamFile); /* usually 0x30 or 0x20 */
|
||||
|
||||
#if 0
|
||||
@ -74,7 +75,7 @@ VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE *streamFile) {
|
||||
/* 0x14: always null? */
|
||||
/* 0x18: table5? (unknown) start offset? */
|
||||
/* 0x1c: unknown, often null */
|
||||
/* each table entry is an uint32_t offset */
|
||||
/* each table entry is an uint32_t offset; after entries there is padding */
|
||||
/* if a table isn't present entries is 0 and offset points to next table */
|
||||
|
||||
/* find meta_offset in table3 (headers) and total subsongs */
|
||||
@ -89,14 +90,16 @@ VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE *streamFile) {
|
||||
|
||||
/* manually find subsongs as entries can be dummy (ex. sfx banks in FF XIV or FF Type-0) */
|
||||
for (i = 0; i < headers_entries; i++) {
|
||||
off_t header_offset = read_32bit(headers_offset + i*0x04,streamFile);
|
||||
off_t entry_offset = read_32bit(headers_offset + i*0x04,streamFile);
|
||||
|
||||
if (read_32bit(header_offset+0x0c,streamFile) == -1)
|
||||
if (read_32bit(entry_offset+0x0c,streamFile) == -1)
|
||||
continue; /* codec -1 when dummy */
|
||||
|
||||
total_subsongs++;
|
||||
if (!meta_offset && total_subsongs == target_subsong)
|
||||
meta_offset = header_offset;
|
||||
if (!meta_offset && total_subsongs == target_subsong) {
|
||||
meta_offset = entry_offset;
|
||||
target_entry = i;
|
||||
}
|
||||
}
|
||||
if (meta_offset == 0) goto fail;
|
||||
/* SCD can contain 0 entries too */
|
||||
@ -129,10 +132,24 @@ VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE *streamFile) {
|
||||
post_meta_offset += read_32bit(post_meta_offset+0x04, streamFile);
|
||||
}
|
||||
|
||||
/* find name if possible */
|
||||
if (version == 4) {
|
||||
int info_entries = read_16bit(tables_offset+0x00,streamFile);
|
||||
int headers_entries = read_16bit(tables_offset+0x04,streamFile);
|
||||
off_t info_offset = tables_offset+0x20;
|
||||
|
||||
/* not very exact as table1 and table3 entries may differ in V3, not sure about V4 */
|
||||
if (info_entries == headers_entries) {
|
||||
off_t entry_offset = read_16bit(info_offset + 0x04*target_entry,streamFile);
|
||||
name_offset = entry_offset+0x30;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#ifdef VGM_USE_VORBIS
|
||||
/* special case using init_vgmstream_ogg_vorbis */
|
||||
if (codec == 0x06) {
|
||||
VGMSTREAM *ogg_vgmstream;
|
||||
uint8_t ogg_version, ogg_byte;
|
||||
vgm_vorbis_info_t inf = {0};
|
||||
|
||||
@ -176,7 +193,10 @@ VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE *streamFile) {
|
||||
}
|
||||
|
||||
/* actual Ogg init */
|
||||
return init_vgmstream_ogg_vorbis_callbacks(streamFile, filename, NULL, start_offset, &inf);
|
||||
ogg_vgmstream = init_vgmstream_ogg_vorbis_callbacks(streamFile, filename, NULL, start_offset, &inf);
|
||||
if (ogg_vgmstream && name_offset)
|
||||
read_string(ogg_vgmstream->stream_name, PATH_LIMIT, name_offset, streamFile);
|
||||
return ogg_vgmstream;
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -189,6 +209,8 @@ VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE *streamFile) {
|
||||
vgmstream->num_streams = total_subsongs;
|
||||
vgmstream->stream_size = stream_size;
|
||||
vgmstream->meta_type = meta_SQEX_SCD;
|
||||
if (name_offset)
|
||||
read_string(vgmstream->stream_name, PATH_LIMIT, name_offset, streamFile);
|
||||
|
||||
switch (codec) {
|
||||
case 0x01: /* PCM */
|
||||
@ -319,7 +341,7 @@ VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE *streamFile) {
|
||||
}
|
||||
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
case 0x0B: { /* XMA2 [Final Fantasy (X360), Lightning Returns (X360) sfx] */
|
||||
case 0x0B: { /* XMA2 [Final Fantasy (X360), Lightning Returns (X360) sfx, Kingdom Hearts 2.8 (X1)] */
|
||||
ffmpeg_codec_data *ffmpeg_data = NULL;
|
||||
uint8_t buf[200];
|
||||
int32_t bytes;
|
||||
@ -373,6 +395,27 @@ VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE *streamFile) {
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef VGM_USE_ATRAC9
|
||||
case 0x16: { /* ATRAC9 [Kingdom Hearts 2.8 (PS4)] */
|
||||
atrac9_config cfg = {0};
|
||||
|
||||
/* post header has various typical ATRAC9 values */
|
||||
cfg.channels = vgmstream->channels;
|
||||
cfg.config_data = read_32bit(post_meta_offset+0x0c,streamFile);
|
||||
cfg.encoder_delay = read_32bit(post_meta_offset+0x18,streamFile);
|
||||
|
||||
vgmstream->codec_data = init_atrac9(&cfg);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->coding_type = coding_ATRAC9;
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
||||
vgmstream->num_samples = read_32bit(post_meta_offset+0x10,streamFile); /* loop values above are also weird and ignored */
|
||||
vgmstream->loop_start_sample = read_32bit(post_meta_offset+0x20, streamFile) - (loop_flag ? cfg.encoder_delay : 0); //loop_start
|
||||
vgmstream->loop_end_sample = read_32bit(post_meta_offset+0x24, streamFile) - (loop_flag ? cfg.encoder_delay : 0); //loop_end
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
case -1: /* used for dummy entries */
|
||||
default:
|
||||
VGM_LOG("SCD: unknown codec 0x%x\n", codec);
|
||||
|
@ -2,13 +2,14 @@
|
||||
#include "../coding/coding.h"
|
||||
|
||||
|
||||
static STREAMFILE* setup_sead_hca_streamfile(STREAMFILE *streamFile, off_t subfile_offset, size_t subfile_size);
|
||||
static STREAMFILE* setup_sead_hca_streamfile(STREAMFILE *streamFile, off_t subfile_offset, size_t subfile_size, int encryption, size_t header_size, size_t key_start);
|
||||
|
||||
/* SABF/MABF - Square Enix's "Sead" audio games [Dragon Quest Builders (PS3), Dissidia Opera Omnia (mobile), FF XV (PS4)] */
|
||||
/* SABF/MABF - Square Enix's "sead" audio games [Dragon Quest Builders (PS3), Dissidia Opera Omnia (mobile), FF XV (PS4)] */
|
||||
VGMSTREAM * init_vgmstream_sqex_sead(STREAMFILE * streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
off_t start_offset, mtrl_offset, meta_offset, post_meta_offset; //, info_offset, name_offset = 0;
|
||||
size_t stream_size, subheader_size; //, name_size = 0;
|
||||
off_t start_offset, tables_offset, mtrl_offset, meta_offset, post_meta_offset; //, info_offset, name_offset = 0;
|
||||
size_t stream_size, descriptor_size, subheader_size, special_size; //, name_size = 0;
|
||||
|
||||
|
||||
int loop_flag = 0, channel_count, codec, sample_rate, loop_start, loop_end;
|
||||
int is_sab = 0, is_mab = 0;
|
||||
@ -36,38 +37,43 @@ VGMSTREAM * init_vgmstream_sqex_sead(STREAMFILE * streamFile) {
|
||||
// goto fail;
|
||||
/* 0x04(1): version? (usually 0x02, rarely 0x01, ex FF XV title) */
|
||||
/* 0x05(1): 0x00/01? */
|
||||
/* 0x06(2): chunk size? (usually 0x10, rarely 0x20) */
|
||||
if (read_16bitBE(0x06,streamFile) < 0x100) { /* use size as no apparent flag */
|
||||
/* 0x06(2): version? (usually 0x10, rarely 0x20) */
|
||||
if (read_16bitBE(0x06,streamFile) < 0x100) { /* use some value as no apparent flag */
|
||||
read_32bit = read_32bitBE;
|
||||
read_16bit = read_16bitBE;
|
||||
} else {
|
||||
read_32bit = read_32bitLE;
|
||||
read_16bit = read_16bitLE;
|
||||
}
|
||||
/* 0x08(1): ?, 0x09(1): ?, 0x0a(2): ? */
|
||||
/* 0x08(1): version 0x04?, 0x0a(2): ? */
|
||||
descriptor_size = read_8bit(0x09,streamFile);
|
||||
|
||||
if (read_32bit(0x0c,streamFile) != get_streamfile_size(streamFile))
|
||||
goto fail;
|
||||
/* 0x10(10): file descriptor ("BGM", "Music", "SE", etc) */
|
||||
/* 0x10(n): file descriptor ("BGM", "Music", "SE", etc, long names are ok), padded */
|
||||
tables_offset = 0x10 + (descriptor_size + 0x01); /* string null seems counted for padding */
|
||||
if (tables_offset % 0x10)
|
||||
tables_offset += 0x10 - (tables_offset % 0x10);
|
||||
|
||||
|
||||
/** offset tables **/
|
||||
if (is_sab) {
|
||||
if (read_32bitBE(0x20,streamFile) != 0x736E6420) goto fail; /* "snd " (info) */
|
||||
if (read_32bitBE(0x30,streamFile) != 0x73657120) goto fail; /* "seq " (unknown) */
|
||||
if (read_32bitBE(0x40,streamFile) != 0x74726B20) goto fail; /* "trk " (unknown) */
|
||||
if (read_32bitBE(0x50,streamFile) != 0x6D74726C) goto fail; /* "mtrl" (headers/streams) */
|
||||
//info_offset = read_32bit(0x28,streamFile);
|
||||
//seq_offset = read_32bit(0x38,streamFile);
|
||||
//trk_offset = read_32bit(0x48,streamFile);
|
||||
mtrl_offset = read_32bit(0x58,streamFile);
|
||||
if (read_32bitBE(tables_offset+0x00,streamFile) != 0x736E6420) goto fail; /* "snd " (info) */
|
||||
if (read_32bitBE(tables_offset+0x10,streamFile) != 0x73657120) goto fail; /* "seq " (unknown) */
|
||||
if (read_32bitBE(tables_offset+0x20,streamFile) != 0x74726B20) goto fail; /* "trk " (unknown) */
|
||||
if (read_32bitBE(tables_offset+0x30,streamFile) != 0x6D74726C) goto fail; /* "mtrl" (headers/streams) */
|
||||
//info_offset = read_32bit(tables_offset+0x08,streamFile);
|
||||
//seq_offset = read_32bit(tables_offset+0x18,streamFile);
|
||||
//trk_offset = read_32bit(tables_offset+0x28,streamFile);
|
||||
mtrl_offset = read_32bit(tables_offset+0x38,streamFile);
|
||||
}
|
||||
else if (is_mab) {
|
||||
if (read_32bitBE(0x20,streamFile) != 0x6D757363) goto fail; /* "musc" (info) */
|
||||
if (read_32bitBE(0x30,streamFile) != 0x696E7374) goto fail; /* "inst" (unknown) */
|
||||
if (read_32bitBE(0x40,streamFile) != 0x6D74726C) goto fail; /* "mtrl" (headers/streams) */
|
||||
//info_offset = read_32bit(0x28,streamFile);
|
||||
//inst_offset = read_32bit(0x38,streamFile);
|
||||
mtrl_offset = read_32bit(0x48,streamFile);
|
||||
if (read_32bitBE(tables_offset+0x00,streamFile) != 0x6D757363) goto fail; /* "musc" (info) */
|
||||
if (read_32bitBE(tables_offset+0x10,streamFile) != 0x696E7374) goto fail; /* "inst" (unknown) */
|
||||
if (read_32bitBE(tables_offset+0x20,streamFile) != 0x6D74726C) goto fail; /* "mtrl" (headers/streams) */
|
||||
//info_offset = read_32bit(tables_offset+0x08,streamFile);
|
||||
//inst_offset = read_32bit(tables_offset+0x18,streamFile);
|
||||
mtrl_offset = read_32bit(tables_offset+0x28,streamFile);
|
||||
}
|
||||
else {
|
||||
goto fail;
|
||||
@ -114,7 +120,7 @@ VGMSTREAM * init_vgmstream_sqex_sead(STREAMFILE * streamFile) {
|
||||
loop_end = read_32bit(meta_offset+0x10,streamFile);
|
||||
subheader_size = read_32bit(meta_offset+0x14,streamFile); /* including subfile header */
|
||||
stream_size = read_32bit(meta_offset+0x18,streamFile); /* not including subfile header */
|
||||
/* 0x1c: null? */
|
||||
special_size = read_32bit(meta_offset+0x1c,streamFile);
|
||||
|
||||
loop_flag = (loop_end > 0);
|
||||
post_meta_offset = meta_offset + 0x20;
|
||||
@ -122,7 +128,7 @@ VGMSTREAM * init_vgmstream_sqex_sead(STREAMFILE * streamFile) {
|
||||
|
||||
/** info section (get stream name) **/
|
||||
//if (is_sab) { //todo load name based on entry id
|
||||
/* "snd ": unknown flags/sizes and name */
|
||||
/* "snd " */
|
||||
/* 0x08(2): file number within descriptor */
|
||||
/* 0x1a(2): base_entry size (-0x10?) */
|
||||
//name_size = read_32bit(snd_offset+0x20,streamFile);
|
||||
@ -130,7 +136,11 @@ VGMSTREAM * init_vgmstream_sqex_sead(STREAMFILE * streamFile) {
|
||||
/* 0x24(4): unique id? (referenced in "seq" section) */
|
||||
//}
|
||||
//else if (is_mab) {
|
||||
/* "musc": unknown flags sizes and names, another format */
|
||||
/* "musc" */
|
||||
//looks like a "music cue" section, pointing to one subsection per "material".
|
||||
// ex. one cue may point to 3 named subsongs/sections.
|
||||
// some common header info from all materials is repeated (ex. sample rate), while other
|
||||
// (loops, maybe proper num_samples) are listed per material but don't always match thei header
|
||||
//}
|
||||
|
||||
|
||||
@ -161,6 +171,37 @@ VGMSTREAM * init_vgmstream_sqex_sead(STREAMFILE * streamFile) {
|
||||
break;
|
||||
}
|
||||
|
||||
#ifdef VGM_USE_VORBIS
|
||||
case 0x03: { /* OGG [Final Fantasy XV Benchmark sfx (PC)] */
|
||||
VGMSTREAM *ogg_vgmstream = NULL;
|
||||
vgm_vorbis_info_t inf = {0};
|
||||
off_t subfile_offset = post_meta_offset + subheader_size;
|
||||
char filename[PATH_LIMIT];
|
||||
|
||||
streamFile->get_name(streamFile,filename,sizeof(filename));
|
||||
|
||||
inf.layout_type = layout_ogg_vorbis;
|
||||
inf.meta_type = vgmstream->meta_type;
|
||||
inf.total_subsongs = total_subsongs;
|
||||
inf.stream_size = stream_size;
|
||||
/* post header has some kind of repeated values, config/table? */
|
||||
|
||||
ogg_vgmstream = init_vgmstream_ogg_vorbis_callbacks(streamFile, filename, NULL, subfile_offset, &inf);
|
||||
if (ogg_vgmstream) {
|
||||
ogg_vgmstream->num_streams = vgmstream->num_streams;
|
||||
ogg_vgmstream->stream_size = vgmstream->stream_size;
|
||||
|
||||
close_vgmstream(vgmstream);
|
||||
return ogg_vgmstream;
|
||||
}
|
||||
else {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef VGM_USE_ATRAC9
|
||||
case 0x04: { /* ATRAC9 [Dragon Quest Builders (Vita), Final Fantaxy XV (PS4)] */
|
||||
atrac9_config cfg = {0};
|
||||
@ -170,12 +211,12 @@ VGMSTREAM * init_vgmstream_sqex_sead(STREAMFILE * streamFile) {
|
||||
cfg.channels = vgmstream->channels;
|
||||
cfg.config_data = read_32bit(post_meta_offset+0x0c,streamFile);
|
||||
cfg.encoder_delay = read_32bit(post_meta_offset+0x18,streamFile);
|
||||
VGM_LOG("1\n");
|
||||
|
||||
vgmstream->codec_data = init_atrac9(&cfg);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->coding_type = coding_ATRAC9;
|
||||
vgmstream->layout_type = layout_none;
|
||||
VGM_LOG("2\n");
|
||||
|
||||
vgmstream->sample_rate = read_32bit(post_meta_offset+0x1c,streamFile); /* SAB's sample rate can be different but it's ignored */
|
||||
vgmstream->num_samples = read_32bit(post_meta_offset+0x10,streamFile); /* loop values above are also weird and ignored */
|
||||
vgmstream->loop_start_sample = read_32bit(post_meta_offset+0x20, streamFile) - (loop_flag ? cfg.encoder_delay : 0); //loop_start
|
||||
@ -185,7 +226,7 @@ VGM_LOG("2\n");
|
||||
#endif
|
||||
|
||||
#ifdef VGM_USE_MPEG
|
||||
case 0x06: { /* MSF subfile (MPEG mode) [Dragon Quest Builders (PS3)] */
|
||||
case 0x06: { /* MSF subfile (MPEG mode) [Dragon Quest Builders (PS3)] */
|
||||
mpeg_codec_data *mpeg_data = NULL;
|
||||
mpeg_custom_config cfg = {0};
|
||||
|
||||
@ -210,10 +251,14 @@ VGM_LOG("2\n");
|
||||
STREAMFILE *temp_streamFile = NULL;
|
||||
off_t subfile_offset = post_meta_offset + 0x10;
|
||||
size_t subfile_size = stream_size + subheader_size - 0x10;
|
||||
/* post header has 0x10 unknown + HCA header */
|
||||
|
||||
/* post header: values from the HCA header, in file endianness + HCA header */
|
||||
size_t key_start = special_size & 0xff;
|
||||
size_t header_size = read_16bit(post_meta_offset+0x02, streamFile);
|
||||
int encryption = read_16bit(post_meta_offset+0x0c, streamFile); //maybe 8bit?
|
||||
/* encryption type 0x01 found in Final Fantasy XII TZA (PS4/PC) */
|
||||
|
||||
temp_streamFile = setup_sead_hca_streamfile(streamFile, subfile_offset, subfile_size);
|
||||
temp_streamFile = setup_sead_hca_streamfile(streamFile, subfile_offset, subfile_size, encryption, header_size, key_start);
|
||||
if (!temp_streamFile) goto fail;
|
||||
|
||||
temp_vgmstream = init_vgmstream_hca(temp_streamFile);
|
||||
@ -249,7 +294,49 @@ fail:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static STREAMFILE* setup_sead_hca_streamfile(STREAMFILE *streamFile, off_t subfile_offset, size_t subfile_size) {
|
||||
|
||||
typedef struct {
|
||||
size_t header_size;
|
||||
size_t key_start;
|
||||
} sead_decryption_data;
|
||||
|
||||
/* Encrypted HCA */
|
||||
static size_t sead_decryption_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length, sead_decryption_data* data) {
|
||||
/* Found in FFXII_TZA.exe (same key in SCD Ogg V3) */
|
||||
static const uint8_t encryption_key[0x100] = {
|
||||
0x3A, 0x32, 0x32, 0x32, 0x03, 0x7E, 0x12, 0xF7, 0xB2, 0xE2, 0xA2, 0x67, 0x32, 0x32, 0x22, 0x32, // 00-0F
|
||||
0x32, 0x52, 0x16, 0x1B, 0x3C, 0xA1, 0x54, 0x7B, 0x1B, 0x97, 0xA6, 0x93, 0x1A, 0x4B, 0xAA, 0xA6, // 10-1F
|
||||
0x7A, 0x7B, 0x1B, 0x97, 0xA6, 0xF7, 0x02, 0xBB, 0xAA, 0xA6, 0xBB, 0xF7, 0x2A, 0x51, 0xBE, 0x03, // 20-2F
|
||||
0xF4, 0x2A, 0x51, 0xBE, 0x03, 0xF4, 0x2A, 0x51, 0xBE, 0x12, 0x06, 0x56, 0x27, 0x32, 0x32, 0x36, // 30-3F
|
||||
0x32, 0xB2, 0x1A, 0x3B, 0xBC, 0x91, 0xD4, 0x7B, 0x58, 0xFC, 0x0B, 0x55, 0x2A, 0x15, 0xBC, 0x40, // 40-4F
|
||||
0x92, 0x0B, 0x5B, 0x7C, 0x0A, 0x95, 0x12, 0x35, 0xB8, 0x63, 0xD2, 0x0B, 0x3B, 0xF0, 0xC7, 0x14, // 50-5F
|
||||
0x51, 0x5C, 0x94, 0x86, 0x94, 0x59, 0x5C, 0xFC, 0x1B, 0x17, 0x3A, 0x3F, 0x6B, 0x37, 0x32, 0x32, // 60-6F
|
||||
0x30, 0x32, 0x72, 0x7A, 0x13, 0xB7, 0x26, 0x60, 0x7A, 0x13, 0xB7, 0x26, 0x50, 0xBA, 0x13, 0xB4, // 70-7F
|
||||
0x2A, 0x50, 0xBA, 0x13, 0xB5, 0x2E, 0x40, 0xFA, 0x13, 0x95, 0xAE, 0x40, 0x38, 0x18, 0x9A, 0x92, // 80-8F
|
||||
0xB0, 0x38, 0x00, 0xFA, 0x12, 0xB1, 0x7E, 0x00, 0xDB, 0x96, 0xA1, 0x7C, 0x08, 0xDB, 0x9A, 0x91, // 90-9F
|
||||
0xBC, 0x08, 0xD8, 0x1A, 0x86, 0xE2, 0x70, 0x39, 0x1F, 0x86, 0xE0, 0x78, 0x7E, 0x03, 0xE7, 0x64, // A0-AF
|
||||
0x51, 0x9C, 0x8F, 0x34, 0x6F, 0x4E, 0x41, 0xFC, 0x0B, 0xD5, 0xAE, 0x41, 0xFC, 0x0B, 0xD5, 0xAE, // B0-BF
|
||||
0x41, 0xFC, 0x3B, 0x70, 0x71, 0x64, 0x33, 0x32, 0x12, 0x32, 0x32, 0x36, 0x70, 0x34, 0x2B, 0x56, // C0-CF
|
||||
0x22, 0x70, 0x3A, 0x13, 0xB7, 0x26, 0x60, 0xBA, 0x1B, 0x94, 0xAA, 0x40, 0x38, 0x00, 0xFA, 0xB2, // D0-DF
|
||||
0xE2, 0xA2, 0x67, 0x32, 0x32, 0x12, 0x32, 0xB2, 0x32, 0x32, 0x32, 0x32, 0x75, 0xA3, 0x26, 0x7B, // E0-EF
|
||||
0x83, 0x26, 0xF9, 0x83, 0x2E, 0xFF, 0xE3, 0x16, 0x7D, 0xC0, 0x1E, 0x63, 0x21, 0x07, 0xE3, 0x01, // F0-FF
|
||||
};
|
||||
size_t bytes_read;
|
||||
int i;
|
||||
|
||||
bytes_read = streamfile->read(streamfile, dest, offset, length);
|
||||
|
||||
/* decrypt data (xor) */
|
||||
if (offset >= data->header_size) {
|
||||
for (i = 0; i < bytes_read; i++) {
|
||||
dest[i] ^= encryption_key[(data->key_start + (offset - data->header_size) + i) % 0x100];
|
||||
}
|
||||
}
|
||||
|
||||
return bytes_read;
|
||||
}
|
||||
|
||||
static STREAMFILE* setup_sead_hca_streamfile(STREAMFILE *streamFile, off_t subfile_offset, size_t subfile_size, int encryption, size_t header_size, size_t key_start) {
|
||||
STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL;
|
||||
|
||||
/* setup subfile */
|
||||
@ -261,6 +348,18 @@ static STREAMFILE* setup_sead_hca_streamfile(STREAMFILE *streamFile, off_t subfi
|
||||
if (!new_streamFile) goto fail;
|
||||
temp_streamFile = new_streamFile;
|
||||
|
||||
if (encryption) {
|
||||
sead_decryption_data io_data = {0};
|
||||
size_t io_data_size = sizeof(sead_decryption_data);
|
||||
|
||||
io_data.header_size = header_size;
|
||||
io_data.key_start = key_start;
|
||||
|
||||
new_streamFile = open_io_streamfile(temp_streamFile, &io_data,io_data_size, sead_decryption_read);
|
||||
if (!new_streamFile) goto fail;
|
||||
temp_streamFile = new_streamFile;
|
||||
}
|
||||
|
||||
new_streamFile = open_fakename_streamfile(temp_streamFile, NULL,"hca");
|
||||
if (!new_streamFile) goto fail;
|
||||
temp_streamFile = new_streamFile;
|
||||
|
@ -798,7 +798,6 @@ static void get_xsb_name(char * buf, size_t maxsize, int target_stream, xwb_head
|
||||
/* get name offset */
|
||||
for (i = start_sound; i < xsb.xsb_sounds_count; i++) {
|
||||
xsb_sound *s = &(xsb.xsb_sounds[i]);
|
||||
VGM_LOG("wa=%i, sel=%i, si=%i vs ti=%i\n", s->wavebank, cfg__selected_wavebank, s->stream_index, target_stream);
|
||||
if (s->wavebank == cfg__selected_wavebank-1
|
||||
&& s->stream_index == target_stream-1){
|
||||
name_offset = s->name_offset;
|
||||
|
Loading…
x
Reference in New Issue
Block a user