mirror of
https://github.com/vgmstream/vgmstream.git
synced 2024-11-15 02:57:38 +01:00
commit
994ef88409
@ -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;
|
||||
|
44
doc/TXTH.md
44
doc/TXTH.md
@ -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
|
||||
```
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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 ?
|
||||
|
@ -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"},
|
||||
|
||||
};
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
57
src/layout/blocked_ubi_sce.c
Normal file
57
src/layout/blocked_ubi_sce.c
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
|
@ -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"
|
||||
|
@ -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" />
|
||||
|
@ -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>
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
|
@ -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))
|
||||
|
@ -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 */
|
||||
{
|
||||
|
@ -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
83
src/meta/encrypted.c
Normal 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;
|
||||
}
|
118
src/meta/isb.c
118
src/meta/isb.c
@ -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;
|
||||
|
||||
|
@ -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*/
|
||||
|
277
src/meta/mul.c
277
src/meta/mul.c
@ -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
65
src/meta/mul_streamfile.h
Normal 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_ */
|
@ -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:
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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 */
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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.
|
||||
|
@ -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:
|
||||
|
@ -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 */
|
||||
|
Loading…
Reference in New Issue
Block a user