Merge pull request #469 from bnnm/ubi-hx-nub-etc

Ubi hx nub etc
This commit is contained in:
bnnm 2019-09-08 21:54:07 +02:00 committed by GitHub
commit 0fe6f3c31e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 937 additions and 88 deletions

View File

@ -134,6 +134,7 @@ them playable through vgmstream.
- .aif to .laif or .aiffl or .aifcl (standard Mac AIF, Asobo AIF, Ogg)
- .aiff/aifc to .aiffl/aifcl (standard Mac AIF)
- .asf to .lasf (EA games, Argonaut ASF)
- .bin to .lbin (various)
- .flac to .lflac (standard FLAC)
- .mp2 to .lmp2 (standard MP2)
- .mp3 to .lmp3 (standard MP3)
@ -343,8 +344,8 @@ are used in few games.
- Argonaut ASF 4-bit ADPCM
- Ocean DSA 4-bit ADPCM
- Circus XPCM ADPCM
- OKI 4-bit ADPCM (16-bit output, PC-FX)
- Ocean DSA 4-bit ADPCM
- OKI 4-bit ADPCM (16-bit output, 4-shift, PC-FX)
- Ubisoft 4/6-bit ADPCM
- SDX2 2:1 Squareroot-Delta-Exact compression DPCM
- CBD2 2:1 Cuberoot-Delta-Exact compression DPCM
- Activision EXAKT SASSC DPCM

View File

@ -59,6 +59,7 @@ Requires MSVC (foobar/SDK only links to MSVC C++ DLLs) and these dependencies:
- FDK-AAC, in *(vgmstream)/dependencies/fdk-aac/*: https://github.com/kode54/fdk-aac
- QAAC, in *(vgmstream)/dependencies/qaac/*: https://github.com/kode54/qaac
- WTL (if needed), in *(vgmstream)/dependencies/WTL/*: http://wtl.sourceforge.net/
- may need to install ATL and MFC libraries (can be installed in Visual Studio Installer)
The following project modifications are required:
- For *foobar2000_ATL_helpers* add *../../../WTL/Include* to the compilers's *additional includes*

View File

@ -181,6 +181,7 @@ void decode_circus_adpcm(VGMSTREAMCHANNEL * stream, sample * outbuf, int channel
/* oki_decoder */
void decode_pcfx(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int mode);
void decode_oki16(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel);
void decode_oki4s(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel);
size_t oki_bytes_to_samples(size_t bytes, int channels);
/* ptadpcm_decoder */
@ -193,6 +194,7 @@ void decode_ubi_adpcm(VGMSTREAM * vgmstream, sample_t * outbuf, int32_t samples_
void reset_ubi_adpcm(ubi_adpcm_codec_data *data);
void seek_ubi_adpcm(ubi_adpcm_codec_data *data, int32_t num_sample);
void free_ubi_adpcm(ubi_adpcm_codec_data *data);
int ubi_adpcm_get_samples(ubi_adpcm_codec_data *data);
/* ea_mt_decoder*/
ea_mt_codec_data *init_ea_mt(int channels, int type);

View File

@ -2,10 +2,13 @@
static const int step_sizes[49] = { /* OKI table (subsection of IMA's table) */
16, 17, 19, 21, 23, 25, 28, 31, 34, 37, 41, 45, 50,
55, 60, 66, 73, 80, 88, 97, 107, 118, 130, 143, 157,
173, 190, 209, 230, 253, 279, 307, 337, 371, 408, 449,
494, 544, 598, 658, 724, 796, 876, 963, 1060, 1166, 1282, 1411, 1552
16, 17, 19, 21, 23, 25, 28, 31,
34, 37, 41, 45, 50, 55, 60, 66,
73, 80, 88, 97, 107, 118, 130, 143,
157, 173, 190, 209, 230, 253, 279, 307,
337, 371, 408, 449, 494, 544, 598, 658,
724, 796, 876, 963, 1060, 1166, 1282, 1411,
1552
};
static const int stex_indexes[16] = { /* OKI table (also from IMA) */
@ -60,8 +63,9 @@ static void oki16_expand_nibble(VGMSTREAMCHANNEL * stream, off_t byte_offset, in
code = (read_8bit(byte_offset,stream->streamfile) >> nibble_shift) & 0xf;
step = step_sizes[*step_index];
/* IMA 'mul' style (standard OKI uses 'shift-add') */
delta = (code & 0x7);
delta = (((delta * 2) + 1) * step) >> 3; /* IMA 'mul' style (standard OKI uses 'shift-add') */
delta = (((delta * 2) + 1) * step) >> 3;
if (code & 0x8)
delta = -delta;
*hist1 += delta;
@ -75,6 +79,31 @@ static void oki16_expand_nibble(VGMSTREAMCHANNEL * stream, off_t byte_offset, in
*out_sample = *hist1;
}
static void oki4s_expand_nibble(VGMSTREAMCHANNEL * stream, off_t byte_offset, int nibble_shift, int32_t * hist1, int32_t * step_index, int16_t *out_sample) {
int code, step, delta;
code = (read_8bit(byte_offset,stream->streamfile) >> nibble_shift) & 0xf;
step = step_sizes[*step_index];
step = step << 4; /* original table has precomputed step_sizes so that this isn't done */
/* IMA 'shift-add' style (like standard OKI) */
delta = step >> 3;
if (code & 1) delta += step >> 2;
if (code & 2) delta += step >> 1;
if (code & 4) delta += step;
if (code & 8) delta = -delta;
*hist1 += delta;
*hist1 = clamp16(*hist1); /* standard OKI clamps hist to 2047,-2048 here */
*step_index += stex_indexes[code];
if (*step_index < 0) *step_index = 0;
if (*step_index > 48) *step_index = 48;
*out_sample = *hist1;
}
/* PC-FX ADPCM decoding, variation of OKI/Dialogic/VOX ADPCM. Based on mednafen/pcfx-music-dump.
* Apparently most ADPCM was made with a buggy encoder, resulting in incorrect sound in real hardware
* and sound clipped at half. Decoding can be controlled with modes:
@ -138,6 +167,37 @@ void decode_oki16(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspaci
stream->adpcm_step_index = step_index;
}
/* OKI variation with 16-bit output (vs standard's 12-bit) and pre-adjusted tables (shifted by 4), found in Jubeat Clan (AC).
* Reverse engineered from the DLLs. */
void decode_oki4s(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) {
int i, sample_count = 0;
int32_t hist1 = stream->adpcm_history1_32;
int step_index = stream->adpcm_step_index;
int16_t out_sample;
int is_stereo = channelspacing > 1;
/* external interleave */
/* no header (external setup), pre-clamp for wrong values */
if (step_index < 0) step_index=0;
if (step_index > 48) step_index=48;
/* decode nibbles (layout: varies) */
for (i = first_sample; i < first_sample + samples_to_do; i++, sample_count += channelspacing) {
off_t byte_offset = is_stereo ?
stream->offset + i : /* stereo: one nibble per channel */
stream->offset + i/2; /* mono: consecutive nibbles (assumed) */
int nibble_shift =
is_stereo ? (!(channel&1) ? 0:4) : (!(i&1) ? 0:4); /* even = low, odd = high */
oki4s_expand_nibble(stream, byte_offset,nibble_shift, &hist1, &step_index, &out_sample);
outbuf[sample_count] = (out_sample);
}
stream->adpcm_history1_32 = hist1;
stream->adpcm_step_index = step_index;
}
size_t oki_bytes_to_samples(size_t bytes, int channels) {
if (channels <= 0) return 0;

View File

@ -95,21 +95,19 @@ ubi_adpcm_codec_data *init_ubi_adpcm(STREAMFILE *sf, off_t offset, int channels)
data = calloc(1, sizeof(ubi_adpcm_codec_data));
if (!data) goto fail;
if (!parse_header(sf, data, offset))
if (!parse_header(sf, data, offset)) {
VGM_LOG("UBI ADPCM: wrong header\n");
goto fail;
}
if (data->header.channels != channels)
if (data->header.channels != channels) {
VGM_LOG("UBI ADPCM: wrong number of channels: %i vs %i\n", data->header.channels, channels);
goto fail;
}
data->start_offset = offset + 0x30;
data->offset = data->start_offset;
//todo untested
if (data->header.bits_per_sample == 6 && data->header.channels == 2) {
VGM_LOG("UBI ADPCM: found 6-bit stereo\n");
goto fail;
}
return data;
fail:
free_ubi_adpcm(data);
@ -296,11 +294,11 @@ static int32_t clamp_val(int32_t val, int32_t min, int32_t max) {
static int16_t expand_code_6bit(uint8_t code, ubi_adpcm_channel_data* state) {
int step0_index;
int32_t step0_next, step0, delta0;
int32_t code_signed, step0_next, step0, delta0;
int32_t sample_new;
step0_index = abs(code - 31); /* 0..32, but should only go up to 31 */
code_signed = (int32_t)code - 31; /* convert coded 0..63 value to signed value, where 0=-31 .. 31=0 .. 63=32 */
step0_index = abs(code_signed); /* 0..32, but should only go up to 31 */
step0_next = adpcm6_table1[step0_index] + state->step1;
step0 = (state->step1 & 0xFFFF) * 246;
step0 = (step0 + adpcm6_table2[step0_index]) >> 8;
@ -308,7 +306,7 @@ static int16_t expand_code_6bit(uint8_t code, ubi_adpcm_channel_data* state) {
delta0 = 0;
if (!(((step0_next & 0xFFFFFF00) - 1) & (1 << 31))) {
int delta0_index = ((step0_next >> 3) & 0x1F) + (code < 31 ? 33 : 0);
int delta0_index = ((step0_next >> 3) & 0x1F) + (code_signed < 0 ? 33 : 0);
int delta0_shift = clamp_val((step0_next >> 8) & 0xFF, 0,31);
delta0 = (delta_table[delta0_index] << delta0_shift) >> 10;
}
@ -325,15 +323,15 @@ static int16_t expand_code_6bit(uint8_t code, ubi_adpcm_channel_data* state) {
return sample_new;
}
/* may be simplified (masks, saturation, etc) as some values should never happen in the encoder */
static int16_t expand_code_4bit(uint8_t code, ubi_adpcm_channel_data* state) {
int step0_index;
int32_t step0_next, step0, delta0, next0, coef1_next, coef2_next;
int32_t code_signed, step0_next, step0, delta0, next0, coef1_next, coef2_next;
int32_t sample_new;
step0_index = abs(code - 7); /* 0..8, but should only go up to 7 */
code_signed = (int32_t)code - 7; /* convert coded 0..15 value to signed value, where 0=-7 .. 7=0 .. 15=8 */
step0_index = abs(code_signed); /* 0..8, but should only go up to 7 */
step0_next = adpcm4_table1[step0_index] + state->step1;
step0 = (state->step1 & 0xFFFF) * 246;
step0 = (step0 + adpcm4_table2[step0_index]) >> 8;
@ -341,7 +339,7 @@ static int16_t expand_code_4bit(uint8_t code, ubi_adpcm_channel_data* state) {
delta0 = 0;
if (!(((step0_next & 0xFFFFFF00) - 1) & (1 << 31))) {
int delta0_index = ((step0_next >> 3) & 0x1F) + (code < 7 ? 33 : 0);
int delta0_index = ((step0_next >> 3) & 0x1F) + (code_signed < 0 ? 33 : 0);
int delta0_shift = clamp_val((step0_next >> 8) & 0xFF, 0,31);
delta0 = (delta_table[delta0_index] << delta0_shift) >> 10;
}
@ -454,8 +452,8 @@ static void decode_subframe_stereo(ubi_adpcm_channel_data* ch0_state, ubi_adpcm_
* ex. uint8_t 0x98576787DB5725A8... becomes 0x87675798 LE = 8 7 6 7 5 7 9 8 ...
* - for 6-bit, 32b contain ~5 codes with leftover bits used in following 32b
* ex. uint8_t 0x98576787DB5725A8... becomes 0x87675798 LE = 100001 110110 011101 010111 100110 00,
* 0xA82557DB LE = 1010 100000 100101 010101 111101 1011.. (where last 00 | 1010 = 001010), etc
* Codes aren't signed but rather part of an index
* 0xA82557DB LE = 1010 100000 100101 010101 111101 1011 ... (where last 00 | first 1010 = 001010), etc
* Codes aren't signed but rather have a particular meaning (see decoding).
*/
void unpack_codes(uint8_t *data, uint8_t* codes, int code_count, int bps) {
int i;
@ -580,3 +578,11 @@ static void decode_frame(STREAMFILE* sf, ubi_adpcm_codec_data *data) {
data->samples_consumed = 0;
data->samples_filled = (code_count_a + code_count_b) / channels;
}
int ubi_adpcm_get_samples(ubi_adpcm_codec_data *data) {
if (!data)
return 0;
return data->header.sample_count / data->header.channels;
}

View File

@ -2,7 +2,8 @@
/* Defines the list of accepted extensions. vgmstream doesn't use it internally so it's here
* to inform plugins that need it. Common extensions are commented out to avoid stealing them. */
* to inform plugins that need it. Common extensions are commented out to avoid stealing them
* and possibly adding an unwanted association to the player. */
/* Some extensions require external libraries and could be #ifdef, not worth. */
@ -93,6 +94,7 @@ static const char* extension_list[] = {
"bik",
"bika",
"bik2",
//"bin", //common
"bk2",
"bmdx",
"bms",
@ -185,6 +187,12 @@ static const char* extension_list[] = {
"hlwav",
"hps",
"hsf",
"hx2",
"hx3",
"hxc",
"hxd",
"hxg",
"hxx",
"hwas",
"iab",
@ -231,6 +239,7 @@ static const char* extension_list[] = {
"laifc", //fake extension for .aifc
"lac3", //fake extension for .ac3, FFmpeg/not parsed
"lasf", //fake extension for .asf (various)
"lbin", //fake extension for .bin (various)
"leg",
"lflac", //fake extension for .flac, FFmpeg/not parsed
"lin",
@ -682,6 +691,7 @@ static const coding_info coding_info_list[] = {
{coding_XMD, "Konami XMD 4-bit ADPCM"},
{coding_PCFX, "PC-FX 4-bit ADPCM"},
{coding_OKI16, "OKI 4-bit ADPCM (16-bit output)"},
{coding_OKI4S, "OKI 4-bit ADPCM (4-shift)"},
{coding_PTADPCM, "Platinum 4-bit ADPCM"},
{coding_SDX2, "Squareroot-delta-exact (SDX2) 8-bit DPCM"},
@ -1198,6 +1208,8 @@ static const meta_info meta_info_list[] = {
{meta_DSP_ITL_i, "Infernal .ITL DSP header"},
{meta_IMA, "Blitz Games .IMA header"},
{meta_XMV_VALVE, "Valve XMV header"},
{meta_UBI_HX, "Ubisoft HXx header"},
{meta_BMP_KONAMI, "Konami BMP header"},
};

View File

@ -508,6 +508,10 @@
RelativePath=".\meta\bik.c"
>
</File>
<File
RelativePath=".\meta\bmp_konami.c"
>
</File>
<File
RelativePath=".\meta\bnk_sony.c"
>
@ -1586,10 +1590,14 @@
RelativePath=".\meta\ubi_bao.c"
>
</File>
<File
RelativePath=".\meta\ubi_ckd.c"
>
</File>
<File
RelativePath=".\meta\ubi_ckd.c"
>
</File>
<File
RelativePath=".\meta\ubi_hx.c"
>
</File>
<File
RelativePath=".\meta\ubi_lyn.c"
>

View File

@ -256,6 +256,7 @@
<ClCompile Include="meta\baf.c" />
<ClCompile Include="meta\bgw.c" />
<ClCompile Include="meta\bik.c" />
<ClCompile Include="meta\bmp_konami.c" />
<ClCompile Include="meta\bnk_sony.c" />
<ClCompile Include="meta\bnsf.c" />
<ClCompile Include="meta\brstm.c" />
@ -481,6 +482,7 @@
<ClCompile Include="meta\vgs.c" />
<ClCompile Include="meta\ubi_bao.c" />
<ClCompile Include="meta\ubi_ckd.c" />
<ClCompile Include="meta\ubi_hx.c" />
<ClCompile Include="meta\ubi_lyn.c" />
<ClCompile Include="meta\ubi_raki.c" />
<ClCompile Include="meta\ubi_sb.c" />

View File

@ -979,6 +979,9 @@
<ClCompile Include="meta\ubi_ckd.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\ubi_hx.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\ubi_lyn.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
@ -1594,6 +1597,9 @@
<ClCompile Include="meta\bik.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\bmp_konami.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\bnk_sony.c">
<Filter>meta\Source Files</Filter>
</ClCompile>

45
src/meta/bmp_konami.c Normal file
View File

@ -0,0 +1,45 @@
#include "meta.h"
/* BMP - from Jubeat Clan (AC) */
VGMSTREAM * init_vgmstream_bmp_konami(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
off_t start_offset;
int loop_flag, channel_count;
/* checks */
/* .bin: actual extension
* .lbin: for plugins */
if (!check_extensions(streamFile, "bin,lbin"))
goto fail;
if (read_32bitBE(0x00,streamFile) != 0x424D5000) /* "BMP\0" "*/
goto fail;
channel_count = read_8bit(0x10,streamFile); /* assumed */
if (channel_count != 2) goto fail;
loop_flag = 0;
start_offset = 0x20;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count, loop_flag);
if (!vgmstream) goto fail;
vgmstream->meta_type = meta_BMP_KONAMI;
vgmstream->num_samples = read_32bitBE(0x04,streamFile);
vgmstream->sample_rate = read_32bitBE(0x14, streamFile);
vgmstream->coding_type = coding_OKI4S;
vgmstream->layout_type = layout_none;
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
goto fail;
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;
}

View File

@ -864,4 +864,8 @@ VGMSTREAM * init_vgmstream_nub_is14(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_xmv_valve(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_ubi_hx(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_bmp_konami(STREAMFILE * streamFile);
#endif /*_META_H*/

View File

@ -6,26 +6,25 @@
/* If these variables are packed properly in the struct (one after another)
* then this is actually how they are laid out in the file, albeit big-endian */
struct dsp_header {
uint32_t sample_count;
uint32_t nibble_count;
uint32_t sample_rate;
uint16_t loop_flag;
uint16_t format;
uint32_t loop_start_offset;
uint32_t loop_end_offset;
uint32_t ca;
int16_t coef[16]; /* really 8x2 */
uint16_t gain;
uint16_t initial_ps;
int16_t initial_hist1;
int16_t initial_hist2;
uint16_t loop_ps;
int16_t loop_hist1;
int16_t loop_hist2;
int16_t channel_count; /* DSPADPCM.exe ~v2.7 extension */
int16_t block_size;
/* padding/reserved up to 0x60 */
/* DSPADPCM.exe from GC adds some extra data here (uninitialized MSVC memory?) */
uint32_t sample_count; /* 0x00 */
uint32_t nibble_count; /* 0x04 */
uint32_t sample_rate; /* 0x08 */
uint16_t loop_flag; /* 0x0c */
uint16_t format; /* 0x0e */
uint32_t loop_start_offset; /* 0x10 */
uint32_t loop_end_offset; /* 0x14 */
uint32_t ca; /* 0x18 */
int16_t coef[16]; /* 0x1c (really 8x2) */
uint16_t gain; /* 0x3c */
uint16_t initial_ps; /* 0x3e */
int16_t initial_hist1; /* 0x40 */
int16_t initial_hist2; /* 0x42 */
uint16_t loop_ps; /* 0x44 */
int16_t loop_hist1; /* 0x46 */
int16_t loop_hist2; /* 0x48 */
int16_t channel_count; /* 0x4a (DSPADPCM.exe ~v2.7 extension) */
int16_t block_size; /* 0x4c */
/* padding/reserved up to 0x60, DSPADPCM.exe from GC adds garbage here (uninitialized MSVC memory?) */
};
/* read the above struct; returns nonzero on failure */

View File

@ -15,6 +15,7 @@ VGMSTREAM * init_vgmstream_nub(STREAMFILE *streamFile) {
const char* fake_ext;
VGMSTREAM*(*init_vgmstream_function)(STREAMFILE *) = NULL;
char name[STREAM_NAME_SIZE] = {0};
int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL;
/* checks */
@ -25,6 +26,13 @@ VGMSTREAM * init_vgmstream_nub(STREAMFILE *streamFile) {
if (read_32bitBE(0x04,streamFile) != 0x00000000) /* null */
goto fail;
/* ToV PS4 uses LE */
if (guess_endianness32bit(0x08, streamFile)) {
read_32bit = read_32bitBE;
} else{
read_32bit = read_32bitLE;
}
/* parse TOC */
{
off_t offset, data_start, header_start;
@ -33,16 +41,16 @@ VGMSTREAM * init_vgmstream_nub(STREAMFILE *streamFile) {
/* - base header */
/* 0x08: file id/number */
total_subsongs = read_32bitBE(0x0c, streamFile); /* .nub with 0 files do exist */
data_start = read_32bitBE(0x10, streamFile);
total_subsongs = read_32bit(0x0c, streamFile); /* .nub with 0 files do exist */
data_start = read_32bit(0x10, streamFile);
/* 0x14: data end */
header_start = read_32bitBE(0x18, streamFile);
header_start = read_32bit(0x18, streamFile);
/* 0x1c: header end */
if (target_subsong == 0) target_subsong = 1;
if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail;
offset = read_32bitBE(header_start + (target_subsong-1)*0x04, streamFile);
offset = read_32bit(header_start + (target_subsong-1)*0x04, streamFile);
/* .nus have all headers first then all data, but extractors often just paste them together,
* so we'll combine header+data on the fly to make them playable with existing parsers.
@ -53,11 +61,11 @@ VGMSTREAM * init_vgmstream_nub(STREAMFILE *streamFile) {
/* 00: extension (as referenced in companion files with internal filenames, ex. "BGM_MovingDemo1.is14" > "is14") */
/* 04: config? */
/* 08: header id/number */
codec = (uint32_t)read_32bitBE(offset + 0x0c, streamFile);
codec = (uint32_t)read_32bit(offset + 0x0c, streamFile);
/* 10: null */
stream_size = read_32bitBE(offset + 0x14, streamFile); /* 0x10 aligned */
stream_offset = read_32bitBE(offset + 0x18, streamFile) + data_start;
subheader_size = read_32bitBE(offset + 0x1c, streamFile);
stream_size = read_32bit(offset + 0x14, streamFile); /* 0x10 aligned */
stream_offset = read_32bit(offset + 0x18, streamFile) + data_start;
subheader_size = read_32bit(offset + 0x1c, streamFile);
/* rest looks like config/volumes/etc */
subheader_start = 0xBC;
@ -102,6 +110,8 @@ VGMSTREAM * init_vgmstream_nub(STREAMFILE *streamFile) {
goto fail;
}
;VGM_LOG("NUB: subfile offset=%lx + %x\n", stream_offset, stream_size);
temp_streamFile = make_nub_streamfile(streamFile, header_offset, header_size, stream_offset, stream_size, fake_ext);
if (!temp_streamFile) goto fail;
}
@ -117,7 +127,7 @@ VGMSTREAM * init_vgmstream_nub(STREAMFILE *streamFile) {
snprintf(filename,sizeof(filename), "nuSound2ToneStr%s.bin", basename);
nameFile = open_streamfile_by_filename(streamFile, filename);
if (nameFile && read_32bitBE(0x08, nameFile) == total_subsongs) {
if (nameFile && read_32bit(0x08, nameFile) == total_subsongs) {
off_t header_start = 0x40; /* first name is bank name */
char name1[0x20+1] = {0};
char name2[0x20+1] = {0};
@ -441,6 +451,7 @@ VGMSTREAM * init_vgmstream_nub_is14(STREAMFILE *streamFile) {
STREAMFILE *temp_streamFile = NULL;
off_t header_offset, stream_offset;
size_t header_size, stream_size;
int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL;
/* checks */
@ -449,15 +460,21 @@ VGMSTREAM * init_vgmstream_nub_is14(STREAMFILE *streamFile) {
if (read_32bitBE(0x00,streamFile) != 0x69733134) /* "is14" */
goto fail;
if (guess_endianness32bit(0x04, streamFile)) {
read_32bit = read_32bitBE;
} else{
read_32bit = read_32bitLE;
}
/* paste header+data together and pass to meta */
header_offset = 0xBC;
header_size = read_32bitBE(0x1c, streamFile);
header_size = read_32bit(0x1c, streamFile);
stream_offset = align_size_to_block(header_offset + header_size, 0x10);
stream_size = read_32bitBE(header_offset + header_size - 0x04, streamFile);/* size at 0x14 is padded, use "sdat" size */
stream_size = read_32bitBE(header_offset + header_size - 0x04, streamFile); /* size at 0x14 is padded, use "sdat" size BE */
VGM_LOG("%lx, %x, %lx, %x\n", header_offset, header_size, stream_offset, stream_size);
temp_streamFile = make_nub_streamfile(streamFile, header_offset, header_size, stream_offset, stream_size, "bnsf");
if (!temp_streamFile) goto fail;
dump_streamfile(temp_streamFile, 0);
vgmstream = init_vgmstream_bnsf(temp_streamFile);
if (!vgmstream) goto fail;

649
src/meta/ubi_hx.c Normal file
View File

@ -0,0 +1,649 @@
#include "meta.h"
#include "../layout/layout.h"
#include "../coding/coding.h"
typedef enum { PCM, UBI, PSX, DSP, XIMA, ATRAC3, XMA2 } ubi_hx_codec;
typedef struct {
int big_endian;
int total_subsongs;
int codec_id;
ubi_hx_codec codec; /* unified codec */
int header_index; /* entry number within section2 */
off_t header_offset; /* entry offset within internal .HXx */
size_t header_size; /* entry offset within internal .HXx */
char class_name[255];
size_t class_size;
size_t stream_mode;
off_t stream_offset; /* data offset within external stream */
size_t stream_size; /* data size within external stream */
uint32_t cuuid1; /* usually "Res" id1: class (1=Event, 3=Wave), id2: group id+sound id, */
uint32_t cuuid2; /* others have some complex id (not hash), id1: parent id?, id2: file id? */
int loop_flag;
int channels;
int sample_rate;
int num_samples;
int is_external;
char resource_name[0x28]; /* filename to the external stream */
char internal_name[255]; /* WavRes's assigned name */
char readable_name[255]; /* final subsong name */
} ubi_hx_header;
static int parse_hx(ubi_hx_header * hx, STREAMFILE *sf, int target_subsong);
static VGMSTREAM * init_vgmstream_ubi_hx_header(ubi_hx_header *hx, STREAMFILE *sf);
/* .HXx - banks from Ubisoft's HXAudio engine games [Rayman Arena, Rayman 3, XIII] */
VGMSTREAM * init_vgmstream_ubi_hx(STREAMFILE *streamFile) {
VGMSTREAM* vgmstream = NULL;
ubi_hx_header hx = {0};
int target_subsong = streamFile->stream_index;
/* checks */
/* .hxd: Rayman Arena (all)
* .hxc: Rayman 3 (PC), XIII (PC)
* .hx2: Rayman 3 (PS2), XIII (PS2)
* .hxg: Rayman 3 (GC), XIII (GC)
* .hxx: Rayman 3 (Xbox), Rayman 3 HD (X360)
* .hx3: Rayman 3 HD (PS3) */
if (!check_extensions(streamFile, "hxd,hxc,hx2,hxg,hxx,hx3"))
goto fail;
/* .HXx is a slightly less bizarre bank with various resource classes (events, streams, etc, not unlike other Ubi's engines)
* then an index to those types. Some games leave a companion .bnh with text info, probably leftover from their tools.
* Game seems to play files by calling linked ids: EventResData (play/stop/etc) > Random/Program/Wav ResData (1..N refs) > FileIdObj */
/* HX CONFIG */
hx.big_endian = guess_endianness32bit(0x00, streamFile);
/* HX HEADER */
if (!parse_hx(&hx, streamFile, target_subsong))
goto fail;
/* CREATE VGMSTREAM */
vgmstream = init_vgmstream_ubi_hx_header(&hx, streamFile);
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;
}
static void build_readable_name(char * buf, size_t buf_size, ubi_hx_header * hx) {
const char *grp_name;
if (hx->is_external)
grp_name = hx->resource_name;
else
grp_name = "internal";
if (hx->internal_name[0])
snprintf(buf,buf_size, "%s/%i/%08x-%08x/%s/%s", "hx", hx->header_index, hx->cuuid1,hx->cuuid2, grp_name, hx->internal_name);
else
snprintf(buf,buf_size, "%s/%i/%08x-%08x/%s", "hx", hx->header_index, hx->cuuid1,hx->cuuid2, grp_name);
}
/* get referenced name from WavRes, using the index again (abridged) */
static int parse_name(ubi_hx_header * hx, STREAMFILE *sf) {
int32_t (*read_32bit)(off_t,STREAMFILE*) = hx->big_endian ? read_32bitBE : read_32bitLE;
off_t index_offset, offset;
int i, index_entries;
char class_name[255];
index_offset = read_32bit(0x00, sf);
index_entries = read_32bit(index_offset + 0x08, sf);
offset = index_offset + 0x0c;
for (i = 0; i < index_entries; i++) {
off_t header_offset;
size_t class_size;
int j, link_count, language_count, is_found = 0;
class_size = read_32bit(offset + 0x00, sf);
if (class_size > sizeof(class_name)+1) goto fail;
read_string(class_name,class_size+1, offset + 0x04, sf); /* not null-terminated */
offset += 0x04 + class_size;
header_offset = read_32bit(offset + 0x08, sf);
offset += 0x10;
//unknown_count = read_32bit(offset + 0x00, sf);
offset += 0x04;
link_count = read_32bit(offset + 0x00, sf);
offset += 0x04;
for (j = 0; j < link_count; j++) {
uint32_t link_id1 = (uint32_t)read_32bit(offset + 0x00, sf);
uint32_t link_id2 = (uint32_t)read_32bit(offset + 0x04, sf);
if (link_id1 == hx->cuuid1 && link_id2 == hx->cuuid2) {
is_found = 1;
}
offset += 0x08;
}
language_count = read_32bit(offset + 0x00, sf);
offset += 0x04;
for (j = 0; j < language_count; j++) {
uint32_t link_id1 = (uint32_t)read_32bit(offset + 0x08, sf);
uint32_t link_id2 = (uint32_t)read_32bit(offset + 0x0c, sf);
if (link_id1 == hx->cuuid1 && link_id2 == hx->cuuid2) {
is_found = 1;
}
offset += 0x10;
}
/* identify all possible names so unknown platforms fail */
if (is_found && (
strcmp(class_name, "CPCWavResData") == 0 ||
strcmp(class_name, "CPS2WavResData") == 0 ||
strcmp(class_name, "CGCWavResData") == 0 ||
strcmp(class_name, "CXBoxWavResData") == 0 ||
strcmp(class_name, "CPS3WavResData") == 0)) {
size_t resclass_size, internal_size;
off_t wavres_offset = header_offset;
/* parse WavRes header */
resclass_size = read_32bit(wavres_offset, sf);
wavres_offset += 0x04 + resclass_size + 0x08 + 0x04; /* skip class + cuiid + flags */
internal_size = read_32bit(wavres_offset + 0x00, sf); /* usually 0 in consoles */
if (internal_size > sizeof(hx->internal_name)+1) goto fail;
read_string(hx->internal_name,internal_size+1, wavres_offset + 0x04, sf);
return 1;
}
}
fail:
return 0;
}
/* parse a single known header resource at offset */
static int parse_header(ubi_hx_header * hx, STREAMFILE *sf, off_t offset, size_t size, int index) {
int32_t (*read_32bit)(off_t,STREAMFILE*) = hx->big_endian ? read_32bitBE : read_32bitLE;
int16_t (*read_16bit)(off_t,STREAMFILE*) = hx->big_endian ? read_16bitBE : read_16bitLE;
off_t riff_offset, riff_size, chunk_offset, stream_adjust = 0, resource_size;
int cue_flag = 0;
//todo cleanup/unify common readings
//;VGM_LOG("UBI HX: header class %s, o=%lx, s=%x\n\n", class_name, header_offset, header_size);
hx->header_index = index;
hx->header_offset = offset;
hx->header_size = size;
hx->class_size = read_32bit(offset + 0x00, sf);
if (hx->class_size > sizeof(hx->class_name)+1) goto fail;
read_string(hx->class_name,hx->class_size+1, offset + 0x04, sf);
offset += 0x04 + hx->class_size;
hx->cuuid1 = (uint32_t)read_32bit(offset + 0x00, sf);
hx->cuuid2 = (uint32_t)read_32bit(offset + 0x04, sf);
offset += 0x08;
if (strcmp(hx->class_name, "CPCWaveFileIdObj") == 0 ||
strcmp(hx->class_name, "CPS2WaveFileIdObj") == 0 ||
strcmp(hx->class_name, "CGCWaveFileIdObj") == 0) {
uint32_t flag_type = read_32bit(offset + 0x00, sf);
if (flag_type == 0x01 || flag_type == 0x02) { /* Rayman Arena */
if (read_32bit(offset + 0x04, sf) != 0x00) goto fail;
hx->stream_mode = read_32bit(offset + 0x08, sf); /* flag: 0=internal, 1=external */
/* 0x0c: flag: 0=static, 1=stream */
offset += 0x10;
}
else if (flag_type == 0x03) { /* others */
/* 0x04: some kind of parent id shared by multiple Waves, or 0 */
offset += 0x08;
if (strcmp(hx->class_name, "CGCWaveFileIdObj") == 0) {
if (read_32bit(offset + 0x00, sf) != read_32bit(offset + 0x04, sf)) goto fail; /* meaning? */
hx->stream_mode = read_32bit(offset + 0x04, sf);
offset += 0x08;
}
else {
hx->stream_mode = read_8bit(offset, sf);
offset += 0x01;
}
}
else {
VGM_LOG("UBI HX: unknown flag-type\n");
goto fail;
}
/* get bizarro adjust (found in XIII external files) */
if (hx->stream_mode == 0x0a) {
stream_adjust = read_32bit(offset, sf); /* what */
offset += 0x04;
}
//todo probably a flag: &1=external, &2=stream, &8=has adjust (XIII), &4=??? (XIII PS2, small, mono)
switch(hx->stream_mode) {
case 0x00: /* memory (internal file) */
riff_offset = offset;
riff_size = read_32bit(riff_offset + 0x04, sf) + 0x08;
break;
case 0x01: /* static (smaller external file) */
case 0x03: /* stream (bigger external file) */
case 0x07: /* static? */
case 0x0a: /* static? */
resource_size = read_32bit(offset + 0x00, sf);
if (resource_size > sizeof(hx->resource_name)+1) goto fail;
read_string(hx->resource_name,resource_size+1, offset + 0x04, sf);
riff_offset = offset + 0x04 + resource_size;
riff_size = read_32bit(riff_offset + 0x04, sf) + 0x08;
hx->is_external = 1;
break;
default:
goto fail;
}
/* parse pseudo-RIFF "fmt" */
if (read_32bit(riff_offset, sf) != 0x46464952) /* "RIFF" in machine endianness */
goto fail;
hx->codec_id = read_16bit(riff_offset + 0x14 , sf);
switch(hx->codec_id) {
case 0x01: hx->codec = PCM; break;
case 0x02: hx->codec = UBI; break;
case 0x03: hx->codec = PSX; break;
case 0x04: hx->codec = DSP; break;
default: goto fail;
}
hx->channels = read_16bit(riff_offset + 0x16, sf);
hx->sample_rate = read_32bit(riff_offset + 0x18, sf);
/* find "datx" (external) or "data" (internal) also in machine endianness */
if (hx->is_external) {
if (!find_chunk_riff_ve(sf, 0x78746164,riff_offset + 0x0c,riff_size - 0x0c, &chunk_offset,NULL, hx->big_endian))
goto fail;
hx->stream_size = read_32bit(chunk_offset + 0x00, sf);
hx->stream_offset = read_32bit(chunk_offset + 0x04, sf) + stream_adjust;
}
else {
if (!find_chunk_riff_ve(sf, 0x61746164,riff_offset + 0x0c,riff_size - 0x0c, &chunk_offset,NULL, hx->big_endian))
goto fail;
hx->stream_offset = chunk_offset;
hx->stream_size = riff_size - (chunk_offset - riff_offset);
}
/* can contain other RIFF stuff like "cue ", "labl" and "ump3"
* XIII music uses cue/labl to play/loop dynamic sections */
}
else if (strcmp(hx->class_name, "CXBoxStaticHWWaveFileIdObj") == 0 ||
strcmp(hx->class_name, "CXBoxStreamHWWaveFileIdObj") == 0 ||
strcmp(hx->class_name, "CPS3StaticAC3WaveFileIdObj") == 0 ||
strcmp(hx->class_name, "CPS3StreamAC3WaveFileIdObj") == 0) {
hx->stream_offset = read_32bit(offset + 0x00, sf);
hx->stream_size = read_32bit(offset + 0x04, sf);
offset += 0x08;
if (read_32bit(offset + 0x00, sf) != 0x01) goto fail;
/* 0x04: 0? */
offset += 0x08;
hx->stream_mode = read_8bit(offset, sf);
offset += 0x01;
if ((strcmp(hx->class_name, "CXBoxStaticHWWaveFileIdObj") == 0 ||
strcmp(hx->class_name, "CXBoxStreamHWWaveFileIdObj") == 0) && !hx->big_endian) {
/* micro header: some mix of channels + block size + sample rate + flags, unsure of which bits */
hx->codec = XIMA;
hx->channels = (uint8_t)read_8bit(offset + 0x01, sf);
switch(hx->channels) { /* upper 2 bits? */
case 0x48: hx->channels = 1; break;
case 0x90: hx->channels = 2; break;
default: goto fail;
}
hx->sample_rate = (uint16_t)(read_16bit(offset + 0x02, sf) & 0x7FFF) << 1; /* ??? */
cue_flag = (uint8_t) read_8bit (offset + 0x03, sf) & (1<<7);
offset += 0x04;
}
else if ((strcmp(hx->class_name, "CXBoxStaticHWWaveFileIdObj") == 0 ||
strcmp(hx->class_name, "CXBoxStreamHWWaveFileIdObj") == 0) && hx->big_endian) {
/* fake fmt chunk */
hx->codec = XMA2;
hx->channels = (uint16_t)read_16bit(offset + 0x02, sf);
hx->sample_rate = read_32bit(offset + 0x04, sf);
hx->num_samples = read_32bit(offset + 0x18, sf) / 0x02 / hx->channels;
cue_flag = read_32bit(offset + 0x34, sf);
offset += 0x38;
}
else {
/* MSFC header */
hx->codec = ATRAC3;
hx->codec_id = read_32bit(offset + 0x04, sf);
hx->channels = read_32bit(offset + 0x08, sf);
hx->sample_rate = read_32bit(offset + 0x10, sf);
cue_flag = read_32bit(offset + 0x40, sf);
offset += 0x44;
}
/* skip cue table that sometimes exists in streams */
if (cue_flag) {
int j;
size_t cue_count = read_32bit(offset, sf);
offset += 0x04;
for (j = 0; j < cue_count; j++) {
/* 0x00: id? */
size_t description_size = read_32bit(offset + 0x04, sf); /* for next string */
offset += 0x08 + description_size;
}
}
switch(hx->stream_mode) {
case 0x01: /* static (smaller external file) */
case 0x03: /* stream (bigger external file) */
resource_size = read_32bit(offset + 0x00, sf);
if (resource_size > sizeof(hx->resource_name)+1) goto fail;
read_string(hx->resource_name,resource_size+1, offset + 0x04, sf);
hx->is_external = 1;
break;
default:
goto fail;
}
}
else {
goto fail;
}
return 1;
fail:
VGM_LOG("UBI HX: error parsing header at %lx\n", hx->header_offset);
return 0;
}
/* parse a bank index and its possible audio headers (some info from Droolie's .bms) */
static int parse_hx(ubi_hx_header * hx, STREAMFILE *sf, int target_subsong) {
int32_t (*read_32bit)(off_t,STREAMFILE*) = hx->big_endian ? read_32bitBE : read_32bitLE;
off_t index_offset, offset;
int i, index_entries;
char class_name[255];
index_offset = read_32bit(0x00, sf);
if (read_32bit(index_offset + 0x00, sf) != 0x58444E49) /* "XDNI" (INDX in given endianness) */
goto fail;
if (read_32bit(index_offset + 0x04, sf) != 0x02) /* type? */
goto fail;
if (target_subsong == 0) target_subsong = 1;
index_entries = read_32bit(index_offset + 0x08, sf);
offset = index_offset + 0x0c;
for (i = 0; i < index_entries; i++) {
off_t header_offset;
size_t class_size, header_size;
int j, unknown_count, link_count, language_count;
//;VGM_LOG("UBI HX: index %i at %lx\n", i, offset);
/* parse index entries: offset to actual header plus some extra info also in the header */
class_size = read_32bit(offset + 0x00, sf);
if (class_size > sizeof(class_name)+1) goto fail;
read_string(class_name,class_size+1, offset + 0x04, sf); /* not null-terminated */
offset += 0x04 + class_size;
/* 0x00: id1+2 */
header_offset = read_32bit(offset + 0x08, sf);
header_size = read_32bit(offset + 0x0c, sf);
offset += 0x10;
/* not seen */
unknown_count = read_32bit(offset + 0x00, sf);
if (unknown_count != 0) {
VGM_LOG("UBI HX: found unknown near %lx\n", offset);
goto fail;
}
offset += 0x04;
/* ids that this object directly points to (ex. Event > Random) */
link_count = read_32bit(offset + 0x00, sf);
offset += 0x04 + 0x08 * link_count;
/* localized id list of WavRes (can use this list instead of the prev one) */
language_count = read_32bit(offset + 0x00, sf);
offset += 0x04;
for (j = 0; j < language_count; j++) {
/* 0x00: lang code, in reverse endianness: "en ", "fr ", etc */
/* 0x04: possibly count of ids for this lang */
/* 0x08: id1+2 */
if (read_32bit(offset + 0x04, sf) != 1) {
VGM_LOG("UBI HX: wrong lang count near %lx\n", offset);
goto fail; /* WavRes doesn't have this field */
}
offset += 0x10;
}
//todo figure out CProgramResData sequences
/* identify all possible names so unknown platforms fail */
if (strcmp(class_name, "CEventResData") == 0 || /* play/stop/etc event */
strcmp(class_name, "CProgramResData") == 0 || /* some kind of map/object-like config to make sequences in some cases? */
strcmp(class_name, "CActorResData") == 0 || /* same? */
strcmp(class_name, "CRandomResData") == 0 || /* chooses random WavRes from a list */
strcmp(class_name, "CTreeBank") == 0 || /* points to TreeRes? */
strcmp(class_name, "CTreeRes") == 0 || /* points to TreeBank? */
strcmp(class_name, "CSwitchResData") == 0 || /* big list of WavRes */
strcmp(class_name, "CPCWavResData") == 0 || /* points to WaveFileIdObj */
strcmp(class_name, "CPS2WavResData") == 0 ||
strcmp(class_name, "CGCWavResData") == 0 ||
strcmp(class_name, "CXBoxWavResData") == 0 ||
strcmp(class_name, "CPS3WavResData") == 0) {
continue;
}
else if (strcmp(class_name, "CPCWaveFileIdObj") == 0 ||
strcmp(class_name, "CPS2WaveFileIdObj") == 0 ||
strcmp(class_name, "CGCWaveFileIdObj") == 0 ||
strcmp(class_name, "CXBoxStaticHWWaveFileIdObj") == 0 ||
strcmp(class_name, "CXBoxStreamHWWaveFileIdObj") == 0 ||
strcmp(class_name, "CPS3StaticAC3WaveFileIdObj") == 0 ||
strcmp(class_name, "CPS3StreamAC3WaveFileIdObj") == 0) {
;
}
else {
VGM_LOG("UBI HX: unknown type: %s\n", class_name);
goto fail;
}
if (link_count != 0) {
VGM_LOG("UBI HX: found links in wav object\n");
goto fail;
}
hx->total_subsongs++;
if (hx->total_subsongs != target_subsong)
continue;
if (!parse_header(hx, sf, header_offset, header_size, i))
goto fail;
if (!parse_name(hx, sf))
goto fail;
build_readable_name(hx->readable_name,sizeof(hx->readable_name), hx);
}
if (target_subsong < 0 || target_subsong > hx->total_subsongs || hx->total_subsongs < 1) goto fail;
return 1;
fail:
return 0;
}
static STREAMFILE * open_hx_streamfile(ubi_hx_header *hx, STREAMFILE *sf) {
STREAMFILE *streamData = NULL;
if (!hx->is_external)
return NULL;
streamData = open_streamfile_by_filename(sf, hx->resource_name);
if (streamData == NULL) {
VGM_LOG("UBI HX: external stream '%s' not found\n", hx->resource_name);
goto fail;
}
/* streams often have a "RIFF" with "fmt" and "data" but stream offset/size is already adjusted to skip them */
return streamData;
fail:
return NULL;
}
static VGMSTREAM * init_vgmstream_ubi_hx_header(ubi_hx_header *hx, STREAMFILE *streamFile) {
STREAMFILE *streamTemp = NULL;
STREAMFILE *streamData = NULL;
VGMSTREAM* vgmstream = NULL;
if (hx->is_external) {
streamTemp = open_hx_streamfile(hx, streamFile);
if (streamTemp == NULL) goto fail;
streamData = streamTemp;
}
else {
streamData = streamFile;
}
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(hx->channels, hx->loop_flag);
if (!vgmstream) goto fail;
vgmstream->meta_type = meta_UBI_HX;
vgmstream->sample_rate = hx->sample_rate;
vgmstream->num_streams = hx->total_subsongs;
vgmstream->stream_size = hx->stream_size;
switch(hx->codec) {
case PCM:
vgmstream->coding_type = coding_PCM16LE;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = 0x02;
vgmstream->num_samples = pcm_bytes_to_samples(hx->stream_size, hx->channels, 16);
break;
case UBI:
vgmstream->codec_data = init_ubi_adpcm(streamData, hx->stream_offset, vgmstream->channels);
if (!vgmstream->codec_data) goto fail;
vgmstream->coding_type = coding_UBI_ADPCM;
vgmstream->layout_type = layout_none;
vgmstream->num_samples = ubi_adpcm_get_samples(vgmstream->codec_data);
/* XIII has 6-bit stereo music, Rayman 3 4-bit music, both use 6-bit mono) */
break;
case PSX:
vgmstream->coding_type = coding_PSX;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = 0x10;
vgmstream->num_samples = ps_bytes_to_samples(hx->stream_size, hx->channels);
break;
case DSP:
vgmstream->coding_type = coding_NGC_DSP;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = 0x08;
/* dsp header at start offset */
vgmstream->num_samples = read_32bitBE(hx->stream_offset + 0x00, streamData);
dsp_read_coefs_be(vgmstream, streamData, hx->stream_offset + 0x1c, 0x60);
dsp_read_hist_be (vgmstream, streamData, hx->stream_offset + 0x40, 0x60);
hx->stream_offset += 0x60 * hx->channels;
hx->stream_size -= 0x60 * hx->channels;
break;
case XIMA:
vgmstream->coding_type = coding_XBOX_IMA;
vgmstream->layout_type = layout_none;
vgmstream->num_samples = xbox_ima_bytes_to_samples(hx->stream_size, hx->channels);
break;
#ifdef VGM_USE_FFMPEG
case XMA2: {
int bytes, block_count, block_size;
uint8_t buf[0x200];
block_size = 0x800;
block_count = hx->stream_size / block_size;
bytes = ffmpeg_make_riff_xma2(buf,0x200, hx->num_samples, hx->stream_size, hx->channels, hx->sample_rate, block_count, block_size);
vgmstream->codec_data = init_ffmpeg_header_offset(streamData, buf,bytes, hx->stream_offset,hx->stream_size);
if (!vgmstream->codec_data) goto fail;
vgmstream->coding_type = coding_FFmpeg;
vgmstream->layout_type = layout_none;
vgmstream->num_samples = hx->num_samples;
xma_fix_raw_samples_ch(vgmstream, streamData, hx->stream_offset,hx->stream_size, hx->channels, 0,0);
break;
}
case ATRAC3: {
int block_align, encoder_delay;
encoder_delay = 1024 + 69*2;
switch(hx->codec_id) {
case 4: block_align = 0x60 * vgmstream->channels; break;
case 5: block_align = 0x98 * vgmstream->channels; break;
case 6: block_align = 0xC0 * vgmstream->channels; break;
default: goto fail;
}
vgmstream->num_samples = atrac3_bytes_to_samples(hx->stream_size, block_align) - encoder_delay;
vgmstream->codec_data = init_ffmpeg_atrac3_raw(streamData, hx->stream_offset,hx->stream_size, vgmstream->num_samples,vgmstream->channels,vgmstream->sample_rate, block_align, encoder_delay);
if (!vgmstream->codec_data) goto fail;
vgmstream->coding_type = coding_FFmpeg;
vgmstream->layout_type = layout_none;
break;
}
#endif
default:
goto fail;
}
strcpy(vgmstream->stream_name, hx->readable_name);
if (!vgmstream_open_stream(vgmstream, streamData, hx->stream_offset))
goto fail;
close_streamfile(streamTemp);
return vgmstream;
fail:
close_streamfile(streamTemp);
close_vgmstream(vgmstream);
return NULL;
}

View File

@ -1685,8 +1685,8 @@ static int parse_offsets(ubi_sb_header * sb, STREAMFILE *streamFile) {
}
return 1;
fail:
return 0;
//fail:
// return 0;
}
/* parse a single known header resource at offset (see config_sb for info) */

View File

@ -202,7 +202,7 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) {
/* Some Wwise files (ex. Oddworld PSV, Bayonetta 2 WiiU, often in BGM.bnk) are truncated mirrors of another file.
* They come in RAM banks, probably to play the beginning while the rest of the real stream loads.
* They come in RAM banks, prefetch to play the beginning while the rest of the real stream loads.
* We'll add basic support to avoid complaints of this or that .wem not playing */
if (ww.data_offset + ww.data_size > ww.file_size) {
//VGM_LOG("WWISE: truncated data size (prefetch): (real=0x%x > riff=0x%x)\n", ww.data_size, ww.file_size);
@ -213,8 +213,8 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) {
goto fail;
}
if (ww.codec == IMA || ww.codec == VORBIS || ww.codec == XMA2)
ww.truncated = 1; /* only seen those, probably others exist */
if (ww.codec == IMA || ww.codec == VORBIS || ww.codec == XMA2 || ww.codec == OPUSNX)
ww.truncated = 1; /* only seen those, probably all exist */
else
goto fail;
}
@ -579,6 +579,13 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) {
skip = switch_opus_get_encoder_delay(start_offset, streamFile); /* should be 120 */
/* OPUS is VBR so this is very approximate percent, meh */
if (ww.truncated) {
vgmstream->num_samples = (int32_t)(vgmstream->num_samples *
(double)(ww.file_size - start_offset) / (double)ww.data_size);
ww.data_size = ww.file_size - start_offset;
}
vgmstream->codec_data = init_ffmpeg_switch_opus(streamFile, start_offset,ww.data_size, vgmstream->channels, skip, vgmstream->sample_rate);
if (!vgmstream->codec_data) goto fail;
vgmstream->coding_type = coding_FFmpeg;

View File

@ -1030,26 +1030,28 @@ int check_extensions(STREAMFILE *streamFile, const char * cmp_exts) {
*
* returns 0 on failure
*/
int find_chunk_be(STREAMFILE *streamFile, uint32_t chunk_id, off_t start_offset, int full_chunk_size, off_t *out_chunk_offset, size_t *out_chunk_size) {
return find_chunk(streamFile, chunk_id, start_offset, full_chunk_size, out_chunk_offset, out_chunk_size, 1, 0);
}
int find_chunk_le(STREAMFILE *streamFile, uint32_t chunk_id, off_t start_offset, int full_chunk_size, off_t *out_chunk_offset, size_t *out_chunk_size) {
return find_chunk(streamFile, chunk_id, start_offset, full_chunk_size, out_chunk_offset, out_chunk_size, 0, 0);
}
int find_chunk(STREAMFILE *streamFile, uint32_t chunk_id, off_t start_offset, int full_chunk_size, off_t *out_chunk_offset, size_t *out_chunk_size, int size_big_endian, int zero_size_end) {
size_t filesize;
off_t current_chunk = start_offset;
static int find_chunk_internal(STREAMFILE *streamFile, uint32_t chunk_id, off_t start_offset, size_t max_size, int full_chunk_size, off_t *out_chunk_offset, size_t *out_chunk_size, int big_endian_type, int big_endian_size, int zero_size_end) {
int32_t (*read_32bit_type)(off_t,STREAMFILE*) = big_endian_type ? read_32bitBE : read_32bitLE;
int32_t (*read_32bit_size)(off_t,STREAMFILE*) = big_endian_size ? read_32bitBE : read_32bitLE;
off_t offset, max_offset;
size_t file_size = get_streamfile_size(streamFile);
if (max_size == 0)
max_size = file_size;
offset = start_offset;
max_offset = offset + max_size;
if (max_offset > file_size)
max_offset = file_size;
filesize = get_streamfile_size(streamFile);
/* read chunks */
while (current_chunk < filesize) {
uint32_t chunk_type = read_32bitBE(current_chunk,streamFile);
off_t chunk_size = size_big_endian ?
read_32bitBE(current_chunk+4,streamFile) :
read_32bitLE(current_chunk+4,streamFile);
while (offset < max_offset) {
uint32_t chunk_type = read_32bit_type(offset + 0x00,streamFile);
uint32_t chunk_size = read_32bit_size(offset + 0x04,streamFile);
if (chunk_type == chunk_id) {
if (out_chunk_offset) *out_chunk_offset = current_chunk+8;
if (out_chunk_offset) *out_chunk_offset = offset + 0x08;
if (out_chunk_size) *out_chunk_size = chunk_size;
return 1;
}
@ -1058,11 +1060,23 @@ int find_chunk(STREAMFILE *streamFile, uint32_t chunk_id, off_t start_offset, in
if (chunk_size == 0 && zero_size_end)
return 0;
current_chunk += full_chunk_size ? chunk_size : 4+4+chunk_size;
offset += full_chunk_size ? chunk_size : 0x08 + chunk_size;
}
return 0;
}
int find_chunk_be(STREAMFILE *streamFile, uint32_t chunk_id, off_t start_offset, int full_chunk_size, off_t *out_chunk_offset, size_t *out_chunk_size) {
return find_chunk(streamFile, chunk_id, start_offset, full_chunk_size, out_chunk_offset, out_chunk_size, 1, 0);
}
int find_chunk_le(STREAMFILE *streamFile, uint32_t chunk_id, off_t start_offset, int full_chunk_size, off_t *out_chunk_offset, size_t *out_chunk_size) {
return find_chunk(streamFile, chunk_id, start_offset, full_chunk_size, out_chunk_offset, out_chunk_size, 0, 0);
}
int find_chunk(STREAMFILE *streamFile, uint32_t chunk_id, off_t start_offset, int full_chunk_size, off_t *out_chunk_offset, size_t *out_chunk_size, int big_endian_size, int zero_size_end) {
return find_chunk_internal(streamFile, chunk_id, start_offset, 0, full_chunk_size, out_chunk_offset, out_chunk_size, 1, big_endian_size, zero_size_end);
}
int find_chunk_riff_ve(STREAMFILE *streamFile, uint32_t chunk_id, off_t start_offset, size_t max_size, off_t *out_chunk_offset, size_t *out_chunk_size, int big_endian) {
return find_chunk_internal(streamFile, chunk_id, start_offset, max_size, 0, out_chunk_offset, out_chunk_size, big_endian, big_endian, 0);
}
/* copies name as-is (may include full path included) */
void get_streamfile_name(STREAMFILE *streamFile, char * buffer, size_t size) {

View File

@ -267,7 +267,9 @@ int check_extensions(STREAMFILE *streamFile, const char * cmp_exts);
int find_chunk_be(STREAMFILE *streamFile, uint32_t chunk_id, off_t start_offset, int full_chunk_size, off_t *out_chunk_offset, size_t *out_chunk_size);
int find_chunk_le(STREAMFILE *streamFile, uint32_t chunk_id, off_t start_offset, int full_chunk_size, off_t *out_chunk_offset, size_t *out_chunk_size);
int find_chunk(STREAMFILE *streamFile, uint32_t chunk_id, off_t start_offset, int full_chunk_size, off_t *out_chunk_offset, size_t *out_chunk_size, int size_big_endian, int zero_size_end);
int find_chunk(STREAMFILE *streamFile, uint32_t chunk_id, off_t start_offset, int full_chunk_size, off_t *out_chunk_offset, size_t *out_chunk_size, int big_endian_size, int zero_size_end);
int find_chunk_riff_ve(STREAMFILE *streamFile, uint32_t chunk_id, off_t start_offset, size_t max_size, off_t *out_chunk_offset, size_t *out_chunk_size, int big_endian);
void get_streamfile_name(STREAMFILE *streamFile, char * buffer, size_t size);
void get_streamfile_filename(STREAMFILE *streamFile, char * buffer, size_t size);

View File

@ -477,6 +477,8 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = {
init_vgmstream_nub_idsp,
init_vgmstream_nub_is14,
init_vgmstream_xmv_valve,
init_vgmstream_ubi_hx,
init_vgmstream_bmp_konami,
/* 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 */
@ -501,7 +503,7 @@ static VGMSTREAM * init_vgmstream_internal(STREAMFILE *streamFile) {
fcns_size = (sizeof(init_vgmstream_functions)/sizeof(init_vgmstream_functions[0]));
/* try a series of formats, see which works */
for (i =0; i < fcns_size; i++) {
for (i = 0; i < fcns_size; i++) {
/* call init function and see if valid VGMSTREAM was returned */
VGMSTREAM * vgmstream = (init_vgmstream_functions[i])(streamFile);
if (!vgmstream)
@ -1149,6 +1151,7 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) {
case coding_OTNS_IMA:
case coding_UBI_IMA:
case coding_OKI16:
case coding_OKI4S:
return 1;
case coding_PCM4:
case coding_PCM4_U:
@ -1347,6 +1350,7 @@ int get_vgmstream_frame_size(VGMSTREAM * vgmstream) {
case coding_BLITZ_IMA:
case coding_PCFX:
case coding_OKI16:
case coding_OKI4S:
return 0x01;
case coding_MS_IMA:
case coding_RAD_IMA:
@ -2116,6 +2120,13 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to
}
break;
case coding_OKI4S:
for (ch = 0; ch < vgmstream->channels; ch++) {
decode_oki4s(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch,
vgmstream->channels,vgmstream->samples_into_block,samples_to_do, ch);
}
break;
case coding_UBI_ADPCM:
decode_ubi_adpcm(vgmstream, buffer+samples_written*vgmstream->channels, samples_to_do);
break;

View File

@ -164,7 +164,8 @@ typedef enum {
coding_DSA, /* Ocean DSA 4-bit ADPCM */
coding_XMD, /* Konami XMD 4-bit ADPCM */
coding_PCFX, /* PC-FX 4-bit ADPCM */
coding_OKI16, /* OKI 4-bit ADPCM with 16-bit output */
coding_OKI16, /* OKI 4-bit ADPCM with 16-bit output and modified expand */
coding_OKI4S, /* OKI 4-bit ADPCM with 16-bit output and cuadruple step */
coding_PTADPCM, /* Platinum 4-bit ADPCM */
/* others */
@ -711,6 +712,8 @@ typedef enum {
meta_DSP_ITL_i,
meta_IMA,
meta_XMV_VALVE,
meta_UBI_HX,
meta_BMP_KONAMI,
} meta_t;