Merge pull request #587 from bnnm/mul-ubisb-etc

mul ubisb etc
This commit is contained in:
bnnm 2020-04-05 22:44:37 +02:00 committed by GitHub
commit 994ef88409
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
35 changed files with 1069 additions and 356 deletions

View File

@ -89,7 +89,9 @@ static ao_option *device_options = NULL;
static ao_sample_format current_sample_format;
static sample_t *buffer = NULL;
static int buffer_size_kb = 16;
/* reportedly 1kb helps Raspberry Pi Zero play FFmpeg formats without stuttering
* (presumably other low powered devices too), plus it's the default in other plugins */
static int buffer_size_kb = 1;
static int repeat = 0;
static int verbose = 0;

View File

@ -139,10 +139,14 @@ as explained below, but often will use default values. Accepted codec strings:
# - PCM4_U PCM 4-bit unsigned
# * Variation with modified encoding
# - OKI16 OKI ADPCM with 16-bit output (not VOX/Dialogic 12-bit)
# * For few PS2 games (Sweet Legacy, Hooligan)
# * For rare PS2 games (Sweet Legacy, Hooligan)
# - AAC Advanced Audio Coding (raw without .mp4)
# * For some 3DS games and many iOS games
# * Should set skip_samples (around 1024 but varies)
# - TGC Tiger Game.com 4-bit ADPCM
# * For Tiger Game.com
# - ASF Argonaut ASF ADPCM
# * For rare Argonaut games [Croc (SAT)]
codec = (codec string)
```
@ -679,7 +683,7 @@ sample_rate = @0x04 #reads at 0x1204
## Examples
**Colin McRae DiRT (PC) .wip.txth**
#### Colin McRae DiRT (PC) .wip.txth
```
id_value = 0x00000000 #check that value at 0x00 is really 0x00000000
id_offset = @0x00:BE
@ -693,7 +697,7 @@ loop_start_sample = 0
loop_end_sample = data_size
```
**Kim Possible: What's the Switch (PS2) .str.txth**
#### Kim Possible: What's the Switch (PS2) .str.txth
```
codec = PSX
interleave = 0x2000
@ -703,7 +707,7 @@ num_samples = data_size
interleave_last = auto
```
**Manhunt (Xbox) .rib.txth**
#### Manhunt (Xbox) .rib.txth
```
codec = XBOX
codec_mode = 1 #interleaved XBOX
@ -715,7 +719,7 @@ start_offset = 0x00
num_samples = data_size
```
**Pitfall The Lost Expedition (PC) .txth**
#### Pitfall The Lost Expedition (PC) .txth
```
codec = DVI_IMA
interleave = 0x80
@ -725,7 +729,7 @@ sample_rate = 44100
num_samples = data_size
```
**Spy Hunter (GC) .pcm.txth**
#### Spy Hunter (GC) .pcm.txth
```
codec = PCM8
sample_rate = 32000
@ -734,7 +738,7 @@ start_offset = 0
num_samples = data_size
```
**Ultimate Board Game Collection (Wii) .dsp.txth**
#### Ultimate Board Game Collection (Wii) .dsp.txth
```
codec = NGC_DSP
interleave = 0x10000
@ -753,7 +757,8 @@ coef_offset = 0x1c
coef_spacing = 0x10000
coef_endianness = BE
```
**Aladdin in Nasira's Revenge (PS1) .cvs.txth**
#### Aladdin in Nasira's Revenge (PS1) .cvs.txth
```
codec = PSX
interleave = 0x10
@ -763,7 +768,7 @@ padding_size = auto-empty
num_samples = data_size
```
**Shikigami no Shiro - Nanayozuki Gensoukyoku (PS2) bgm.txth**
#### Shikigami no Shiro - Nanayozuki Gensoukyoku (PS2) bgm.txth
```
codec = PSX
interleave = 0x1000
@ -790,7 +795,7 @@ loop_end_sample = @0x10 * channels
data_size = @0x08 * channels #for bitrate
```
**Dragon Poker (Mobile) .snd.txth**
#### Dragon Poker (Mobile) .snd.txth
```
# parse MP3 inside the .snd
subfile_extension = mp3
@ -805,7 +810,7 @@ loop_start_sample = 0
loop_end_sample = data_size
```
**Simple 2000 Series Vol. 120 - The Saigo no Nihonhei (PS2) .xag.txth**
#### Simple 2000 Series Vol. 120 - The Saigo no Nihonhei (PS2) .xag.txth
```
header_file = TSNDDRVC.IRX
@ -853,15 +858,14 @@ subfile_extension = seb
subfile_size = ((@0x04 - @0x00) & 0xFFFFF) * 0x800
```
**Zack & Wiki (Wii) .ssd.txth**
#### Zack & Wiki (Wii) .ssd.txth
```
header_file = bgm_S01.srt
name_table = .names.txt
base_offset = @0x0c:BE
base_offset = base_offset + @0x08:BE + name_value
base_offset = base_offset + @0x00:BE
base_offset = base_offset + @0x00:BE - name_value
codec = NGC_DSP
channels = 2
@ -891,7 +895,7 @@ st_s01_02c.ssd: 7*0x04
```
**Zack & Wiki (Wii) st_s01_00a.txth**
#### Zack & Wiki (Wii) st_s01_00a.txth
```
#alt from above with untouched folders
header_file = Sound/BGM/bgm_S01.srt
@ -900,7 +904,7 @@ name_table = .names.txt
base_offset = @0x0c:BE
base_offset = base_offset + @0x08:BE + name_value
base_offset = base_offset + @0x00:BE
base_offset = base_offset + @0x00:BE - name_value
codec = NGC_DSP
channels = 2
@ -929,3 +933,11 @@ coef_endianness = BE
*snd/stream/st_s01_02c.ssd: 7*0x04
# uses wildcards for full paths from plugins
```
#### Croc (SAT) .asf.txth
```
codec = ASF
sample_rate = 22050
channels = 2
num_samples = data_size
```

View File

@ -15,6 +15,8 @@ libvgmstream_la_LIBADD = -lm
EXTRA_DIST = (auto-updated)
EXTRA_DIST += ../ext_includes/clHCA.h
AM_CFLAGS += -DVGM_USE_G7221
if HAVE_VORBIS
if HAVE_VORBISFILE
AM_CFLAGS += -DVGM_USE_VORBIS

View File

@ -10,6 +10,8 @@ libcoding_la_LDFLAGS =
libcoding_la_SOURCES = (auto-updated)
EXTRA_DIST = (auto-updated)
AM_CFLAGS += -DVGM_USE_G7221
if HAVE_VORBIS
if HAVE_VORBISFILE
AM_CFLAGS += -DVGM_USE_VORBIS

View File

@ -54,3 +54,8 @@ void decode_asf(VGMSTREAMCHANNEL *stream, sample_t *outbuf, int channelspacing,
stream->adpcm_history1_32 = hist1;
stream->adpcm_history2_32 = hist2;
}
int32_t asf_bytes_to_samples(size_t bytes, int channels) {
if (channels <= 0) return 0;
return bytes / channels / 0x11 * 32;
}

View File

@ -35,7 +35,7 @@ void decode_apple_ima4(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channel
void decode_fsb_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel);
void decode_wwise_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel);
void decode_awc_ima(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
void decode_ubi_ima(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel);
void decode_ubi_ima(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, int codec_config);
void decode_h4m_ima(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, uint16_t frame_format);
size_t ima_bytes_to_samples(size_t bytes, int channels);
size_t ms_ima_bytes_to_samples(size_t bytes, int block_align, int channels);
@ -167,6 +167,7 @@ void decode_fadpcm(VGMSTREAMCHANNEL *stream, sample_t *outbuf, int channelspacin
/* asf_decoder */
void decode_asf(VGMSTREAMCHANNEL *stream, sample_t *outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
int32_t asf_bytes_to_samples(size_t bytes, int channels);
/* dsa_decoder */
void decode_dsa(VGMSTREAMCHANNEL *stream, sample_t *outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);

View File

@ -1009,8 +1009,9 @@ void decode_awc_ima(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspa
/* DVI stereo/mono with some mini header and sample output */
void decode_ubi_ima(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) {
void decode_ubi_ima(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, int codec_config) {
int i, sample_count = 0;
int has_header = (codec_config & 0x80) == 0;
int32_t hist1 = stream->adpcm_history1_32;
int step_index = stream->adpcm_step_index;
@ -1018,7 +1019,7 @@ void decode_ubi_ima(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspa
//internal interleave
//header in the beginning of the stream
if (stream->channel_start_offset == stream->offset) {
if (has_header && stream->channel_start_offset == stream->offset) {
int version, big_endian, header_samples, max_samples_to_do;
int16_t (*read_16bit)(off_t,STREAMFILE*) = NULL;
off_t offset = stream->offset;
@ -1051,8 +1052,12 @@ void decode_ubi_ima(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspa
}
}
if (has_header) {
first_sample -= 10; //todo fix hack (needed to adjust nibble offset below)
}
if (step_index < 0) step_index=0;
if (step_index > 88) step_index=88;
for (i = first_sample; i < first_sample + samples_to_do; i++, sample_count += channelspacing) {
off_t byte_offset = channelspacing == 1 ?

View File

@ -453,6 +453,7 @@ static const char* extension_list[] = {
"snr",
"sns",
"snu",
"snz", //txth/reserved [Killzone HD (PS3)]
"sod",
"son",
"spd",
@ -726,7 +727,6 @@ static const coding_info coding_info_list[] = {
{coding_REF_IMA, "Reflections 4-bit IMA ADPCM"},
{coding_AWC_IMA, "Rockstar AWC 4-bit IMA ADPCM"},
{coding_UBI_IMA, "Ubisoft 4-bit IMA ADPCM"},
{coding_H4M_IMA, "Hudson HVQM4 4-bit IMA ADPCM"},
{coding_MSADPCM, "Microsoft 4-bit ADPCM"},
@ -849,6 +849,7 @@ static const layout_info layout_info_list[] = {
{layout_blocked_xa_aiff, "blocked (XA AIFF)"},
{layout_blocked_vs_square, "blocked (Square VS)"},
{layout_blocked_vid1, "blocked (VID1)"},
{layout_blocked_ubi_sce, "blocked (Ubi SCE)"},
};
static const meta_info meta_info_list[] = {
@ -1283,6 +1284,7 @@ static const meta_info meta_info_list[] = {
{meta_TGC, "Tiger Game.com .4 header"},
{meta_KWB, "Koei Tecmo WaveBank header"},
{meta_LRMD, "Sony LRMD header"},
{meta_WWISE_FX, "Audiokinetic Wwise FX header"},
};

View File

@ -10,6 +10,8 @@ liblayout_la_LDFLAGS =
liblayout_la_SOURCES = (auto-updated)
EXTRA_DIST = (auto-updated)
AM_CFLAGS += -DVGM_USE_G7221
if HAVE_VORBIS
if HAVE_VORBISFILE
AM_CFLAGS += -DVGM_USE_VORBIS

View File

@ -208,6 +208,9 @@ void block_update(off_t block_offset, VGMSTREAM * vgmstream) {
case layout_blocked_vid1:
block_update_vid1(block_offset,vgmstream);
break;
case layout_blocked_ubi_sce:
block_update_ubi_sce(block_offset,vgmstream);
break;
default: /* not a blocked layout */
break;
}

View File

@ -0,0 +1,57 @@
#include "layout.h"
#include "../vgmstream.h"
/* weird mix of Ubi ADPCM format with Ubi IMA, found in Splinter Cell Essentials (PSP) */
void block_update_ubi_sce(off_t block_offset, VGMSTREAM* vgmstream) {
STREAMFILE* sf = vgmstream->ch[0].streamfile;
int i, channels;
size_t header_size, frame_size, subframe_size, padding_size;
/* format mimics Ubi ADPCM's:
* - 0x34/38 header with frame size (ex. 0x600), pre-read in meta
* xN frames:
* - 0x34 channel header per channel, with ADPCM config
* - subframe (ex. 0x300) + padding byte
* - subframe (ex. 0x300) + padding byte
*
* to skip the padding byte we'll detect subframes using codec_config as a counter
* (higher bit has a special meaning)
*/
vgmstream->codec_config |= 0x80; /* flag for decoder, ugly I know */
if ((vgmstream->codec_config & 1) == 0) {
header_size = 0x34; /* read header in first subframe */
}
else {
header_size = 0x00;
}
vgmstream->codec_config ^= 1; /* swap counter bit */
channels = vgmstream->channels;
frame_size = vgmstream->full_block_size;
subframe_size = frame_size / 2;
padding_size = 0x01;
vgmstream->current_block_offset = block_offset;
vgmstream->current_block_size = subframe_size;
vgmstream->next_block_offset = block_offset + header_size * vgmstream->channels + subframe_size + padding_size;
for (i = 0; i < vgmstream->channels; i++) {
vgmstream->ch[i].offset = block_offset + header_size * channels;
if (header_size > 0) {
vgmstream->ch[i].adpcm_step_index = read_32bitLE(block_offset + header_size * i + 0x04, sf);
vgmstream->ch[i].adpcm_history1_32 = read_32bitLE(block_offset + header_size * i + 0x08, sf);
/* TODO figure out
* First step seems to always be a special value for the decoder, unsure of meaning.
* 0 = too quiet and max = 88 = waveform starts a bit off and clicky. First hist is usually +-1,
* other frames look, fine not sure what are they aiming for.
*/
if (vgmstream->ch[i].adpcm_step_index == 0x500) {
vgmstream->ch[i].adpcm_step_index = 88;
}
}
}
}

View File

@ -47,6 +47,7 @@ void block_update_h4m(off_t block_offset, VGMSTREAM * vgmstream);
void block_update_xa_aiff(off_t block_offset, VGMSTREAM * vgmstream);
void block_update_vs_square(off_t block_offset, VGMSTREAM * vgmstream);
void block_update_vid1(off_t block_offset, VGMSTREAM* vgmstream);
void block_update_ubi_sce(off_t block_offset, VGMSTREAM* vgmstream);
/* other layouts */
void render_vgmstream_interleave(sample_t * buffer, int32_t sample_count, VGMSTREAM * vgmstream);

View File

@ -300,6 +300,10 @@
RelativePath=".\meta\mta2_streamfile.h"
>
</File>
<File
RelativePath=".\meta\mul_streamfile.h"
>
</File>
<File
RelativePath=".\meta\mzrt_streamfile.h"
>
@ -663,6 +667,10 @@
<File
RelativePath=".\meta\ea_wve_ad10.c"
>
</File>
<File
RelativePath=".\meta\encrypted.c"
>
</File>
<File
RelativePath=".\meta\mul.c"
@ -2389,6 +2397,10 @@
<File
RelativePath=".\layout\blocked_tra.c"
>
</File>
<File
RelativePath=".\layout\blocked_ubi_sce.c"
>
</File>
<File
RelativePath=".\layout\blocked_vs.c"

View File

@ -122,6 +122,7 @@
<ClInclude Include="meta\xnb_streamfile.h" />
<ClInclude Include="meta\xnb_lz4mg.h" />
<ClInclude Include="meta\mta2_streamfile.h" />
<ClInclude Include="meta\mul_streamfile.h" />
<ClInclude Include="meta\mzrt_streamfile.h" />
<ClInclude Include="meta\nus3bank_streamfile.h" />
<ClInclude Include="meta\ogg_vorbis_streamfile.h" />
@ -172,6 +173,7 @@
<ClCompile Include="layout\blocked_vs_str.c" />
<ClCompile Include="layout\layered.c" />
<ClCompile Include="layout\blocked_tra.c" />
<ClCompile Include="layout\blocked_ubi_sce.c" />
<ClCompile Include="meta\akb.c" />
<ClCompile Include="meta\awb.c" />
<ClCompile Include="meta\awc.c" />
@ -309,6 +311,7 @@
<ClCompile Include="meta\ea_eaac.c" />
<ClCompile Include="meta\ea_wve_au00.c" />
<ClCompile Include="meta\ea_wve_ad10.c" />
<ClCompile Include="meta\encrypted.c" />
<ClCompile Include="meta\mul.c" />
<ClCompile Include="meta\exakt_sc.c" />
<ClCompile Include="meta\ffw.c" />

View File

@ -122,6 +122,9 @@
<ClInclude Include="meta\mta2_streamfile.h">
<Filter>meta\Header Files</Filter>
</ClInclude>
<ClInclude Include="meta\mul_streamfile.h">
<Filter>meta\Header Files</Filter>
</ClInclude>
<ClInclude Include="meta\mzrt_streamfile.h">
<Filter>meta\Header Files</Filter>
</ClInclude>
@ -448,6 +451,9 @@
<ClCompile Include="meta\ea_wve_ad10.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\encrypted.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\mul.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
@ -1489,6 +1495,9 @@
<ClCompile Include="layout\blocked_tra.c">
<Filter>layout\Source Files</Filter>
</ClCompile>
<ClCompile Include="layout\blocked_ubi_sce.c">
<Filter>layout\Source Files</Filter>
</ClCompile>
<ClCompile Include="coding\lsf_decoder.c">
<Filter>coding\Source Files</Filter>
</ClCompile>

View File

@ -10,6 +10,8 @@ libmeta_la_LDFLAGS =
libmeta_la_SOURCES = (auto-updated)
EXTRA_DIST = (auto-updated)
AM_CFLAGS += -DVGM_USE_G7221
if HAVE_VORBIS
if HAVE_VORBISFILE
AM_CFLAGS += -DVGM_USE_VORBIS

View File

@ -42,12 +42,19 @@ VGMSTREAM* init_vgmstream_bkhd(STREAMFILE* sf) {
subfile_offset = read_u32(offset + 0x04, sf) + data_offset;
subfile_size = read_u32(offset + 0x08, sf);
//;VGM_LOG("BKHD: %lx, %x\n", subfile_offset, subfile_size);
temp_sf = setup_subfile_streamfile(sf, subfile_offset, subfile_size, "wem");
if (!temp_sf) goto fail;
subfile_id = read_u32(0x00, temp_sf);
if (subfile_id == 0x52494646 || subfile_id == 0x52494658) { /* "RIFF" / "RIFX" */
vgmstream = init_vgmstream_wwise(temp_sf);
if (!vgmstream) goto fail;
}
else {
vgmstream = init_vgmstream_bkhd_fx(temp_sf);
if (!vgmstream) goto fail;
}
vgmstream->num_streams = total_subsongs;
snprintf(vgmstream->stream_name, STREAM_NAME_SIZE, "%u", subfile_id);
@ -60,3 +67,60 @@ fail:
close_vgmstream(vgmstream);
return NULL;
}
/* BKHD mini format, probably from a generator plugin [Borderlands 2 (X360)] */
VGMSTREAM* init_vgmstream_bkhd_fx(STREAMFILE* sf) {
VGMSTREAM* vgmstream = NULL;
off_t start_offset, data_size;
int big_endian, loop_flag, channels, sample_rate, entries;
uint32_t (*read_u32)(off_t,STREAMFILE*);
/* checks */
if (!check_extensions(sf,"wem,bnk")) /* assumed */
goto fail;
big_endian = guess_endianness32bit(0x00, sf);
read_u32 = big_endian ? read_u32be : read_u32le;
if (read_u32(0x00, sf) != 0x0400) /* codec? */
goto fail;
if (read_u32(0x04, sf) != 0x0800) /* codec? */
goto fail;
sample_rate = read_u32(0x08, sf);
channels = read_u32(0x0c, sf);
/* 0x10: some id or small size? */
/* 0x14/18: some float? */
entries = read_u32(0x1c, sf);
/* 0x20 data size / 0x10 */
if (read_u8(0x24, sf) != 4) /* bps */
goto fail;
/* 0x30: unknown table of 16b that goes up and down */
start_offset = 0x30 + align_size_to_block(entries * 0x02, 0x10);
data_size = get_streamfile_size(sf) - start_offset;
loop_flag = 0;
/* output sounds a bit funny, maybe not an actual stream but parts using the table */
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channels, loop_flag);
if (!vgmstream) goto fail;
vgmstream->meta_type = meta_WWISE_FX;
vgmstream->sample_rate = sample_rate;
vgmstream->coding_type = coding_PCMFLOAT;
vgmstream->layout_type = layout_interleave;
vgmstream->codec_endian = big_endian;
vgmstream->interleave_block_size = 0x4;
vgmstream->num_samples = pcm_bytes_to_samples(data_size, channels, 32);
if (!vgmstream_open_stream(vgmstream,sf,start_offset))
goto fail;
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;
}

View File

@ -4,7 +4,7 @@
typedef enum { PSX, PCM16, ATRAC9, HEVAG } bnk_codec;
/* .BNK - Sony's Scream Tool bank format [Puyo Puyo Tetris (PS4), NekoBuro: Cats Block (Vita)] */
VGMSTREAM * init_vgmstream_bnk_sony(STREAMFILE *streamFile) {
VGMSTREAM* init_vgmstream_bnk_sony(STREAMFILE* sf) {
VGMSTREAM* vgmstream = NULL;
off_t start_offset, stream_offset, name_offset = 0;
size_t stream_size, interleave = 0;
@ -15,7 +15,7 @@ VGMSTREAM * init_vgmstream_bnk_sony(STREAMFILE *streamFile) {
uint32_t pitch, flags;
uint32_t atrac9_info = 0;
int total_subsongs, target_subsong = streamFile->stream_index;
int total_subsongs, target_subsong = sf->stream_index;
int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL;
int16_t (*read_16bit)(off_t,STREAMFILE*) = NULL;
float (*read_f32)(off_t,STREAMFILE*) = NULL;
@ -23,17 +23,17 @@ VGMSTREAM * init_vgmstream_bnk_sony(STREAMFILE *streamFile) {
/* checks */
if (!check_extensions(streamFile, "bnk"))
if (!check_extensions(sf, "bnk"))
goto fail;
/* bnk version */
if (read_32bitBE(0x00,streamFile) == 0x00000003) { /* PS3 */
if (read_32bitBE(0x00,sf) == 0x00000003) { /* PS3 */
read_32bit = read_32bitBE;
read_16bit = read_16bitBE;
read_f32 = read_f32be;
big_endian = 1;
}
else if (read_32bitBE(0x00,streamFile) == 0x03000000) { /* PS2/Vita/PS4 */
else if (read_32bitBE(0x00,sf) == 0x03000000) { /* PS2/Vita/PS4 */
read_32bit = read_32bitLE;
read_16bit = read_16bitLE;
read_f32 = read_f32le;
@ -43,13 +43,13 @@ VGMSTREAM * init_vgmstream_bnk_sony(STREAMFILE *streamFile) {
goto fail;
}
parts = read_32bit(0x04,streamFile);
parts = read_32bit(0x04,sf);
if (parts < 2 || parts > 3) goto fail;
sblk_offset = read_32bit(0x08,streamFile);
sblk_offset = read_32bit(0x08,sf);
/* 0x0c: sklb size */
data_offset = read_32bit(0x10,streamFile);
data_size = read_32bit(0x14,streamFile);
data_offset = read_32bit(0x10,sf);
data_size = read_32bit(0x14,sf);
/* when sblk_offset >= 0x20: */
/* 0x18: ZLSD small footer, rare [Yakuza 6's Puyo Puyo (PS4)] */
/* 0x1c: ZLSD size */
@ -60,9 +60,9 @@ VGMSTREAM * init_vgmstream_bnk_sony(STREAMFILE *streamFile) {
/* SBlk part: parse header */
if (read_32bit(sblk_offset+0x00,streamFile) != 0x6B6C4253) /* "klBS" (SBlk = sample block?) */
if (read_32bit(sblk_offset+0x00,sf) != 0x6B6C4253) /* "klBS" (SBlk = sample block?) */
goto fail;
sblk_version = read_32bit(sblk_offset+0x04,streamFile);
sblk_version = read_32bit(sblk_offset+0x04,sf);
/* 0x08: possibly when sblk_version=0x0d, 0x03=Vita, 0x06=PS4 */
//;VGM_LOG("BNK: sblk_offset=%lx, data_offset=%lx, sblk_version %x\n", sblk_offset, data_offset, sblk_version);
@ -79,11 +79,11 @@ VGMSTREAM * init_vgmstream_bnk_sony(STREAMFILE *streamFile) {
switch(sblk_version) {
case 0x01: /* Ratchet & Clank (PS2) */
section_entries = (uint16_t)read_16bit(sblk_offset+0x16,streamFile); /* entry size: ~0x0c */
material_entries = (uint16_t)read_16bit(sblk_offset+0x18,streamFile); /* entry size: ~0x28 */
stream_entries = (uint16_t)read_16bit(sblk_offset+0x1a,streamFile); /* entry size: none (count) */
table1_offset = sblk_offset + read_32bit(sblk_offset+0x1c,streamFile);
table2_offset = sblk_offset + read_32bit(sblk_offset+0x20,streamFile);
section_entries = (uint16_t)read_16bit(sblk_offset+0x16,sf); /* entry size: ~0x0c */
material_entries = (uint16_t)read_16bit(sblk_offset+0x18,sf); /* entry size: ~0x28 */
stream_entries = (uint16_t)read_16bit(sblk_offset+0x1a,sf); /* entry size: none (count) */
table1_offset = sblk_offset + read_32bit(sblk_offset+0x1c,sf);
table2_offset = sblk_offset + read_32bit(sblk_offset+0x20,sf);
table3_offset = table2_offset; /* mixed table in this version */
table4_offset = 0; /* not included */
@ -92,16 +92,17 @@ VGMSTREAM * init_vgmstream_bnk_sony(STREAMFILE *streamFile) {
table2_suboffset = 0;
break;
case 0x03: /* Yu-Gi-Oh! GX - The Beginning of Destiny (PS2) */
case 0x04: /* Test banks */
case 0x05: /* Ratchet & Clank (PS3) */
case 0x09: /* Puyo Puyo Tetris (PS4) */
section_entries = (uint16_t)read_16bit(sblk_offset+0x16,streamFile); /* entry size: ~0x0c */
material_entries = (uint16_t)read_16bit(sblk_offset+0x18,streamFile); /* entry size: ~0x08 */
stream_entries = (uint16_t)read_16bit(sblk_offset+0x1a,streamFile); /* entry size: ~0x18 + variable */
table1_offset = sblk_offset + read_32bit(sblk_offset+0x1c,streamFile);
table2_offset = sblk_offset + read_32bit(sblk_offset+0x20,streamFile);
table3_offset = sblk_offset + read_32bit(sblk_offset+0x34,streamFile);
table4_offset = sblk_offset + read_32bit(sblk_offset+0x38,streamFile);
section_entries = (uint16_t)read_16bit(sblk_offset+0x16,sf); /* entry size: ~0x0c */
material_entries = (uint16_t)read_16bit(sblk_offset+0x18,sf); /* entry size: ~0x08 */
stream_entries = (uint16_t)read_16bit(sblk_offset+0x1a,sf); /* entry size: ~0x18 + variable */
table1_offset = sblk_offset + read_32bit(sblk_offset+0x1c,sf);
table2_offset = sblk_offset + read_32bit(sblk_offset+0x20,sf);
table3_offset = sblk_offset + read_32bit(sblk_offset+0x34,sf);
table4_offset = sblk_offset + read_32bit(sblk_offset+0x38,sf);
table1_entry_size = 0x0c;
table1_suboffset = 0x08;
@ -110,13 +111,13 @@ VGMSTREAM * init_vgmstream_bnk_sony(STREAMFILE *streamFile) {
case 0x0d: /* Polara (Vita), Crypt of the Necrodancer (Vita) */
case 0x0e: /* Yakuza 6's Puyo Puyo (PS4) */
table1_offset = sblk_offset + read_32bit(sblk_offset+0x18,streamFile);
table2_offset = sblk_offset + read_32bit(sblk_offset+0x1c,streamFile);
table3_offset = sblk_offset + read_32bit(sblk_offset+0x2c,streamFile);
table4_offset = sblk_offset + read_32bit(sblk_offset+0x30,streamFile);
section_entries = (uint16_t)read_16bit(sblk_offset+0x38,streamFile); /* entry size: ~0x24 */
material_entries = (uint16_t)read_16bit(sblk_offset+0x3a,streamFile); /* entry size: ~0x08 */
stream_entries = (uint16_t)read_16bit(sblk_offset+0x3c,streamFile); /* entry size: ~0x5c + variable */
table1_offset = sblk_offset + read_32bit(sblk_offset+0x18,sf);
table2_offset = sblk_offset + read_32bit(sblk_offset+0x1c,sf);
table3_offset = sblk_offset + read_32bit(sblk_offset+0x2c,sf);
table4_offset = sblk_offset + read_32bit(sblk_offset+0x30,sf);
section_entries = (uint16_t)read_16bit(sblk_offset+0x38,sf); /* entry size: ~0x24 */
material_entries = (uint16_t)read_16bit(sblk_offset+0x3a,sf); /* entry size: ~0x08 */
stream_entries = (uint16_t)read_16bit(sblk_offset+0x3c,sf); /* entry size: ~0x5c + variable */
table1_entry_size = 0x24;
table1_suboffset = 0x0c;
@ -151,19 +152,34 @@ VGMSTREAM * init_vgmstream_bnk_sony(STREAMFILE *streamFile) {
case 0x01:
/* table2/3 has size 0x28 entries, seemingly:
* 0x00: subtype(01=sound)
* 0x0A: pitch
* 0x08: same as other versions (pitch, flags, offset...)
* rest: padding
* 0x18: stream offset
* there is no stream size so we'd need some sample counting
* many sounds repeat entries for no apparent reason
* there is no stream size like in v0x03
*/
goto fail;
for (i = 0; i < material_entries; i++) {
uint8_t table2_type = read_32bit(table2_offset + (i*0x28) + 0x00, sf);
if (table2_type != 0x01)
continue;
total_subsongs++;
if (total_subsongs == target_subsong) {
table2_entry_offset = 0;
table3_entry_offset = (i*0x28) + 0x08;
/* continue to count all subsongs*/
}
}
break;
default:
for (i = 0; i < material_entries; i++) {
uint32_t table2_value, table2_subinfo, table2_subtype;
table2_value = (uint32_t)read_32bit(table2_offset+(i*0x08)+table2_suboffset+0x00,streamFile);
table2_value = (uint32_t)read_32bit(table2_offset+(i*0x08)+table2_suboffset+0x00,sf);
table2_subinfo = (table2_value >> 0) & 0xFFFF;
table2_subtype = (table2_value >> 16) & 0xFFFF;
if (table2_subtype != 0x100)
@ -194,14 +210,15 @@ VGMSTREAM * init_vgmstream_bnk_sony(STREAMFILE *streamFile) {
/* parse sounds */
switch(sblk_version) {
case 0x01:
case 0x03:
case 0x04:
case 0x05:
case 0x09:
pitch = (uint8_t)read_8bit(table3_offset+table3_entry_offset+0x02,streamFile);
flags = (uint8_t)read_8bit(table3_offset+table3_entry_offset+0x0f,streamFile);
stream_offset = read_32bit(table3_offset+table3_entry_offset+0x10,streamFile);
stream_size = read_32bit(table3_offset+table3_entry_offset+0x14,streamFile);
pitch = (uint8_t)read_8bit(table3_offset+table3_entry_offset+0x02,sf);
flags = (uint8_t)read_8bit(table3_offset+table3_entry_offset+0x0f,sf);
stream_offset = read_32bit(table3_offset+table3_entry_offset+0x10,sf);
stream_size = read_32bit(table3_offset+table3_entry_offset+0x14,sf);
/* must use some log/formula but whatevs */
switch(pitch) {
@ -218,6 +235,7 @@ VGMSTREAM * init_vgmstream_bnk_sony(STREAMFILE *streamFile) {
case 0xAF: sample_rate = 14000; break; //?
case 0xAE: sample_rate = 13000; break; //?
case 0xAC: sample_rate = 12000; break; //?
case 0xAB: sample_rate = 11050; break; //?
case 0xAA: sample_rate = 11025; break;
case 0xA9: sample_rate = 10000; break; //?
case 0xA4: sample_rate = 6000; break; //?
@ -230,10 +248,10 @@ VGMSTREAM * init_vgmstream_bnk_sony(STREAMFILE *streamFile) {
case 0x0d:
case 0x0e:
flags = (uint8_t)read_8bit(table3_offset+table3_entry_offset+0x12,streamFile);
stream_offset = read_32bit(table3_offset+table3_entry_offset+0x44,streamFile);
stream_size = read_32bit(table3_offset+table3_entry_offset+0x48,streamFile);
sample_rate = (int)read_f32(table3_offset+table3_entry_offset+0x4c,streamFile);
flags = (uint8_t)read_8bit(table3_offset+table3_entry_offset+0x12,sf);
stream_offset = read_32bit(table3_offset+table3_entry_offset+0x44,sf);
stream_size = read_32bit(table3_offset+table3_entry_offset+0x48,sf);
sample_rate = (int)read_f32(table3_offset+table3_entry_offset+0x4c,sf);
break;
default:
@ -251,7 +269,7 @@ VGMSTREAM * init_vgmstream_bnk_sony(STREAMFILE *streamFile) {
case 0x0e:
/* find if this sound has an assigned name in table1 */
for (i = 0; i < section_entries; i++) {
off_t entry_offset = (uint16_t)read_16bit(table1_offset+(i*table1_entry_size)+table1_suboffset+0x00,streamFile);
off_t entry_offset = (uint16_t)read_16bit(table1_offset+(i*table1_entry_size)+table1_suboffset+0x00,sf);
/* rarely (ex. Polara sfx) one name applies to multiple materials,
* from current entry_offset to next entry_offset (section offsets should be in order) */
@ -267,15 +285,15 @@ VGMSTREAM * init_vgmstream_bnk_sony(STREAMFILE *streamFile) {
/* 0x0c: table4 size */
/* variable: entries */
/* variable: names (null terminated) */
table4_entries_offset = table4_offset + read_32bit(table4_offset+0x08, streamFile);
table4_entries_offset = table4_offset + read_32bit(table4_offset+0x08, sf);
table4_names_offset = table4_entries_offset + (0x10*section_entries);
//;VGM_LOG("BNK: t4_entries=%lx, t4_names=%lx\n", table4_entries_offset, table4_names_offset);
/* get assigned name from table4 names */
for (i = 0; i < section_entries; i++) {
int entry_id = read_32bit(table4_entries_offset+(i*0x10)+0x0c, streamFile);
int entry_id = read_32bit(table4_entries_offset+(i*0x10)+0x0c, sf);
if (entry_id == table4_entry_id) {
name_offset = table4_names_offset + read_32bit(table4_entries_offset+(i*0x10)+0x00, streamFile);
name_offset = table4_names_offset + read_32bit(table4_entries_offset+(i*0x10)+0x00, sf);
break;
}
}
@ -302,6 +320,29 @@ VGMSTREAM * init_vgmstream_bnk_sony(STREAMFILE *streamFile) {
case 0x05:
channel_count = 1;
/* early versions don't have PS-ADPCM size, could check next offset but it's all kind of loopy */
if (sblk_version <= 0x03 && stream_size == 0 && (flags & 0x80) == 0) {
off_t offset;
off_t max_offset = get_streamfile_size(sf);
stream_size += 0x10;
for (offset = data_offset + stream_offset + 0x10; offset < max_offset; offset += 0x10) {
/* beginning frame (if file loops won't have end frame) */
if (read_32bitBE(offset + 0x00, sf) == 0x00000000 && read_32bitBE(offset + 0x04, sf) == 0x00000000)
break;
stream_size += 0x10;
/* end frame */
if (read_32bitBE(offset + 0x00, sf) == 0x00077777 && read_32bitBE(offset + 0x04, sf) == 0x77777777)
break;
}
//;VGM_LOG("BNK: stream offset=%lx + %lx, new size=%x\n", data_offset, stream_offset, stream_size);
}
/* hack for PS3 files that use dual subsongs as stereo */
if (total_subsongs == 2 && stream_size * 2 == data_size) {
channel_count = 2;
@ -314,7 +355,7 @@ VGMSTREAM * init_vgmstream_bnk_sony(STREAMFILE *streamFile) {
codec = PCM16; /* rare [Wipeout HD (PS3)] */
}
else {
loop_flag = ps_find_loop_offsets(streamFile, start_offset, stream_size, channel_count, interleave, &loop_start, &loop_end);
loop_flag = ps_find_loop_offsets(sf, start_offset, stream_size, channel_count, interleave, &loop_start, &loop_end);
loop_flag = (flags & 0x40); /* no loops values in sight so may only apply to PS-ADPCM flags */
codec = PSX;
@ -324,20 +365,20 @@ VGMSTREAM * init_vgmstream_bnk_sony(STREAMFILE *streamFile) {
break;
case 0x09:
type = read_16bit(start_offset+0x00,streamFile);
extradata_size = 0x08 + read_32bit(start_offset+0x04,streamFile); /* 0x14 for AT9 */
type = read_16bit(start_offset+0x00,sf);
extradata_size = 0x08 + read_32bit(start_offset+0x04,sf); /* 0x14 for AT9 */
switch(type) {
case 0x02: /* ATRAC9 mono */
case 0x05: /* ATRAC9 stereo */
if (read_32bit(start_offset+0x08,streamFile) + 0x08 != extradata_size) /* repeat? */
if (read_32bit(start_offset+0x08,sf) + 0x08 != extradata_size) /* repeat? */
goto fail;
channel_count = (type == 0x02) ? 1 : 2;
atrac9_info = (uint32_t)read_32bitBE(start_offset+0x0c,streamFile);
atrac9_info = (uint32_t)read_32bitBE(start_offset+0x0c,sf);
/* 0x10: null? */
loop_length = read_32bit(start_offset+0x14,streamFile);
loop_start = read_32bit(start_offset+0x18,streamFile);
loop_length = read_32bit(start_offset+0x14,sf);
loop_start = read_32bit(start_offset+0x18,sf);
loop_end = loop_start + loop_length; /* loop_start is -1 if not set */
codec = ATRAC9;
@ -351,26 +392,26 @@ VGMSTREAM * init_vgmstream_bnk_sony(STREAMFILE *streamFile) {
case 0x0d:
case 0x0e:
type = read_16bit(start_offset+0x00,streamFile);
if (read_32bit(start_offset+0x04,streamFile) != 0x01) /* type? */
type = read_16bit(start_offset+0x00,sf);
if (read_32bit(start_offset+0x04,sf) != 0x01) /* type? */
goto fail;
extradata_size = 0x10 + read_32bit(start_offset+0x08,streamFile); /* 0x80 for AT9, 0x10 for PCM/PS-ADPCM */
extradata_size = 0x10 + read_32bit(start_offset+0x08,sf); /* 0x80 for AT9, 0x10 for PCM/PS-ADPCM */
/* 0x0c: null? */
switch(type) {
case 0x02: /* ATRAC9 mono */
case 0x05: /* ATRAC9 stereo */
if (read_32bit(start_offset+0x10,streamFile) + 0x10 != extradata_size) /* repeat? */
if (read_32bit(start_offset+0x10,sf) + 0x10 != extradata_size) /* repeat? */
goto fail;
channel_count = (type == 0x02) ? 1 : 2;
atrac9_info = (uint32_t)read_32bitBE(start_offset+0x14,streamFile);
atrac9_info = (uint32_t)read_32bitBE(start_offset+0x14,sf);
/* 0x18: null? */
/* 0x1c: channels? */
/* 0x20: null? */
loop_length = read_32bit(start_offset+0x24,streamFile);
loop_start = read_32bit(start_offset+0x28,streamFile);
loop_length = read_32bit(start_offset+0x24,sf);
loop_start = read_32bit(start_offset+0x28,sf);
loop_end = loop_start + loop_length; /* loop_start is -1 if not set */
codec = ATRAC9;
@ -379,11 +420,11 @@ VGMSTREAM * init_vgmstream_bnk_sony(STREAMFILE *streamFile) {
case 0x01: /* PCM16LE mono? (NekoBuro/Polara sfx) */
case 0x04: /* PCM16LE stereo? (NekoBuro/Polara sfx) */
/* 0x10: null? */
channel_count = read_32bit(start_offset+0x14,streamFile);
channel_count = read_32bit(start_offset+0x14,sf);
interleave = 0x02;
loop_start = read_32bit(start_offset+0x18,streamFile);
loop_length = read_32bit(start_offset+0x1c,streamFile);
loop_start = read_32bit(start_offset+0x18,sf);
loop_length = read_32bit(start_offset+0x1c,sf);
loop_end = loop_start + loop_length; /* loop_start is -1 if not set */
codec = PCM16;
@ -391,11 +432,11 @@ VGMSTREAM * init_vgmstream_bnk_sony(STREAMFILE *streamFile) {
case 0x00: /* PS-ADPCM (test banks) */
/* 0x10: null? */
channel_count = read_32bit(start_offset+0x14,streamFile);
channel_count = read_32bit(start_offset+0x14,sf);
interleave = 0x02;
loop_start = read_32bit(start_offset+0x18,streamFile);
loop_length = read_32bit(start_offset+0x1c,streamFile);
loop_start = read_32bit(start_offset+0x18,sf);
loop_length = read_32bit(start_offset+0x1c,sf);
loop_end = loop_start + loop_length; /* loop_start is -1 if not set */
codec = HEVAG;
@ -486,10 +527,10 @@ VGMSTREAM * init_vgmstream_bnk_sony(STREAMFILE *streamFile) {
}
if (name_offset)
read_string(vgmstream->stream_name,STREAM_NAME_SIZE, name_offset,streamFile);
read_string(vgmstream->stream_name,STREAM_NAME_SIZE, name_offset,sf);
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
if (!vgmstream_open_stream(vgmstream, sf, start_offset))
goto fail;
return vgmstream;
fail:
@ -500,12 +541,16 @@ fail:
#if 0
/* .BNK - Sony's bank, earlier version [Jak and Daxter (PS2), NCAA Gamebreaker 2001 (PS2)] */
VGMSTREAM * init_vgmstream_bnk_sony_v2(STREAMFILE *streamFile) {
VGMSTREAM * init_vgmstream_bnk_sony_v2(STREAMFILE *sf) {
/* 0x00: 0x00000001
* 0x04: sections (2 or 3)
* 0x08+ similar to v3 but "SBv2"
* table formats is a bit different
* header is like v3 but stream size is in other table?
* 0x08+: similar as above (start, size) but with "SBv2"
*
* 0x2C/2E=entries?
* 0x34: table offsets (from sbv2 start)
* - table1: 0x1c entries
* - table2: 0x08 entries, possibly point to table3
* - table3: 0x18 entries, same format as early SBlk (pitch/flags/offset, no size)
*/
fail:

View File

@ -4,12 +4,13 @@
#include "bnsf_keys.h"
#ifdef VGM_USE_G7221
//#define BNSF_BRUTEFORCE
#ifdef BNSF_BRUTEFORCE
static void bruteforce_bnsf_key(STREAMFILE* sf, off_t start, g7221_codec_data* data, uint8_t* best_key);
#endif
static void find_bnsf_key(STREAMFILE *sf, off_t start, g7221_codec_data *data, uint8_t *best_key);
#endif
/* BNSF - Bandai Namco Sound Format/File [Tales of Graces (Wii), Tales of Berseria (PS4)] */
VGMSTREAM * init_vgmstream_bnsf(STREAMFILE *streamFile) {
@ -126,6 +127,7 @@ fail:
return NULL;
}
#ifdef VGM_USE_G7221
static inline void test_key(STREAMFILE* sf, off_t start, g7221_codec_data* data, const char* key, int keylen, int* p_best_score, uint8_t* p_best_key) {
uint8_t tmpkey[24];
int score;
@ -219,3 +221,5 @@ static void bruteforce_bnsf_key(STREAMFILE* sf, off_t start, g7221_codec_data* d
close_streamfile(sf_keys);
}
#endif
#endif

View File

@ -44,6 +44,7 @@ VGMSTREAM * init_vgmstream_cstr(STREAMFILE* sf) {
loop_flag = (loop_start >= 0);
start_offset = 0x20 + 0x60 * channels + first_skip;
#if 1
/* nonlooped tracks may not set first skip for no reason, but can be tested with initial p/s */
if (!loop_flag && channels == 2 && first_skip == 0) {
while (first_skip < 0x800) {
@ -55,10 +56,12 @@ VGMSTREAM * init_vgmstream_cstr(STREAMFILE* sf) {
/* not found */
if (first_skip == 0x800)
first_skip = 0;
else
start_offset += first_skip;
}
if (first_skip > 0 && loop_start >= (interleave - first_skip))
loop_start = loop_start - (interleave - first_skip);
#endif
loop_start = loop_start * 2;
/* Mr. Driller oddity, unreliable loop flag */
@ -94,7 +97,7 @@ VGMSTREAM * init_vgmstream_cstr(STREAMFILE* sf) {
vgmstream->interleave_first_block_size = interleave - first_skip;
vgmstream->interleave_first_skip = first_skip;
vgmstream->meta_type = meta_DSP_CSTR;
VGM_LOG("1=%x, =%x\n",vgmstream->interleave_first_block_size, vgmstream->interleave_first_skip);
dsp_read_coefs_be(vgmstream, sf, 0x3c, 0x60);
if (!vgmstream_open_stream(vgmstream, sf, start_offset))

View File

@ -13,6 +13,7 @@ static void block_callback_default(STREAMFILE *sf, deblock_io_data *data) {
static size_t deblock_io_read(STREAMFILE *sf, uint8_t *dest, off_t offset, size_t length, deblock_io_data* data) {
size_t total_read = 0;
//;VGM_LOG("DEBLOCK: of=%lx, sz=%x, po=%lx\n", offset, length, data->physical_offset);
/* re-start when previous offset (can't map logical<>physical offsets) */
if (data->logical_offset < 0 || offset < data->logical_offset) {
@ -81,11 +82,11 @@ static size_t deblock_io_read(STREAMFILE *sf, uint8_t *dest, off_t offset, size_
data->data_size = 0;
data->step_count = data->cfg.step_count;
//VGM_LOG("ignore at %lx + %lx, skips=%i, reads=%i\n", data->physical_offset, data->block_size, data->step_count, data->read_count);
//VGM_LOG("ignore at %lx + %lx, skips=%i\n", data->physical_offset, data->block_size, data->step_count);
continue;
}
//VGM_LOG("accept at %lx + %lx, skips=%i, reads=%i\n", data->physical_offset, data->block_size, data->step_count, data->read_count);
//;VGM_LOG("accept at %lx + %lx, skips=%i\n", data->physical_offset, data->block_size, data->step_count);
/* read block data */
{

View File

@ -1618,11 +1618,13 @@ static layered_layout_data* build_layered_eaaudiocore(STREAMFILE *sf_data, eaac_
if ( !vgmstream_open_stream(data->layers[i], temp_sf, 0x00) ) {
goto fail;
}
close_streamfile(temp_sf);
temp_sf = NULL;
}
if (!setup_layout_layered(data))
goto fail;
close_streamfile(temp_sf);
return data;
fail:

83
src/meta/encrypted.c Normal file
View File

@ -0,0 +1,83 @@
#include "meta.h"
#include "../coding/coding.h"
#include "ogg_vorbis_streamfile.h"
//todo fuse ogg encryptions and use generic names
static const uint8_t tpf_key[] = {
0x0a,0x2b,0x36,0x6f,0x0b,0x0a,0x2b,0x36,0x6f,0x0B
};
static void load_key(ogg_vorbis_io_config_data* cfg, const uint8_t* key, size_t size) {
cfg->is_encrypted = 1;
cfg->key_len = size;
memcpy(cfg->key, key, size);
}
/* parser for various encrypted games */
VGMSTREAM* init_vgmstream_encrypted(STREAMFILE* sf) {
VGMSTREAM* vgmstream = NULL;
STREAMFILE* temp_sf = NULL;
ogg_vorbis_io_config_data cfg = {0};
uint32_t id;
/* checks */
id = read_u32be(0x00, sf);
if (check_extensions(sf,"ogg,logg")) {
/* The Pirate's Fate (PC) */
if (id == 0x454C513C) { /* "OggS" xored */
load_key(&cfg, tpf_key, sizeof(tpf_key));
}
else {
goto fail;
}
temp_sf = setup_ogg_vorbis_streamfile(sf, cfg);
if (!temp_sf) goto fail;
VGM_LOG("2\n");
vgmstream = init_vgmstream_ogg_vorbis(temp_sf);
close_streamfile(temp_sf);
return vgmstream;
}
if (check_extensions(sf,"mp3")) {
/* The Pirate's Fate (PC) */
if ((id & 0xFFFFFF00) == 0x436F0500) { /* "ID3\0" xored */
load_key(&cfg, tpf_key, sizeof(tpf_key));
}
else {
goto fail;
}
temp_sf = setup_ogg_vorbis_streamfile(sf, cfg);
if (!temp_sf) goto fail;
vgmstream = init_vgmstream_ffmpeg(temp_sf);
close_streamfile(temp_sf);
return vgmstream;
}
if (check_extensions(sf,"wav,lwav")) {
/* The Pirate's Fate (PC) */
if (id == 0x58627029) { /* "RIFF" xored */
load_key(&cfg, tpf_key, sizeof(tpf_key));
}
else {
goto fail;
}
temp_sf = setup_ogg_vorbis_streamfile(sf, cfg);
if (!temp_sf) goto fail;
vgmstream = init_vgmstream_riff(temp_sf);
close_streamfile(temp_sf);
return vgmstream;
}
fail:
return NULL;
}

View File

@ -2,32 +2,38 @@
#include "../coding/coding.h"
/* .ISB - Creative ISACT (Interactive Spatial Audio Composition Tools) middleware [Psychonauts (PC)] */
VGMSTREAM * init_vgmstream_isb(STREAMFILE *streamFile) {
/* .ISB - Creative ISACT (Interactive Spatial Audio Composition Tools) middleware [Psychonauts (PC), Mass Effect (multi)] */
VGMSTREAM * init_vgmstream_isb(STREAMFILE *sf) {
VGMSTREAM * vgmstream = NULL;
off_t start_offset = 0, name_offset = 0;
size_t stream_size = 0, name_size = 0;
int loop_flag = 0, channel_count = 0, sample_rate = 0, codec = 0, pcm_bytes = 0, bps = 0;
int total_subsongs, target_subsong = streamFile->stream_index;
int total_subsongs, target_subsong = sf->stream_index;
uint32_t (*read_u32me)(off_t,STREAMFILE*);
uint32_t (*read_u32ce)(off_t,STREAMFILE*);
int big_endian;
/* checks */
if (!check_extensions(streamFile, "isb"))
if (!check_extensions(sf, "isb"))
goto fail;
if (read_32bitBE(0x00,streamFile) != 0x52494646) /* "RIFF" */
big_endian = read_u32be(0x00,sf) == 0x46464952; /* "FFIR" */
read_u32me = big_endian ? read_u32be : read_u32le; /* machine endianness... */
read_u32ce = big_endian ? read_u32le : read_u32be; /* chunks change with endianness but this just reads as BE */
if (read_u32ce(0x00,sf) != 0x52494646) /* "RIFF" */
goto fail;
if (read_32bitLE(0x04,streamFile) + 0x08 != get_streamfile_size(streamFile))
if (read_u32me(0x04,sf) + 0x08 != get_streamfile_size(sf))
goto fail;
if (read_32bitBE(0x08,streamFile) != 0x69736266) /* "isbf" */
if (read_u32ce(0x08,sf) != 0x69736266) /* "isbf" */
goto fail;
/* some files have a companion .icb, seems to be a cue file pointing here */
/* format is RIFF with many custom chunks, apparently for their DAW-like editor with
* complex functions, but most seem always included by default and unused, and games
* Psychonauts seems to use the format as a simple audio bank. Mass Effect (X360)
* apparently uses ISACT, while Psychonauts Xbox/PS2 don't. */
* seems to use the format as a simple audio bank. Psychonauts Xbox/PS2 doesn't use ISACT. */
{
off_t offset, max_offset, header_offset = 0;
@ -38,15 +44,15 @@ VGMSTREAM * init_vgmstream_isb(STREAMFILE *streamFile) {
/* parse base RIFF */
offset = 0x0c;
max_offset = get_streamfile_size(streamFile);
max_offset = get_streamfile_size(sf);
while (offset < max_offset) {
uint32_t chunk_type = read_u32be(offset + 0x00,streamFile);
uint32_t chunk_size = read_s32le(offset + 0x04,streamFile);
uint32_t chunk_type = read_u32ce(offset + 0x00,sf);
uint32_t chunk_size = read_u32me(offset + 0x04,sf);
offset += 0x08;
switch(chunk_type) {
case 0x4C495354: /* "LIST" */
if (read_u32be(offset, streamFile) != 0x73616D70) /* "samp" */
if (read_u32ce(offset, sf) != 0x73616D70) /* "samp" */
break; /* there are "bfob" LIST without data */
total_subsongs++;
@ -72,8 +78,8 @@ VGMSTREAM * init_vgmstream_isb(STREAMFILE *streamFile) {
offset = header_offset + 0x04;
max_offset = offset + header_size;
while (offset < max_offset) {
uint32_t chunk_type = read_u32be(offset + 0x00,streamFile);
uint32_t chunk_size = read_s32le(offset + 0x04,streamFile);
uint32_t chunk_type = read_u32ce(offset + 0x00,sf);
uint32_t chunk_size = read_u32me(offset + 0x04,sf);
offset += 0x08;
switch(chunk_type) {
@ -83,21 +89,21 @@ VGMSTREAM * init_vgmstream_isb(STREAMFILE *streamFile) {
break;
case 0x63686E6B: /* "chnk" */
channel_count = read_u32le(offset + 0x00, streamFile);
channel_count = read_u32me(offset + 0x00, sf);
break;
case 0x73696E66: /* "sinf" */
/* 0x00: null? */
/* 0x04: some value? */
sample_rate = read_u32le(offset + 0x08, streamFile);
pcm_bytes = read_u32le(offset + 0x0c, streamFile);
bps = read_u16le(offset + 0x10, streamFile);
sample_rate = read_u32me(offset + 0x08, sf);
pcm_bytes = read_u32me(offset + 0x0c, sf);
bps = read_u16le(offset + 0x10, sf);
/* 0x12: some value? */
break;
case 0x636D7069: /* "cmpi" */
codec = read_u32le(offset + 0x00, streamFile);
if (read_u32le(offset + 0x04, streamFile) != codec) {
codec = read_u32me(offset + 0x00, sf);
if (read_u32me(offset + 0x04, sf) != codec) {
VGM_LOG("ISB: unknown compression repeat\n");
goto fail;
}
@ -134,11 +140,17 @@ VGMSTREAM * init_vgmstream_isb(STREAMFILE *streamFile) {
vgmstream = allocate_vgmstream(channel_count, loop_flag);
if (!vgmstream) goto fail;
vgmstream->meta_type = meta_WAF; //todo
vgmstream->meta_type = meta_ISB;
vgmstream->sample_rate = sample_rate;
vgmstream->num_streams = total_subsongs;
vgmstream->stream_size = stream_size;
if (name_offset) { /* UTF16 but only uses lower bytes */
if (name_size > STREAM_NAME_SIZE)
name_size = STREAM_NAME_SIZE;
read_string_utf16(vgmstream->stream_name,name_size, name_offset, sf, big_endian);
}
switch(codec) {
case 0x00:
if (bps == 8) {
@ -162,7 +174,7 @@ VGMSTREAM * init_vgmstream_isb(STREAMFILE *streamFile) {
#ifdef VGM_USE_VORBIS
case 0x02:
vgmstream->codec_data = init_ogg_vorbis(streamFile, start_offset, stream_size, NULL);
vgmstream->codec_data = init_ogg_vorbis(sf, start_offset, stream_size, NULL);
if (!vgmstream->codec_data) goto fail;
vgmstream->coding_type = coding_OGG_VORBIS;
vgmstream->layout_type = layout_none;
@ -170,18 +182,64 @@ VGMSTREAM * init_vgmstream_isb(STREAMFILE *streamFile) {
break;
#endif
default: /* according to press releases ISACT may support WMA and XMA */
#ifdef VGM_USE_FFMPEG
case 0x04: {
uint8_t buf[0x100];
size_t bytes;
off_t fmt_offset = start_offset;
size_t fmt_size = 0x20;
start_offset += fmt_size;
stream_size -= fmt_size;
/* XMA1 "fmt" chunk (BE, unlike the usual LE) */
bytes = ffmpeg_make_riff_xma_from_fmt_chunk(buf,sizeof(buf), fmt_offset,fmt_size, stream_size, sf, 1);
vgmstream->codec_data = init_ffmpeg_header_offset(sf, buf,bytes, start_offset, stream_size);
if (!vgmstream->codec_data) goto fail;
vgmstream->coding_type = coding_FFmpeg;
vgmstream->layout_type = layout_none;
vgmstream->num_samples = pcm_bytes / channel_count / (bps/8);
xma_fix_raw_samples(vgmstream, sf, start_offset, stream_size, fmt_offset, 1,1);
break;
}
#endif
case 0x05: {
//TODO: improve
VGMSTREAM *temp_vgmstream = NULL;
STREAMFILE *temp_sf = NULL;
temp_sf = setup_subfile_streamfile(sf, start_offset, stream_size, "msf");
if (!temp_sf) goto fail;
temp_vgmstream = init_vgmstream_msf(temp_sf);
if (temp_vgmstream) {
temp_vgmstream->num_streams = vgmstream->num_streams;
temp_vgmstream->stream_size = vgmstream->stream_size;
temp_vgmstream->meta_type = vgmstream->meta_type;
strcpy(temp_vgmstream->stream_name, vgmstream->stream_name);
close_streamfile(temp_sf);
close_vgmstream(vgmstream);
return temp_vgmstream;
}
else {
close_streamfile(temp_sf);
goto fail;
}
break;
}
default: /* according to press releases ISACT may support WMA */
VGM_LOG("ISB: unknown codec %i\n", codec);
goto fail;
}
if (name_offset) { /* UTF16 but only uses lower bytes */
if (name_size > STREAM_NAME_SIZE)
name_size = STREAM_NAME_SIZE;
read_string_utf16le(vgmstream->stream_name,name_size, name_offset, streamFile);
}
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
if (!vgmstream_open_stream(vgmstream, sf, start_offset))
goto fail;
return vgmstream;

View File

@ -101,7 +101,7 @@ VGMSTREAM * init_vgmstream_ngc_str(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_caf(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_ps2_vpk(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_vpk(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_genh(STREAMFILE *streamFile);
@ -889,5 +889,8 @@ VGMSTREAM * init_vgmstream_kwb(STREAMFILE* sf);
VGMSTREAM * init_vgmstream_lrmd(STREAMFILE* sf);
VGMSTREAM* init_vgmstream_bkhd(STREAMFILE* sf);
VGMSTREAM* init_vgmstream_bkhd_fx(STREAMFILE* sf);
VGMSTREAM* init_vgmstream_encrypted(STREAMFILE* sf);
#endif /*_META_H*/

View File

@ -1,17 +1,22 @@
#include "meta.h"
#include "../layout/layout.h"
#include "../coding/coding.h"
#include "mul_streamfile.h"
typedef enum { NONE, PSX, DSP, XBOX } mul_codec;
typedef enum { PSX, DSP, XBOX, XMA1 } mul_codec;
static int guess_codec(STREAMFILE* sf, int big_endian, int channels, mul_codec* p_codec, off_t* p_extra_offset);
static layered_layout_data* build_layered_mul(STREAMFILE *sf_data, int big_endian, VGMSTREAM* vgmstream);
/* .MUL - from Crystal Dynamics games [Legacy of Kain: Defiance (PS2), Tomb Raider Underworld (multi)] */
VGMSTREAM * init_vgmstream_mul(STREAMFILE *streamFile) {
VGMSTREAM * init_vgmstream_mul(STREAMFILE *sf) {
VGMSTREAM * vgmstream = NULL;
off_t start_offset, coefs_offset = 0;
int loop_flag, channel_count, sample_rate, num_samples, loop_start;
int big_endian;
mul_codec codec = NONE;
int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL;
mul_codec codec;
uint32_t (*read_u32)(off_t,STREAMFILE*) = NULL;
/* checks */
@ -19,21 +24,21 @@ VGMSTREAM * init_vgmstream_mul(STREAMFILE *streamFile) {
* (some files have companion .mus/sam files but seem to be sequences/control stuff)
* .(extensionless): filenames as found in the bigfile
* .emff: fake extension ('Eidos Music File Format') */
if (!check_extensions(streamFile, "mul,,emff"))
if (!check_extensions(sf, "mul,,emff"))
goto fail;
if (read_32bitBE(0x10,streamFile) != 0 ||
read_32bitBE(0x14,streamFile) != 0 ||
read_32bitBE(0x18,streamFile) != 0 ||
read_32bitBE(0x1c,streamFile) != 0)
if (read_u32be(0x10,sf) != 0 ||
read_u32be(0x14,sf) != 0 ||
read_u32be(0x18,sf) != 0 ||
read_u32be(0x1c,sf) != 0)
goto fail;
big_endian = guess_endianness32bit(0x00, streamFile);
read_32bit = big_endian ? read_32bitBE : read_32bitLE;
big_endian = guess_endianness32bit(0x00, sf);
read_u32 = big_endian ? read_u32be : read_u32le;
sample_rate = read_32bit(0x00,streamFile);
loop_start = read_32bit(0x04,streamFile);
num_samples = read_32bit(0x08,streamFile);
channel_count = read_32bit(0x0C,streamFile);
sample_rate = read_u32(0x00,sf);
loop_start = read_u32(0x04,sf);
num_samples = read_u32(0x08,sf);
channel_count = read_u32(0x0C,sf);
if (sample_rate < 8000 || sample_rate > 48000 || channel_count > 8)
goto fail;
/* 0x20: flag when file has non-audio blocks (ignored by the layout) */
@ -41,83 +46,19 @@ VGMSTREAM * init_vgmstream_mul(STREAMFILE *streamFile) {
/* 0x28: loop offset within audio data (not file offset) */
/* 0x2c: some value related to loop? */
/* 0x34: id? */
/* 0x38+: channel config until ~0x100? (multiple 0x3F800000 depending on the number of channels) */
/* 0x38+: channel config until ~0x100? (multiple 0x3F800000 / 1.0f depending on the number of channels) */
/* test known versions (later versions start from 0x24 instead of 0x20) */
if (!(read_32bit(0x38,streamFile) == 0x3F800000 ||
read_32bit(0x3c,streamFile) == 0x3F800000)) /* Tomb Raider Underworld */
if (!(read_u32(0x38,sf) == 0x3F800000 ||
read_u32(0x3c,sf) == 0x3F800000)) /* Tomb Raider Underworld */
goto fail;
loop_flag = (loop_start >= 0); /* 0xFFFFFFFF when not looping */
start_offset = 0x800;
/* format is pretty limited so we need to guess codec */
if (big_endian) {
/* test DSP (GC/Wii): check known coef locations */
if (read_32bitBE(0xC8,streamFile) != 0) { /* Tomb Raider Legend (GC) */
codec = DSP;
coefs_offset = 0xC8;
}
else if (read_32bitBE(0xCC,streamFile) != 0) { /* Tomb Raider Anniversary (Wii) */
codec = DSP;
coefs_offset = 0xCC;
}
else if (read_32bitBE(0x2D0,streamFile) != 0) { /* Tomb Raider Underworld (Wii) */
codec = DSP;
coefs_offset = 0x2D0;
}
// todo test XMA1 (X360): mono streams, each block has 1 sub-blocks of 0x800 packet per channel
// todo test ? (PS3)
}
else {
int i;
off_t offset = start_offset;
size_t file_size = get_streamfile_size(streamFile);
size_t frame_size;
/* check first audio frame */
while (offset < file_size) {
uint32_t block_type = read_32bit(offset+0x00, streamFile);
uint32_t block_size = read_32bit(offset+0x04, streamFile);
uint32_t data_size = read_32bit(offset+0x10, streamFile);
if (block_type != 0x00) {
offset += 0x10 + block_size;
continue; /* not audio */
}
/* test PS-ADPCM (PS2/PSP): flag is always 2 in .mul */
frame_size = 0x10;
for (i = 0; i < data_size / frame_size; i++) {
if (read_8bit(offset + 0x20 + frame_size*i + 0x01, streamFile) != 0x02)
break;
}
if (i == data_size / frame_size) {
codec = PSX;
break;
}
/* test XBOX-IMA (PC/Xbox): reserved frame header value is always 0 */
frame_size = 0x24;
for (i = 0; i < data_size / frame_size; i++) {
if (read_8bit(offset + 0x20 + frame_size*i + 0x03, streamFile) != 0x00)
break;
}
if (i == data_size / frame_size) {
codec = XBOX;
break;
}
break;
}
}
if (codec == NONE) {
VGM_LOG("MUL: unknown codec\n");
if (!guess_codec(sf, big_endian, channel_count, &codec, &coefs_offset))
goto fail;
}
/* build the VGMSTREAM */
@ -141,8 +82,8 @@ VGMSTREAM * init_vgmstream_mul(STREAMFILE *streamFile) {
vgmstream->coding_type = coding_NGC_DSP;
vgmstream->layout_type = layout_blocked_mul;
dsp_read_coefs_be(vgmstream,streamFile,coefs_offset+0x00,0x2e);
dsp_read_hist_be (vgmstream,streamFile,coefs_offset+0x24,0x2e);
dsp_read_coefs_be(vgmstream,sf,coefs_offset+0x00,0x2e);
dsp_read_hist_be (vgmstream,sf,coefs_offset+0x24,0x2e);
break;
case XBOX:
@ -150,11 +91,22 @@ VGMSTREAM * init_vgmstream_mul(STREAMFILE *streamFile) {
vgmstream->layout_type = layout_blocked_mul;
break;
#ifdef VGM_USE_FFMPEG
case XMA1: {
vgmstream->layout_data = build_layered_mul(sf, big_endian, vgmstream);
if (!vgmstream->layout_data) goto fail;
vgmstream->coding_type = coding_FFmpeg;
vgmstream->layout_type = layout_layered;
break;
}
#endif
default:
goto fail;
}
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
if (!vgmstream_open_stream(vgmstream, sf, start_offset))
goto fail;
return vgmstream;
@ -162,3 +114,156 @@ fail:
close_vgmstream(vgmstream);
return NULL;
}
static int guess_codec(STREAMFILE* sf, int big_endian, int channels, mul_codec* p_codec, off_t* p_extra_offset) {
uint32_t (*read_u32)(off_t,STREAMFILE*);
read_u32 = big_endian ? read_u32be : read_u32le;
*p_extra_offset = 0;
if (big_endian) {
/* test DSP (GC/Wii): check known coef locations */
if (read_u32be(0xC8,sf) != 0) { /* Tomb Raider Legend (GC) */
*p_codec = DSP;
*p_extra_offset = 0xC8;
return 1;
}
else if (read_u32be(0xCC,sf) != 0) { /* Tomb Raider Anniversary (Wii) */
*p_codec = DSP;
*p_extra_offset = 0xCC;
return 1;
}
else if (read_u32be(0x2D0,sf) != 0) { /* Tomb Raider Underworld (Wii) */
*p_codec = DSP;
*p_extra_offset = 0x2D0;
return 1;
}
else { //if (ps_check_format(sf, 0x820, 0x100)) {
/* may be PS3/X360, tested below [Tomb Raider 7 (PS3)] */
}
// todo test XMA1 (X360): N mono streams (layered), each block has 1 sub-blocks of 0x800 packet per channel
}
{
int i;
off_t offset = 0x800;
size_t file_size = get_streamfile_size(sf);
size_t frame_size;
/* check first audio frame */
while (offset < file_size) {
uint32_t block_type = read_u32(offset+0x00, sf);
uint32_t block_size = read_u32(offset+0x04, sf);
uint32_t data_size = read_u32(offset+0x10, sf);
if (block_type != 0x00) {
offset += 0x10 + block_size;
continue; /* not audio */
}
/* test XMA1 (X360): has sub-blocks of 0x800 per channel */
if (block_size == 0x810 * channels) {
for (i = 0; i < channels; i++) {
off_t test_offset = offset + 0x10 + 0x810*i;
if (read_u32(test_offset + 0x00, sf) != 0x800 || /* XMA packet size */
read_u32(test_offset + 0x10, sf) != 0x08000000) /* XMA packet first header */
break;
}
if (i == channels) {
*p_codec = XMA1;
return 1;
}
}
/* test PS-ADPCM (PS2/PSP): flag is always 2 in .mul */
frame_size = 0x10;
for (i = 0; i < data_size / frame_size; i++) {
if (read_8bit(offset + 0x20 + frame_size*i + 0x01, sf) != 0x02)
break;
}
if (i == data_size / frame_size) {
*p_codec = PSX;
return 1;
}
/* test XBOX-IMA (PC/Xbox): reserved frame header value is always 0 */
frame_size = 0x24;
for (i = 0; i < data_size / frame_size; i++) {
if (read_8bit(offset + 0x20 + frame_size*i + 0x03, sf) != 0x00)
break;
}
if (i == data_size / frame_size) {
*p_codec = XBOX;
return 1;
}
break;
}
}
return 0;
}
/* MUL contain one XMA1 streams per channel so we need the usual voodoo */
static layered_layout_data* build_layered_mul(STREAMFILE *sf_data, int big_endian, VGMSTREAM* vgmstream) {
layered_layout_data* data = NULL;
STREAMFILE* temp_sf = NULL;
int i, layers = vgmstream->channels;
/* init layout */
data = init_layout_layered(layers);
if (!data) goto fail;
for (i = 0; i < layers; i++) {
int layer_channels = 1;
/* build the layer VGMSTREAM */
data->layers[i] = allocate_vgmstream(layer_channels, 0);
if (!data->layers[i]) goto fail;
data->layers[i]->sample_rate = vgmstream->sample_rate;
data->layers[i]->num_samples = vgmstream->num_samples;
#ifdef VGM_USE_FFMPEG
{
uint8_t buf[0x100];
int bytes;
size_t stream_size;
temp_sf = setup_mul_streamfile(sf_data, big_endian, i, layers);
if (!temp_sf) goto fail;
stream_size = get_streamfile_size(temp_sf);
bytes = ffmpeg_make_riff_xma1(buf, 0x100, data->layers[i]->num_samples, stream_size, data->layers[i]->channels, data->layers[i]->sample_rate, 0);
data->layers[i]->codec_data = init_ffmpeg_header_offset(temp_sf, buf,bytes, 0x00, stream_size);
if (!data->layers[i]->codec_data) goto fail;
data->layers[i]->coding_type = coding_FFmpeg;
data->layers[i]->layout_type = layout_none;
data->layers[i]->stream_size = stream_size;
xma_fix_raw_samples(data->layers[i], temp_sf, 0x00,stream_size, 0, 0,0); /* ? */
close_streamfile(temp_sf);
temp_sf = NULL;
}
#else
goto fail;
#endif
}
if (!setup_layout_layered(data))
goto fail;
return data;
fail:
close_streamfile(temp_sf);
free_layout_layered(data);
return NULL;
}

65
src/meta/mul_streamfile.h Normal file
View File

@ -0,0 +1,65 @@
#ifndef _MUL_STREAMFILE_H_
#define _MUL_STREAMFILE_H_
#include "deblock_streamfile.h"
static void block_callback(STREAMFILE* sf, deblock_io_data* data) {
uint32_t block_type;
size_t block_size;
uint32_t (*read_u32)(off_t,STREAMFILE*) = data->cfg.big_endian ? read_u32be : read_u32le;
if (data->physical_offset == 0) {
data->block_size = 0x800;
data->data_size = 0;
data->skip_size = 0;
return;
}
block_type = read_u32(data->physical_offset + 0x00, sf);
block_size = read_u32(data->physical_offset + 0x04, sf); /* not including main header */
/* some blocks only contain half of data (continues in next block) so use track numbers */
if (block_type == 0x00 && block_size != 0) {
/* header block */
data->block_size = 0x10;
data->data_size = 0;
data->skip_size = 0;
}
else if (block_type == 0x00000800) {
data->block_size = 0x810;
/* actually sub-block with size + number, kinda half-assed but meh... */
if (block_size == data->cfg.track_number) {
data->data_size = 0x800;
data->skip_size = 0x10;
}
else{
data->data_size = 0;
data->skip_size = 0;
}
}
else {
/* non-audio block */
data->block_size = block_size + 0x10;
data->data_size = 0;
data->skip_size = 0;
}
}
/* Deinterleaves MUL streams */
static STREAMFILE* setup_mul_streamfile(STREAMFILE* sf, int big_endian, int track_number, int track_count) {
STREAMFILE *new_sf = NULL;
deblock_config_t cfg = {0};
cfg.big_endian = big_endian;
cfg.track_number = track_number;
cfg.track_count = track_count;
cfg.block_callback = block_callback;
new_sf = open_wrap_streamfile(sf);
new_sf = open_io_deblock_streamfile_f(new_sf, &cfg);
return new_sf;
}
#endif /* _MUL_STREAMFILE_H_ */

View File

@ -36,7 +36,8 @@ typedef enum {
PCM4_U = 26, /* 4-bit unsigned PCM (3rd and 4th gen games) */
OKI16 = 27, /* OKI ADPCM with 16-bit output (unlike OKI/VOX/Dialogic ADPCM's 12-bit) */
AAC = 28, /* Advanced Audio Coding (raw without .mp4) */
TGC = 29 /* Tiger Game.com 4-bit ADPCM */
TGC = 29, /* Tiger Game.com 4-bit ADPCM */
ASF = 30, /* Argonaut ASF 4-bit ADPCM */
} txth_type;
typedef struct {
@ -223,6 +224,7 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) {
case PCM4_U: coding = coding_PCM4_U; break;
case OKI16: coding = coding_OKI16; break;
case TGC: coding = coding_TGC; break;
case ASF: coding = coding_ASF; break;
default:
goto fail;
}
@ -334,6 +336,11 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) {
vgmstream->layout_type = layout_none;
break;
case coding_ASF:
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = 0x11;
break;
case coding_MS_IMA:
if (!txth.interleave) goto fail; /* creates garbage */
@ -450,7 +457,7 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) {
}
else {
/* fake header FFmpeg */
uint8_t buf[200];
uint8_t buf[0x100];
int32_t bytes;
if (txth.codec == ATRAC3) {
@ -465,14 +472,14 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) {
else if (txth.codec == ATRAC3PLUS) {
int block_size = txth.interleave;
bytes = ffmpeg_make_riff_atrac3plus(buf, 200, vgmstream->num_samples, txth.data_size, vgmstream->channels, vgmstream->sample_rate, block_size, txth.skip_samples);
bytes = ffmpeg_make_riff_atrac3plus(buf, sizeof(buf), vgmstream->num_samples, txth.data_size, vgmstream->channels, vgmstream->sample_rate, block_size, txth.skip_samples);
ffmpeg_data = init_ffmpeg_header_offset(txth.streamBody, buf,bytes, txth.start_offset,txth.data_size);
if ( !ffmpeg_data ) goto fail;
}
else if (txth.codec == XMA1) {
int xma_stream_mode = txth.codec_mode == 1 ? 1 : 0;
bytes = ffmpeg_make_riff_xma1(buf, 100, vgmstream->num_samples, txth.data_size, vgmstream->channels, vgmstream->sample_rate, xma_stream_mode);
bytes = ffmpeg_make_riff_xma1(buf, sizeof(buf), vgmstream->num_samples, txth.data_size, vgmstream->channels, vgmstream->sample_rate, xma_stream_mode);
ffmpeg_data = init_ffmpeg_header_offset(txth.streamBody, buf,bytes, txth.start_offset,txth.data_size);
if ( !ffmpeg_data ) goto fail;
}
@ -482,7 +489,7 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) {
block_size = txth.interleave ? txth.interleave : 2048;
block_count = txth.data_size / block_size;
bytes = ffmpeg_make_riff_xma2(buf, 200, vgmstream->num_samples, txth.data_size, vgmstream->channels, vgmstream->sample_rate, block_count, block_size);
bytes = ffmpeg_make_riff_xma2(buf, sizeof(buf), vgmstream->num_samples, txth.data_size, vgmstream->channels, vgmstream->sample_rate, block_count, block_size);
ffmpeg_data = init_ffmpeg_header_offset(txth.streamBody, buf,bytes, txth.start_offset,txth.data_size);
if ( !ffmpeg_data ) goto fail;
}
@ -848,6 +855,7 @@ static int parse_keyval(STREAMFILE * streamFile_, txth_header * txth, const char
else if (is_string(val,"AAC")) txth->codec = AAC;
else if (is_string(val,"TGC")) txth->codec = TGC;
else if (is_string(val,"GCOM_ADPCM")) txth->codec = TGC;
else if (is_string(val,"ASF")) txth->codec = ASF;
else goto fail;
/* set common interleaves to simplify usage
@ -1730,6 +1738,8 @@ static int get_bytes_to_samples(txth_header * txth, uint32_t bytes) {
#endif
case AC3:
return ac3_bytes_to_samples(bytes, txth->interleave, txth->channels);
case ASF:
return asf_bytes_to_samples(bytes, txth->channels);
/* XMA bytes-to-samples is done at the end as the value meanings are a bit different */
case XMA1:

View File

@ -2,58 +2,167 @@
#include "../coding/coding.h"
/* CKD RIFF - Ubisoft audio [Rayman Origins (Wii)] */
VGMSTREAM * init_vgmstream_ubi_ckd(STREAMFILE *streamFile) {
typedef enum { MSADPCM, DSP, MP3, XMA2 } ckd_codec;
/* CKD RIFF - UbiArt Framework (v1) audio [Rayman Origins (Wii/X360/PS3/PC)] */
VGMSTREAM* init_vgmstream_ubi_ckd(STREAMFILE* sf) {
VGMSTREAM* vgmstream = NULL;
off_t start_offset, first_offset = 0xc, chunk_offset;
off_t start_offset, first_offset = 0x0c, chunk_offset;
size_t chunk_size, data_size;
int loop_flag, channel_count, interleave;
int loop_flag, channel_count, interleave = 0, format;
ckd_codec codec;
int big_endian;
uint32_t (*read_u32)(off_t,STREAMFILE*);
uint16_t (*read_u16)(off_t,STREAMFILE*);
/* check extension, case insensitive */
if (!check_extensions(streamFile,"ckd")) goto fail;
/* check header */
if (read_32bitBE(0x0,streamFile) != 0x52494646) /* RIFF */
/* checks */
/* .wav.ckd: main (other files are called .xxx.ckd too) */
if (!check_extensions(sf,"ckd"))
goto fail;
if (read_32bitBE(0x26,streamFile) != 0x6473704C) /* dspL */
/* another slighly funny RIFF */
if (read_u32be(0x00,sf) != 0x52494646) /* "RIFF" */
goto fail;
if (read_u32be(0x08,sf) != 0x57415645) /* "WAVE" */
goto fail;
/* RIFF size is normal */
big_endian = guess_endianness32bit(0x04, sf);
read_u32 = big_endian ? read_u32be : read_u32le;
read_u16 = big_endian ? read_u16be : read_u16le;
loop_flag = 0;
channel_count = read_16bitBE(0x16,streamFile);
format = read_u16(0x14,sf);
channel_count = read_u16(0x16,sf);
/* find data chunk, in 3 variants */
if (find_chunk_be(streamFile, 0x64617453,first_offset,0, &chunk_offset,&chunk_size)) { /*"datS"*/
switch(format) {
case 0x0002:
if (big_endian) {
if (read_u32(0x26,sf) != 0x6473704C) /* "dspL" */
goto fail;
/* find data chunk, in 2 variants */
if (find_chunk_be(sf, 0x64617453,first_offset,0, &chunk_offset,&chunk_size)) { /* "datS" */
/* normal interleave */
start_offset = chunk_offset;
data_size = chunk_size;
interleave = 8;
} else if (find_chunk_be(streamFile, 0x6461744C,first_offset,0, &chunk_offset,&chunk_size)) { /*"datL"*/
/* mono or full interleave (with a "datR" after the "datL", no check as we can just pretend it exists) */
interleave = 0x08;
} else if (find_chunk_be(sf, 0x6461744C,first_offset,0, &chunk_offset,&chunk_size)) { /* "datL" */
/* mono "datL" or full interleave with a "datR" after the "datL" (no check, pretend it exists) */
start_offset = chunk_offset;
data_size = chunk_size * channel_count;
interleave = (4+4) + chunk_size; /* don't forget to skip the "datR"+size chunk */
interleave = (0x4+0x4) + chunk_size; /* don't forget to skip the "datR"+size chunk */
} else {
goto fail;
}
codec = DSP;
}
else {
/* PC has MS-ADPCM, same as wav's except without "fact" (recommended by MS), kinda useless
* but might as well have it here */
if (find_chunk_le(sf, 0x64617461,first_offset,0, &chunk_offset,&chunk_size)) { /* "data" */
start_offset = chunk_offset;
data_size = chunk_size;
} else {
goto fail;
}
interleave = read_u16(0x20, sf);
if (!msadpcm_check_coefs(sf, 0x28))
goto fail;
/* there is also a "smpl" chunk with full loops too, but other codecs don't have it for the same tracks... */
codec = MSADPCM;
}
break;
case 0x0055:
if (read_u32(0x26,sf) != 0x6D736620) /* "msf " */
goto fail;
start_offset = 0x26;
data_size = read_u32(0x2A,sf);
codec = MP3;
break;
case 0x0166:
if (read_u32(0x48,sf) != 0x7365656B && /* "seek */
read_u32(0x48,sf) != 0x7365656B) /* "data" */
goto fail;
if (find_chunk_be(sf, 0x64617461,first_offset,0, &chunk_offset,&chunk_size)) { /* "data" */
start_offset = chunk_offset;
data_size = chunk_size;
} else {
goto fail;
}
codec = XMA2;
break;
default:
goto fail;
}
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail;
vgmstream->sample_rate = read_32bitBE(0x18,streamFile);
vgmstream->sample_rate = read_u32(0x18,sf);
vgmstream->num_samples = dsp_bytes_to_samples(data_size, channel_count);
vgmstream->coding_type = coding_NGC_DSP;
vgmstream->layout_type = channel_count==1 ? layout_none : layout_interleave;
vgmstream->interleave_block_size = interleave;
vgmstream->meta_type = meta_UBI_CKD;
dsp_read_coefs_be(vgmstream,streamFile, 0x4A, (4+4)+0x60);
switch(codec) {
case MSADPCM:
vgmstream->coding_type = coding_MSADPCM;
vgmstream->layout_type = layout_none;
vgmstream->frame_size = interleave;
break;
case DSP:
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = interleave;
dsp_read_coefs_be(vgmstream,sf, 0x4A, (4+4)+0x60);
break;
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
#ifdef VGM_USE_MPEG
case MP3: {
vgmstream->codec_data = init_mpeg(sf, start_offset, &vgmstream->coding_type, vgmstream->channels);
if (!vgmstream->codec_data) goto fail;
vgmstream->layout_type = layout_none;
vgmstream->num_samples = mpeg_bytes_to_samples(data_size, vgmstream->codec_data);
break;
}
#endif
#ifdef VGM_USE_FFMPEG
case XMA2: {
uint8_t buf[0x100];
int bytes;
bytes = ffmpeg_make_riff_xma_from_fmt_chunk(buf, sizeof(buf), 0x14, 0x34, data_size, sf, 1);
vgmstream->codec_data = init_ffmpeg_header_offset(sf, buf,bytes, start_offset,data_size);
if (!vgmstream->codec_data) goto fail;
vgmstream->coding_type = coding_FFmpeg;
vgmstream->layout_type = layout_none;
vgmstream->num_samples = read_u32(0x14+0x18,sf);
xma_fix_raw_samples(vgmstream, sf, start_offset,data_size, 0, 0,0); /* should apply to num_samples? */
break;
}
#endif
default:
goto fail;
}
if (!vgmstream_open_stream(vgmstream,sf,start_offset))
goto fail;
return vgmstream;

View File

@ -7,7 +7,7 @@
#define SB_MAX_LAYER_COUNT 16 /* arbitrary max */
#define SB_MAX_CHAIN_COUNT 256 /* +150 exist in Tonic Trouble */
typedef enum { UBI_IMA, UBI_ADPCM, RAW_PCM, RAW_PSX, RAW_DSP, RAW_XBOX, FMT_VAG, FMT_AT3, RAW_AT3, FMT_XMA1, RAW_XMA1, FMT_OGG, FMT_CWAV, FMT_APM, FMT_MPDX } ubi_sb_codec;
typedef enum { UBI_IMA, UBI_ADPCM, RAW_PCM, RAW_PSX, RAW_DSP, RAW_XBOX, FMT_VAG, FMT_AT3, RAW_AT3, FMT_XMA1, RAW_XMA1, FMT_OGG, FMT_CWAV, FMT_APM, FMT_MPDX, UBI_IMA_SCE } ubi_sb_codec;
typedef enum { UBI_PC, UBI_PS2, UBI_XBOX, UBI_GC, UBI_X360, UBI_PSP, UBI_PS3, UBI_WII, UBI_3DS } ubi_sb_platform;
typedef enum { UBI_NONE = 0, UBI_AUDIO, UBI_LAYER, UBI_SEQUENCE, UBI_SILENCE } ubi_sb_type;
@ -496,6 +496,16 @@ static VGMSTREAM * init_vgmstream_ubi_sb_base(ubi_sb_header *sb, STREAMFILE *str
vgmstream->layout_type = layout_none;
break;
case UBI_IMA_SCE:
vgmstream->coding_type = coding_UBI_IMA;
vgmstream->layout_type = layout_blocked_ubi_sce;
vgmstream->full_block_size = read_32bitLE(0x18, streamData);
/* this "codec" is an ugly hack of IMA w/ Ubi ADPCM's frame format, surely to
* shoehorn a simpler codec into the existing code when porting the game */
start_offset += 0x08 + 0x30; /* skip Ubi ADPCM header */
break;
case UBI_ADPCM:
/* custom Ubi 4/6-bit ADPCM used in early games:
* - Splinter Cell (PC): 4-bit w/ 2ch (music), 6-bit w/ 1ch (sfx)
@ -1553,10 +1563,9 @@ static int parse_stream_codec(ubi_sb_header * sb) {
case UBI_PS3:
sb->codec = RAW_PSX; /* PS3 */
break;
case UBI_PSP:
/* TODO: IMA using Ubisoft ADPCM frame layout [Splinter Cell: Essentials (PSP)] */
VGM_LOG("UBI SB: Unimplemented custom IMA codec.\n");
goto fail;
case UBI_PSP: /* Splinter Cell: Essentials (PSP) */
sb->codec = UBI_IMA_SCE;
break;
default:
sb->codec = UBI_ADPCM;
break;
@ -2704,6 +2713,19 @@ static int config_sb_version(ubi_sb_header * sb, STREAMFILE *streamFile) {
return 1;
}
/* Tom Clancy's Ghost Recon Advanced Warfighter (2006)(Xbox)-bank */
if (sb->version == 0x00130004 && sb->platform == UBI_XBOX) {
config_sb_entry(sb, 0x48, 0x50);
config_sb_audio_fb(sb, 0x1c, (1 << 3), (1 << 4), (1 << 10));
config_sb_audio_he(sb, 0x3c, 0x34, 0x20, 0x28, 0x44, 0x40);
/* what */
sb->cfg.audio_extra_offset = 0x10;
sb->cfg.audio_stream_offset = 0x14;
return 1;
}
/* Prince of Persia: The Two Thrones (2005)(PC)-bank */
if (sb->version == 0x00150000 && sb->platform == UBI_PC) {
config_sb_entry(sb, 0x68, 0x78);

View File

@ -2,7 +2,7 @@
#include "../coding/coding.h"
/* VPK - from SCE America second party devs [God of War (PS2), NBA 08 (PS3)] */
VGMSTREAM * init_vgmstream_ps2_vpk(STREAMFILE *streamFile) {
VGMSTREAM * init_vgmstream_vpk(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
int loop_flag, channel_count;
off_t start_offset, loop_channel_offset;
@ -16,12 +16,14 @@ VGMSTREAM * init_vgmstream_ps2_vpk(STREAMFILE *streamFile) {
if (read_32bitBE(0x00,streamFile) != 0x204B5056) /* " KPV" */
goto fail;
/* seems this consistently has 0x10-0x20 extra bytes, landing in garbage 0xC00000..00 frames at the end */
channel_size = read_32bitLE(0x04,streamFile) - 0x20; /* remove for cleaner ends */
/* files are padded with garbage/silent 0xC00000..00 frames, and channel_size sometimes
* has extra size into the padding: +0x10 (NBA08), +0x20 (GoW), or none (Sly 2, loops ok).
* Could detect and remove to slightly improve full loops, but maybe this is just how the game works */
channel_size = read_32bitLE(0x04,streamFile);
start_offset = read_32bitLE(0x08,streamFile);
channel_count = read_32bitLE(0x14,streamFile);
/* 0x18+(per channel): channel config(?) */
/* 0x18+: channel config(?), 0x04 per channel */
loop_channel_offset = read_32bitLE(0x7FC,streamFile);
loop_flag = (loop_channel_offset != 0); /* found in Sly 2/3 */

View File

@ -1007,11 +1007,14 @@ fail:
if (buf) buf[0] = '\0';
return 0;
}
size_t read_string_utf16le(char *buf, size_t buf_size, off_t offset, STREAMFILE *sf) {
size_t read_string_utf16(char *buf, size_t buf_size, off_t offset, STREAMFILE *sf, int big_endian) {
size_t pos, offpos;
uint16_t (*read_u16)(off_t,STREAMFILE*) = big_endian ? read_u16be : read_u16le;
for (pos = 0, offpos = 0; pos < buf_size; pos++, offpos += 2) {
char c = read_u16le(offset + offpos, sf) & 0xFF; /* lower byte for now */
char c = read_u16(offset + offpos, sf) & 0xFF; /* lower byte for now */
if (buf) buf[pos] = c;
if (c == '\0')
return pos;
@ -1028,6 +1031,13 @@ fail:
return 0;
}
size_t read_string_utf16le(char* buf, size_t buf_size, off_t offset, STREAMFILE* sf) {
return read_string_utf16(buf, buf_size, offset, sf, 0);
}
size_t read_string_utf16be(char* buf, size_t buf_size, off_t offset, STREAMFILE* sf) {
return read_string_utf16(buf, buf_size, offset, sf, 1);
}
size_t read_key_file(uint8_t *buf, size_t buf_size, STREAMFILE *sf) {

View File

@ -336,7 +336,9 @@ size_t read_line(char *buf, int buf_size, off_t offset, STREAMFILE *sf, int *p_l
/* reads a c-string (ANSI only), up to bufsize or NULL, returning size. buf is optional (works as get_string_size). */
size_t read_string(char *buf, size_t buf_size, off_t offset, STREAMFILE *sf);
/* reads a UTF16 string... but actually only as ANSI (discards the upper byte) */
size_t read_string_utf16(char* buf, size_t buf_size, off_t offset, STREAMFILE* sf, int big_endian);
size_t read_string_utf16le(char* buf, size_t buf_size, off_t offset, STREAMFILE* sf);
size_t read_string_utf16be(char* buf, size_t buf_size, off_t offset, STREAMFILE* sf);
/* Opens a file containing decryption keys and copies to buffer.
* Tries "(name.ext)key" (per song), "(.ext)key" (per folder) keynames.

View File

@ -58,7 +58,7 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = {
init_vgmstream_ngc_str,
init_vgmstream_ea_schl,
init_vgmstream_caf,
init_vgmstream_ps2_vpk,
init_vgmstream_vpk,
init_vgmstream_genh,
#ifdef VGM_USE_VORBIS
init_vgmstream_ogg_vorbis,
@ -491,9 +491,11 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = {
init_vgmstream_kwb,
init_vgmstream_lrmd,
init_vgmstream_bkhd,
init_vgmstream_bkhd_fx,
/* lowest priority metas (should go after all metas, and TXTH should go before raw formats) */
init_vgmstream_txth, /* proper parsers should supersede TXTH, once added */
init_vgmstream_encrypted, /* encrypted stuff */
init_vgmstream_raw_int, /* .int raw PCM */
init_vgmstream_ps_headerless, /* tries to detect a bunch of PS-ADPCM formats */
init_vgmstream_raw_snds, /* .snds raw SNDS IMA (*after* ps_headerless) */
@ -1108,6 +1110,7 @@ void render_vgmstream(sample_t * buffer, int32_t sample_count, VGMSTREAM * vgmst
case layout_blocked_xa_aiff:
case layout_blocked_vs_square:
case layout_blocked_vid1:
case layout_blocked_ubi_sce:
render_vgmstream_blocked(buffer,sample_count,vgmstream);
break;
case layout_segmented:
@ -1946,7 +1949,7 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to
case coding_UBI_IMA:
for (ch = 0; ch < vgmstream->channels; ch++) {
decode_ubi_ima(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch,
vgmstream->channels,vgmstream->samples_into_block,samples_to_do, ch);
vgmstream->channels,vgmstream->samples_into_block,samples_to_do, ch, vgmstream->codec_config);
}
break;
case coding_H4M_IMA:

View File

@ -275,6 +275,7 @@ typedef enum {
layout_blocked_xa_aiff, /* XA in AIFF files [Crusader: No Remorse (SAT), Road Rash (3DO)] */
layout_blocked_vs_square,
layout_blocked_vid1,
layout_blocked_ubi_sce,
/* otherwise odd */
layout_segmented, /* song divided in segments (song sections) */
@ -724,6 +725,7 @@ typedef enum {
meta_TGC,
meta_KWB,
meta_LRMD,
meta_WWISE_FX,
} meta_t;
/* standard WAVEFORMATEXTENSIBLE speaker positions */