mirror of
https://github.com/vgmstream/vgmstream.git
synced 2024-11-12 01:30:49 +01:00
commit
0fe6f3c31e
@ -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
|
||||
|
@ -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*
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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"},
|
||||
|
||||
};
|
||||
|
||||
|
@ -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"
|
||||
>
|
||||
|
@ -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" />
|
||||
|
@ -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
45
src/meta/bmp_konami.c
Normal 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;
|
||||
}
|
@ -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*/
|
||||
|
@ -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 */
|
||||
|
@ -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
649
src/meta/ubi_hx.c
Normal 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;
|
||||
}
|
@ -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) */
|
||||
|
@ -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;
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user