Merge pull request #466 from bnnm/ubiadpcm-acb-etc

ubiadpcm acb etc
This commit is contained in:
bnnm 2019-09-02 22:48:29 +02:00 committed by GitHub
commit a349c26be6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 1320 additions and 702 deletions

View File

@ -247,7 +247,7 @@ recent versions (1.4.x) you can configure plugin priority.
### Channel issues ### Channel issues
Some games layer a huge number of channels, that are disabled or downmixed Some games layer a huge number of channels, that are disabled or downmixed
during gameplay. The player may be unable to play those files (for example during gameplay. The player may be unable to play those files (for example
foobar can only play up to 8 channels, and Winamp depends the your sound foobar can only play up to 8 channels, and Winamp depends on your sound
card). For those files you can set the "downmix" option in vgmstream, that card). For those files you can set the "downmix" option in vgmstream, that
can reduce the number of channels to a playable amount. Note that this type can reduce the number of channels to a playable amount. Note that this type
of downmixing is very generic, not meant to be used when converting to other of downmixing is very generic, not meant to be used when converting to other

View File

@ -187,6 +187,13 @@ size_t oki_bytes_to_samples(size_t bytes, int channels);
void decode_ptadpcm(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, size_t frame_size); void decode_ptadpcm(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, size_t frame_size);
size_t ptadpcm_bytes_to_samples(size_t bytes, int channels, size_t frame_size); size_t ptadpcm_bytes_to_samples(size_t bytes, int channels, size_t frame_size);
/* ubi_adpcm_decoder */
ubi_adpcm_codec_data *init_ubi_adpcm(STREAMFILE *streamFile, off_t offset, int channels);
void decode_ubi_adpcm(VGMSTREAM * vgmstream, sample_t * outbuf, int32_t samples_to_do);
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);
/* ea_mt_decoder*/ /* ea_mt_decoder*/
ea_mt_codec_data *init_ea_mt(int channels, int type); ea_mt_codec_data *init_ea_mt(int channels, int type);
ea_mt_codec_data *init_ea_mt_loops(int channels, int pcm_blocks, int loop_sample, off_t *loop_offsets); ea_mt_codec_data *init_ea_mt_loops(int channels, int pcm_blocks, int loop_sample, off_t *loop_offsets);

View File

@ -1,32 +1,32 @@
#include "coding.h" #include "coding.h"
/* a somewhat IMA-like mix of step+step index all in one */ /* a somewhat IMA-like mix of step+next index all in one (maybe an array[][] originally?) */
static const int32_t ptadpcm_table[(16+16)*16] = { /* 16 of (step+index) + 16 values in a nibble */ static const int32_t ptadpcm_table[(16+16)*16] = { /* 16 of (step+index) + 16 values in a nibble */
-14, 2, -10, 2, -7, 1, -5, 1, -3, 0, -2, 0, -1, 0, 0, 0, -14, 2, -10, 2, -7, 1, -5, 1, -3, 0, -2, 0, -1, 0, 0, 0,
0, 0, 1, 0, 2, 0, 3, 0, 5, 1, 7, 1, 10, 2, 14, 2, 0, 0, 1, 0, 2, 0, 3, 0, 5, 1, 7, 1, 10, 2, 14, 2,
-28, 3, -20, 3, -14, 2, -10, 2, -7, 1, -5, 1, -3, 1, -1, 0, -28, 3, -20, 3, -14, 2, -10, 2, -7, 1, -5, 1, -3, 1, -1, 0,
1, 0, 3, 1, 5, 1, 7, 1, 10, 2, 14, 2, 20, 3, 28, 3, 1, 0, 3, 1, 5, 1, 7, 1, 10, 2, 14, 2, 20, 3, 28, 3,
-56, 4, -40, 4, -28, 3, -20, 3, -14, 2, -10, 2, -6, 2, -2, 1, -56, 4, -40, 4, -28, 3, -20, 3, -14, 2, -10, 2, -6, 2, -2, 1,
2, 1, 6, 2, 10, 2, 14, 2, 20, 3, 28, 3, 40, 4, 56, 4, 2, 1, 6, 2, 10, 2, 14, 2, 20, 3, 28, 3, 40, 4, 56, 4,
-112, 5, -80, 5, -56, 4, -40, 4, -28, 3, -20, 3, -12, 3, -4, 2, -112, 5, -80, 5, -56, 4, -40, 4, -28, 3, -20, 3, -12, 3, -4, 2,
4, 2, 12, 3, 20, 3, 28, 3, 40, 4, 56, 4, 80, 5, 112, 5, 4, 2, 12, 3, 20, 3, 28, 3, 40, 4, 56, 4, 80, 5, 112, 5,
-224, 6, -160, 6, -112, 5, -80, 5, -56, 4, -40, 4, -24, 4, -8, 3, -224, 6, -160, 6, -112, 5, -80, 5, -56, 4, -40, 4, -24, 4, -8, 3,
8, 3, 24, 4, 40, 4, 56, 4, 80, 5, 112, 5, 160, 6, 224, 6, 8, 3, 24, 4, 40, 4, 56, 4, 80, 5, 112, 5, 160, 6, 224, 6,
-448, 7, -320, 7, -224, 6, -160, 6, -112, 5, -80, 5, -48, 5, -16, 4, -448, 7, -320, 7, -224, 6, -160, 6, -112, 5, -80, 5, -48, 5, -16, 4,
16, 4, 48, 5, 80, 5, 112, 5, 160, 6, 224, 6, 320, 7, 448, 7, 16, 4, 48, 5, 80, 5, 112, 5, 160, 6, 224, 6, 320, 7, 448, 7,
-896, 8, -640, 8, -448, 7, -320, 7, -224, 6, -160, 6, -96, 6, -32, 5, -896, 8, -640, 8, -448, 7, -320, 7, -224, 6, -160, 6, -96, 6, -32, 5,
32, 5, 96, 6, 160, 6, 224, 6, 320, 7, 448, 7, 640, 8, 896, 8, 32, 5, 96, 6, 160, 6, 224, 6, 320, 7, 448, 7, 640, 8, 896, 8,
-1792, 9, -1280, 9, -896, 8, -640, 8, -448, 7, -320, 7, -192, 7, -64, 6, -1792, 9, -1280, 9, -896, 8, -640, 8, -448, 7, -320, 7, -192, 7, -64, 6,
64, 6, 192, 7, 320, 7, 448, 7, 640, 8, 896, 8, 1280, 9, 1792, 9, 64, 6, 192, 7, 320, 7, 448, 7, 640, 8, 896, 8, 1280, 9, 1792, 9,
-3584, 10, -2560, 10, -1792, 9, -1280, 9, -896, 8, -640, 8, -384, 8, -128, 7, -3584, 10, -2560, 10, -1792, 9, -1280, 9, -896, 8, -640, 8, -384, 8, -128, 7,
128, 7, 384, 8, 640, 8, 896, 8, 1280, 9, 1792, 9, 2560, 10, 3584, 10, 128, 7, 384, 8, 640, 8, 896, 8, 1280, 9, 1792, 9, 2560, 10, 3584, 10,
-7168, 11, -5120, 11, -3584, 10, -2560, 10, -1792, 9, -1280, 9, -768, 9, -256, 8, -7168, 11, -5120, 11, -3584, 10, -2560, 10, -1792, 9, -1280, 9, -768, 9, -256, 8,
256, 8, 768, 9, 1280, 9, 1792, 9, 2560, 10, 3584, 10, 5120, 11, 7168, 11, 256, 8, 768, 9, 1280, 9, 1792, 9, 2560, 10, 3584, 10, 5120, 11, 7168, 11,
-14336, 11, -10240, 11, -7168, 11, -5120, 11, -3584, 10, -2560, 10, -1536, 10, -512, 9, -14336, 11, -10240, 11, -7168, 11, -5120, 11, -3584, 10, -2560, 10, -1536, 10, -512, 9,
512, 9, 1536, 10, 2560, 10, 3584, 10, 5120, 11, 7168, 11, 10240, 11, 14336, 11, 512, 9, 1536, 10, 2560, 10, 3584, 10, 5120, 11, 7168, 11, 10240, 11, 14336, 11,
-28672, 11, -20480, 11, -14336, 11, -10240, 11, -7168, 11, -5120, 11, -3072, 11, -1024, 10, -28672, 11, -20480, 11, -14336, 11, -10240, 11, -7168, 11, -5120, 11, -3072, 11, -1024, 10,
1024, 10, 3072, 11, 5120, 11, 7168, 11, 10240, 11, 14336, 11, 20480, 11, 28672, 11, 1024, 10, 3072, 11, 5120, 11, 7168, 11, 10240, 11, 14336, 11, 20480, 11, 28672, 11,
/* rest is 0s */ /* rest is 0s */
}; };

View File

@ -0,0 +1,582 @@
#include "coding.h"
/* Decodes Ubisoft ADPCM, a rather complex codec with 4-bit (usually music) and 6-bit (usually voices/sfx)
* mono or stereo modes, using multiple tables and temp step/delta values.
*
* Base reverse engineering by Zench: https://bitbucket.org/Zenchreal/decubisnd
* Original ASM MMX/intrinsics to C++ by sigsegv; adapted by bnnm; special thanks to Nicknine.
*
* Data always starts with a 0x30 main header (some games have extra data before too), then frames of
* fixed size: 0x34 ADPCM setup per channel, 1 subframe + 1 padding byte, then another subframe and 1 byte.
* Subframes have 1536 samples or less (like 1024), typical sizes are 0x600 for 4-bit or 0x480 for 6-bit.
* Last frame can contain only one subframe, with less codes than normal (may use padding). Nibbles/codes
* are packed as 32-bit LE with 6-bit or 4-bit codes for all channels (processes kinda like joint stereo).
*/
#define UBI_CHANNELS_MIN 1
#define UBI_CHANNELS_MAX 2
#define UBI_SUBFRAMES_PER_FRAME_MAX 2
#define UBI_CODES_PER_SUBFRAME_MAX 1536 /* for all channels */
#define UBI_FRAME_SIZE_MAX (0x34 * UBI_CHANNELS_MAX + (UBI_CODES_PER_SUBFRAME_MAX * 6 / 8 + 0x1) * UBI_SUBFRAMES_PER_FRAME_MAX)
#define UBI_SAMPLES_PER_FRAME_MAX (UBI_CODES_PER_SUBFRAME_MAX * UBI_SUBFRAMES_PER_FRAME_MAX)
typedef struct {
uint32_t signature;
uint32_t sample_count;
uint32_t subframe_count;
uint32_t codes_per_subframe_last;
uint32_t codes_per_subframe;
uint32_t subframes_per_frame;
uint32_t sample_rate;
uint32_t unknown1c;
uint32_t unknown20;
uint32_t bits_per_sample;
uint32_t unknown28;
uint32_t channels;
} ubi_adpcm_header_data;
typedef struct {
uint32_t signature;
int32_t step1;
int32_t next1;
int32_t next2;
int16_t coef1;
int16_t coef2;
int16_t unused1;
int16_t unused2;
int16_t mod1;
int16_t mod2;
int16_t mod3;
int16_t mod4;
int16_t hist1;
int16_t hist2;
int16_t unused3;
int16_t unused4;
int16_t delta1;
int16_t delta2;
int16_t delta3;
int16_t delta4;
int16_t delta5;
int16_t unused5;
} ubi_adpcm_channel_data;
struct ubi_adpcm_codec_data {
ubi_adpcm_header_data header;
ubi_adpcm_channel_data ch[UBI_CHANNELS_MAX];
off_t start_offset;
off_t offset;
int subframe_number;
uint8_t frame[UBI_FRAME_SIZE_MAX];
uint8_t codes[UBI_CODES_PER_SUBFRAME_MAX];
int16_t samples[UBI_SAMPLES_PER_FRAME_MAX]; /* for all channels, saved in L-R-L-R form */
size_t samples_filled;
size_t samples_consumed;
size_t samples_to_discard;
};
/* *********************************************************************** */
static int parse_header(STREAMFILE* sf, ubi_adpcm_codec_data *data, off_t offset);
static void decode_frame(STREAMFILE* sf, ubi_adpcm_codec_data *data);
ubi_adpcm_codec_data *init_ubi_adpcm(STREAMFILE *sf, off_t offset, int channels) {
ubi_adpcm_codec_data *data = NULL;
data = calloc(1, sizeof(ubi_adpcm_codec_data));
if (!data) goto fail;
if (!parse_header(sf, data, offset))
goto fail;
if (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);
return NULL;
}
void decode_ubi_adpcm(VGMSTREAM * vgmstream, sample_t * outbuf, int32_t samples_to_do) {
STREAMFILE* sf = vgmstream->ch[0].streamfile;
ubi_adpcm_codec_data *data = vgmstream->codec_data;
uint32_t channels = data->header.channels;
int samples_done = 0;
/* Ubi ADPCM frames are rather big, so we decode then copy to outbuf until done */
while (samples_done < samples_to_do) {
if (data->samples_filled) {
int samples_to_get = data->samples_filled;
if (data->samples_to_discard) {
/* discard samples for looping */
if (samples_to_get > data->samples_to_discard)
samples_to_get = data->samples_to_discard;
data->samples_to_discard -= samples_to_get;
}
else {
/* get max samples and copy */
if (samples_to_get > samples_to_do - samples_done)
samples_to_get = samples_to_do - samples_done;
memcpy(outbuf + samples_done*channels,
data->samples + data->samples_consumed*channels,
samples_to_get*channels * sizeof(sample));
samples_done += samples_to_get;
}
/* mark consumed samples */
data->samples_consumed += samples_to_get;
data->samples_filled -= samples_to_get;
}
else {
decode_frame(sf, data);
}
}
}
void reset_ubi_adpcm(ubi_adpcm_codec_data *data) {
if (!data) return;
data->offset = data->start_offset;
data->subframe_number = 0;
}
void seek_ubi_adpcm(ubi_adpcm_codec_data *data, int32_t num_sample) {
if (!data) return;
//todo improve by seeking to closest frame
reset_ubi_adpcm(data);
data->samples_to_discard = num_sample;
}
void free_ubi_adpcm(ubi_adpcm_codec_data *data) {
if (!data)
return;
free(data);
}
/* ************************************************************************ */
static void read_header_state(uint8_t *data, ubi_adpcm_header_data *header) {
header->signature = get_32bitLE(data + 0x00);
header->sample_count = get_32bitLE(data + 0x04);
header->subframe_count = get_32bitLE(data + 0x08);
header->codes_per_subframe_last= get_32bitLE(data + 0x0c);
header->codes_per_subframe = get_32bitLE(data + 0x10);
header->subframes_per_frame = get_32bitLE(data + 0x14);
header->sample_rate = get_32bitLE(data + 0x18); /* optional? */
header->unknown1c = get_32bitLE(data + 0x1c); /* variable */
header->unknown20 = get_32bitLE(data + 0x20); /* null? */
header->bits_per_sample = get_32bitLE(data + 0x24);
header->unknown28 = get_32bitLE(data + 0x28); /* 1~3? */
header->channels = get_32bitLE(data + 0x2c);
}
static int parse_header(STREAMFILE* sf, ubi_adpcm_codec_data *data, off_t offset) {
uint8_t buf[0x30];
size_t bytes;
bytes = read_streamfile(buf, offset, 0x30, sf);
if (bytes != 0x30) goto fail;
read_header_state(buf, &data->header);
if (data->header.signature != 0x08)
goto fail;
if (data->header.codes_per_subframe_last > UBI_CODES_PER_SUBFRAME_MAX ||
data->header.codes_per_subframe > UBI_CODES_PER_SUBFRAME_MAX)
goto fail;
if (data->header.subframes_per_frame != UBI_SUBFRAMES_PER_FRAME_MAX)
goto fail;
if (data->header.bits_per_sample != 4 && data->header.bits_per_sample != 6)
goto fail;
if (data->header.channels > UBI_CHANNELS_MAX || data->header.channels < UBI_CHANNELS_MIN)
goto fail;
return 1;
fail:
return 0;
}
/* *********************************************************************** */
int32_t adpcm6_table1[64] = {
-100000000, -369, -245, -133, -33, 56, 135, 207,
275, 338, 395, 448, 499, 548, 593, 635,
676, 717, 755, 791, 825, 858, 889, 919,
948, 975, 1003, 1029, 1054, 1078, 1103, 1132,
/* probably unused (partly spilled from next table) */
1800, 1800, 1800, 2048, 3072, 4096, 5000, 5056,
5184, 5240, 6144, 6880, 9624, 12880, 14952, 18040,
20480, 22920, 25600, 28040, 32560, 35840, 40960, 45832,
51200, 56320, 63488, 67704, 75776, 89088, 102400, 0,
};
int32_t adpcm6_table2[64] = {
1800, 1800, 1800, 2048, 3072, 4096, 5000, 5056,
5184, 5240, 6144, 6880, 9624, 12880, 14952, 18040,
20480, 22920, 25600, 28040, 32560, 35840, 40960, 45832,
51200, 56320, 63488, 67704, 75776, 89088, 102400, 0,
/* probably unused */
0, 0, 0, 0, 0, 0, 0, 0,
0, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 2, 2, 3, 3, 4,
4, 5, 5, 5, 6, 6, 6, 7,
};
int32_t adpcm4_table1[16] = {
-100000000, 8, 269, 425, 545, 645, 745, 850,
/* probably unused */
-1082465976, 1058977874, 1068540887, 1072986849, 1075167887, 1076761723, 1078439444, 1203982336,
};
int32_t adpcm4_table2[16] = {
-1536, 2314, 5243, 8192, 14336, 25354, 45445, 143626,
/* probably unused */
0, 0, 0, 1, 1, 1, 3, 7,
};
int32_t delta_table[33+33] = {
1024, 1031, 1053, 1076, 1099, 1123, 1148, 1172,
1198, 1224, 1251, 1278, 1306, 1334, 1363, 1393,
1423, 1454, 1485, 1518, 1551, 1584, 1619, 1654,
1690, 1726, 1764, 1802, 1841, 1881, 1922, 1964,
2007,
-1024,-1031,-1053,-1076,-1099,-1123,-1148,-1172,
-1198,-1224,-1251,-1278,-1306,-1334,-1363,-1393,
-1423,-1454,-1485,-1518,-1551,-1584,-1619,-1654,
-1690,-1726,-1764,-1802,-1841,-1881,-1922,-1964,
-2007
};
static int sign16(int16_t test) {
return (test < 0 ? -1 : 1);
}
static int sign32(int32_t test) {
return (test < 0 ? -1 : 1);
}
static int16_t absmax16(int16_t val, int16_t absmax) {
if (val < 0) {
if (val < -absmax) return -absmax;
} else {
if (val > absmax) return absmax;
}
return val;
}
static int32_t clamp_val(int32_t val, int32_t min, int32_t max) {
if (val < min) return min;
else if (val > max) return max;
else return val;
}
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 sample_new;
step0_index = abs(code - 31); /* 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;
step0 = clamp_val(step0, 271, 2560);
delta0 = 0;
if (!(((step0_next & 0xFFFFFF00) - 1) & (1 << 31))) {
int delta0_index = ((step0_next >> 3) & 0x1F) + (code < 31 ? 33 : 0);
int delta0_shift = clamp_val((step0_next >> 8) & 0xFF, 0,31);
delta0 = (delta_table[delta0_index] << delta0_shift) >> 10;
}
sample_new = (int16_t)(delta0 + state->delta1 + state->hist1);
state->hist1 = sample_new;
state->step1 = step0;
state->delta1 = delta0;
VGM_ASSERT(step0_index > 31, "UBI ADPCM: index over 31\n");
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 sample_new;
step0_index = abs(code - 7); /* 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;
step0 = clamp_val(step0, 271, 2560);
delta0 = 0;
if (!(((step0_next & 0xFFFFFF00) - 1) & (1 << 31))) {
int delta0_index = ((step0_next >> 3) & 0x1F) + (code < 7 ? 33 : 0);
int delta0_shift = clamp_val((step0_next >> 8) & 0xFF, 0,31);
delta0 = (delta_table[delta0_index] << delta0_shift) >> 10;
}
next0 = (int16_t)((
(state->mod1 * state->delta1) + (state->mod2 * state->delta2) +
(state->mod3 * state->delta3) + (state->mod4 * state->delta4) ) >> 10);
sample_new = ((state->coef1 * state->hist1) + (state->coef2 * state->hist2)) >> 10;
sample_new = (int16_t)(delta0 + next0 + sample_new);
coef1_next = state->coef1 * 255;
coef2_next = state->coef2 * 254;
delta0 = (int16_t)delta0;
if (delta0 + next0 != 0) {
int32_t sign1, sign2, coef_delta;
sign1 = sign32(delta0 + next0) * sign32(state->delta1 + state->next1);
sign2 = sign32(delta0 + next0) * sign32(state->delta2 + state->next2);
coef_delta = (int16_t)((((sign1 * 3072) + coef1_next) >> 6) & ~0x3);
coef_delta = clamp16(clamp16(coef_delta + 30719) - 30719); //???
coef_delta = clamp16(clamp16(coef_delta + -30720) - -30720); //???
coef_delta = ((int16_t)(sign2 * 1024) - (int16_t)(sign1 * coef_delta)) * 2;
coef1_next += sign1 * 3072;
coef2_next += coef_delta;
}
state->hist2 = state->hist1;
state->hist1 = sample_new;
state->coef2 = absmax16((int16_t)(coef2_next >> 8), 768);
state->coef1 = absmax16((int16_t)(coef1_next >> 8), 960 - state->coef2);
state->next2 = state->next1;
state->next1 = next0;
state->step1 = step0;
state->delta5 = state->delta4;
state->delta4 = state->delta3;
state->delta3 = state->delta2;
state->delta2 = state->delta1;
state->delta1 = delta0;
state->mod4 = clamp16(state->mod4 * 255 + 2048 * sign16(state->delta1) * sign16(state->delta5)) >> 8;
state->mod3 = clamp16(state->mod3 * 255 + 2048 * sign16(state->delta1) * sign16(state->delta4)) >> 8;
state->mod2 = clamp16(state->mod2 * 255 + 2048 * sign16(state->delta1) * sign16(state->delta3)) >> 8;
state->mod1 = clamp16(state->mod1 * 255 + 2048 * sign16(state->delta1) * sign16(state->delta2)) >> 8;
VGM_ASSERT(step0_index > 7, "UBI ADPCM: index over 7\n");
return sample_new;
}
static void decode_subframe_mono(ubi_adpcm_channel_data* ch_state, uint8_t* codes, int16_t* samples, int code_count, int bps) {
int i;
int16_t (*expand_code)(uint8_t,ubi_adpcm_channel_data*) = NULL;
if (bps == 6)
expand_code = expand_code_6bit;
else
expand_code = expand_code_4bit;
for (i = 0; i < code_count; i++) {
samples[i] = expand_code(codes[i], ch_state);
}
}
static void decode_subframe_stereo(ubi_adpcm_channel_data* ch0_state, ubi_adpcm_channel_data* ch1_state, uint8_t* codes, int16_t* samples, int code_count, int bps) {
int i;
int16_t (*expand_code)(uint8_t,ubi_adpcm_channel_data*) = NULL;
if (bps == 6)
expand_code = expand_code_6bit;
else
expand_code = expand_code_4bit;
for (i = 0; i < code_count; i += 8) {
samples[i + 0] = expand_code(codes[i + 0], ch0_state);
samples[i + 1] = expand_code(codes[i + 2], ch0_state);
samples[i + 2] = expand_code(codes[i + 4], ch0_state);
samples[i + 3] = expand_code(codes[i + 6], ch0_state);
samples[i + 4] = expand_code(codes[i + 1], ch1_state);
samples[i + 5] = expand_code(codes[i + 3], ch1_state);
samples[i + 6] = expand_code(codes[i + 5], ch1_state);
samples[i + 7] = expand_code(codes[i + 7], ch1_state);
}
for (i = 0; i < code_count; i += 8) {
int16_t samples_old[8];
memcpy(samples_old, samples, sizeof(samples_old));
samples[0] = clamp16(samples_old[0] + samples_old[4]);
samples[1] = clamp16(samples_old[0] - samples_old[4]);
samples[2] = clamp16(samples_old[1] + samples_old[5]);
samples[3] = clamp16(samples_old[1] - samples_old[5]);
samples[4] = clamp16(samples_old[2] + samples_old[6]);
samples[5] = clamp16(samples_old[2] - samples_old[6]);
samples[6] = clamp16(samples_old[3] + samples_old[7]);
samples[7] = clamp16(samples_old[3] - samples_old[7]);
samples += 8;
}
}
/* unpack uint32 LE data into 4/6-bit codes:
* - for 4-bit, 32b contain 8 codes
* 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
*/
void unpack_codes(uint8_t *data, uint8_t* codes, int code_count, int bps) {
int i;
size_t pos = 0;
uint64_t bits = 0, input = 0;
const uint64_t mask = (bps == 6) ? 0x3f : 0x0f;
for (i = 0; i < code_count; i++) {
if (bits < bps) {
uint32_t source32le = (uint32_t)get_32bitLE(data + pos);
pos += 0x04;
input = (input << 32) | (uint64_t)source32le;
bits += 32;
}
bits -= bps;
codes[i] = (uint8_t)((input >> bits) & mask);
}
}
static void read_channel_state(uint8_t *data, ubi_adpcm_channel_data *ch) {
/* ADPCM frame state, some fields are unused and contain repeated garbage in all frames but
* probably exist for padding (original code uses MMX to operate in multiple 16b at the same time)
* or reserved for other bit modes */
ch->signature = get_32bitLE(data + 0x00);
ch->step1 = get_32bitLE(data + 0x04);
ch->next1 = get_32bitLE(data + 0x08);
ch->next2 = get_32bitLE(data + 0x0c);
ch->coef1 = get_16bitLE(data + 0x10);
ch->coef2 = get_16bitLE(data + 0x12);
ch->unused1 = get_16bitLE(data + 0x14);
ch->unused2 = get_16bitLE(data + 0x16);
ch->mod1 = get_16bitLE(data + 0x18);
ch->mod2 = get_16bitLE(data + 0x1a);
ch->mod3 = get_16bitLE(data + 0x1c);
ch->mod4 = get_16bitLE(data + 0x1e);
ch->hist1 = get_16bitLE(data + 0x20);
ch->hist2 = get_16bitLE(data + 0x22);
ch->unused3 = get_16bitLE(data + 0x24);
ch->unused4 = get_16bitLE(data + 0x26);
ch->delta1 = get_16bitLE(data + 0x28);
ch->delta2 = get_16bitLE(data + 0x2a);
ch->delta3 = get_16bitLE(data + 0x2c);
ch->delta4 = get_16bitLE(data + 0x2e);
ch->delta5 = get_16bitLE(data + 0x30);
ch->unused5 = get_16bitLE(data + 0x32);
VGM_ASSERT(ch->signature != 0x02, "UBI ADPCM: incorrect channel header\n");
VGM_ASSERT(ch->unused3 != 0x00, "UBI ADPCM: found unused3 used\n");
VGM_ASSERT(ch->unused4 != 0x00, "UBI ADPCM: found unused4 used\n");
}
static void decode_frame(STREAMFILE* sf, ubi_adpcm_codec_data *data) {
int code_count_a, code_count_b;
size_t subframe_size_a, subframe_size_b, frame_size, bytes;
int bps = data->header.bits_per_sample;
int channels = data->header.channels;
/* last frame is shorter (subframe A or B may not exist), avoid over-reads in bigfiles */
if (data->subframe_number + 1 == data->header.subframe_count) {
code_count_a = data->header.codes_per_subframe_last;
code_count_b = 0;
} else if (data->subframe_number + 2 == data->header.subframe_count) {
code_count_a = data->header.codes_per_subframe;
code_count_b = data->header.codes_per_subframe_last;
} else {
code_count_a = data->header.codes_per_subframe;
code_count_b = data->header.codes_per_subframe;
}
subframe_size_a = (bps * code_count_a / 8);
if (subframe_size_a) subframe_size_a += 0x01;
subframe_size_b = (bps * code_count_b / 8);
if (subframe_size_b) subframe_size_b += 0x01;
frame_size = 0x34 * channels + subframe_size_a + subframe_size_b;
//todo check later games (ex. Myst IV) if they handle this
/* last frame can have an odd number of codes, with data ending not aligned to 32b,
* but RE'd code unpacking and stereo decoding always assume to be aligned, causing clicks in some cases
* (if data ends in 0xEE it'll try to do 0x000000EE, but only unpack codes 0 0, thus ignoring actual last 2) */
//memset(data->frame, 0, sizeof(data->frame));
//memset(data->codes, 0, sizeof(data->codes));
//memset(data->samples, 0, sizeof(data->samples));
bytes = read_streamfile(data->frame, data->offset, frame_size, sf);
if (bytes != frame_size) {
VGM_LOG("UBI ADPCM: wrong bytes read %x vs %x at %lx\n", bytes, frame_size, data->offset);
//goto fail; //?
}
if (channels == 1) {
read_channel_state(data->frame + 0x00, &data->ch[0]);
unpack_codes(data->frame + 0x34, data->codes, code_count_a, bps);
decode_subframe_mono(&data->ch[0], data->codes, &data->samples[0], code_count_a, bps);
unpack_codes(data->frame + 0x34 + subframe_size_a, data->codes, code_count_b, bps);
decode_subframe_mono(&data->ch[0], data->codes, &data->samples[code_count_a], code_count_b, bps);
}
else if (channels == 2) {
read_channel_state(data->frame + 0x00, &data->ch[0]);
read_channel_state(data->frame + 0x34, &data->ch[1]);
unpack_codes(data->frame + 0x68, data->codes, code_count_a, bps);
decode_subframe_stereo(&data->ch[0], &data->ch[1], data->codes, &data->samples[0], code_count_a, bps);
unpack_codes(data->frame + 0x68 + subframe_size_a, data->codes, code_count_b, bps);
decode_subframe_stereo(&data->ch[0], &data->ch[1], data->codes, &data->samples[code_count_a], code_count_b, bps);
}
/* frame done */
data->offset += frame_size;
data->subframe_number += 2;
data->samples_consumed = 0;
data->samples_filled = (code_count_a + code_count_b) / channels;
}

View File

@ -693,6 +693,7 @@ static const coding_info coding_info_list[] = {
{coding_ACM, "InterPlay ACM"}, {coding_ACM, "InterPlay ACM"},
{coding_NWA, "VisualArt's NWA DPCM"}, {coding_NWA, "VisualArt's NWA DPCM"},
{coding_CIRCUS_ADPCM, "Circus 8-bit ADPCM"}, {coding_CIRCUS_ADPCM, "Circus 8-bit ADPCM"},
{coding_UBI_ADPCM, "Ubisoft 4/6-bit ADPCM"},
{coding_EA_MT, "Electronic Arts MicroTalk"}, {coding_EA_MT, "Electronic Arts MicroTalk"},
@ -806,7 +807,7 @@ static const meta_info meta_info_list[] = {
{meta_FWAV, "Nintendo FWAV header"}, {meta_FWAV, "Nintendo FWAV header"},
{meta_XA, "Sony XA header"}, {meta_XA, "Sony XA header"},
{meta_PS2_RXWS, "Sony RXWS header"}, {meta_PS2_RXWS, "Sony RXWS header"},
{meta_PS2_RAW, ".int PCM raw header"}, {meta_RAW_INT, "PS2 .int raw header"},
{meta_PS2_OMU, "Alter Echo OMU Header"}, {meta_PS2_OMU, "Alter Echo OMU Header"},
{meta_DSP_STM, "Intelligent Systems STM header"}, {meta_DSP_STM, "Intelligent Systems STM header"},
{meta_PS2_EXST, "Sony EXST header"}, {meta_PS2_EXST, "Sony EXST header"},
@ -820,7 +821,7 @@ static const meta_info meta_info_list[] = {
{meta_DSP_GCM, "Double DSP header stereo by .gcm extension"}, {meta_DSP_GCM, "Double DSP header stereo by .gcm extension"},
{meta_IDSP_TT, "Traveller's Tales IDSP header"}, {meta_IDSP_TT, "Traveller's Tales IDSP header"},
{meta_RSTM_SPM, "Nintendo RSTM header (brstmspm)"}, {meta_RSTM_SPM, "Nintendo RSTM header (brstmspm)"},
{meta_RAW, "assumed RAW PCM file by .raw extension"}, {meta_RAW_PCM, "PC .raw raw header"},
{meta_PS2_VAGi, "Sony VAGi header"}, {meta_PS2_VAGi, "Sony VAGi header"},
{meta_PS2_VAGp, "Sony VAGp header"}, {meta_PS2_VAGp, "Sony VAGp header"},
{meta_PS2_pGAV, "Sony pGAV header"}, {meta_PS2_pGAV, "Sony pGAV header"},
@ -828,7 +829,7 @@ static const meta_info meta_info_list[] = {
{meta_STR_WAV, "Blitz Games .STR+WAV header"}, {meta_STR_WAV, "Blitz Games .STR+WAV header"},
{meta_PS2_ILD, "ILD header"}, {meta_PS2_ILD, "ILD header"},
{meta_PS2_PNB, "assumed PNB (PsychoNauts Bgm File) by .pnb extension"}, {meta_PS2_PNB, "assumed PNB (PsychoNauts Bgm File) by .pnb extension"},
{meta_XBOX_WAVM, "Xbox WAVM raw header"}, {meta_RAW_WAVM, "Xbox .wavm raw header"},
{meta_DSP_STR, "assumed Conan Gamecube STR File by .str extension"}, {meta_DSP_STR, "assumed Conan Gamecube STR File by .str extension"},
{meta_EA_SCHL, "Electronic Arts SCHl header (variable)"}, {meta_EA_SCHL, "Electronic Arts SCHl header (variable)"},
{meta_EA_SCHL_fixed, "Electronic Arts SCHl header (fixed)"}, {meta_EA_SCHL_fixed, "Electronic Arts SCHl header (fixed)"},
@ -902,8 +903,8 @@ static const meta_info meta_info_list[] = {
{meta_VS, "Melbourne House .VS header"}, {meta_VS, "Melbourne House .VS header"},
{meta_DC_STR, "Sega Stream Asset Builder header"}, {meta_DC_STR, "Sega Stream Asset Builder header"},
{meta_DC_STR_V2, "variant of Sega Stream Asset Builder header"}, {meta_DC_STR_V2, "variant of Sega Stream Asset Builder header"},
{meta_XBOX_XMU, "XMU header"}, {meta_XMU, "Outrage XMU header"},
{meta_XBOX_XVAS, "Konami .XVAS header"}, {meta_XVAS, "Konami .XVAS header"},
{meta_PS2_XA2, "Acclaim XA2 Header"}, {meta_PS2_XA2, "Acclaim XA2 Header"},
{meta_DC_IDVI, "Capcom IDVI header"}, {meta_DC_IDVI, "Capcom IDVI header"},
{meta_KRAW, "Geometry Wars: Galaxies KRAW header"}, {meta_KRAW, "Geometry Wars: Galaxies KRAW header"},
@ -1035,7 +1036,7 @@ static const meta_info meta_info_list[] = {
{meta_VS_STR, "Square .VS STR* header"}, {meta_VS_STR, "Square .VS STR* header"},
{meta_LSF_N1NJ4N, ".lsf !n1nj4n header"}, {meta_LSF_N1NJ4N, ".lsf !n1nj4n header"},
{meta_VAWX, "feelplus VAWX header"}, {meta_VAWX, "feelplus VAWX header"},
{meta_PC_SNDS, "assumed Heavy Iron IMA by .snds extension"}, {meta_RAW_SNDS, "PC .snds raw header"},
{meta_PS2_WMUS, "assumed The Warriors Sony ADPCM by .wmus extension"}, {meta_PS2_WMUS, "assumed The Warriors Sony ADPCM by .wmus extension"},
{meta_HYPERSCAN_KVAG, "Mattel Hyperscan KVAG"}, {meta_HYPERSCAN_KVAG, "Mattel Hyperscan KVAG"},
{meta_IOS_PSND, "PSND Header"}, {meta_IOS_PSND, "PSND Header"},

View File

@ -252,6 +252,10 @@
RelativePath=".\meta\bar_streamfile.h" RelativePath=".\meta\bar_streamfile.h"
> >
</File> </File>
<File
RelativePath=".\meta\bgw_streamfile.h"
>
</File>
<File <File
RelativePath=".\meta\ea_eaac_streamfile.h" RelativePath=".\meta\ea_eaac_streamfile.h"
> >
@ -1015,7 +1019,7 @@
> >
</File> </File>
<File <File
RelativePath=".\meta\pc_snds.c" RelativePath=".\meta\raw_snds.c"
> >
</File> </File>
<File <File
@ -1135,7 +1139,7 @@
> >
</File> </File>
<File <File
RelativePath=".\meta\ps2_int.c" RelativePath=".\meta\raw_int.c"
> >
</File> </File>
<File <File
@ -1399,7 +1403,7 @@
> >
</File> </File>
<File <File
RelativePath=".\meta\raw.c" RelativePath=".\meta\raw_pcm.c"
> >
</File> </File>
<File <File
@ -1727,15 +1731,15 @@
> >
</File> </File>
<File <File
RelativePath=".\meta\xbox_wavm.c" RelativePath=".\meta\raw_wavm.c"
> >
</File> </File>
<File <File
RelativePath=".\meta\xbox_xmu.c" RelativePath=".\meta\xmu.c"
> >
</File> </File>
<File <File
RelativePath=".\meta\xbox_xvas.c" RelativePath=".\meta\xvas.c"
> >
</File> </File>
<File <File
@ -2034,10 +2038,14 @@
RelativePath=".\coding\sassc_decoder.c" RelativePath=".\coding\sassc_decoder.c"
> >
</File> </File>
<File <File
RelativePath=".\coding\sdx2_decoder.c" RelativePath=".\coding\sdx2_decoder.c"
> >
</File> </File>
<File
RelativePath=".\coding\ubi_adpcm_decoder.c"
>
</File>
<File <File
RelativePath=".\coding\ws_decoder.c" RelativePath=".\coding\ws_decoder.c"
> >

View File

@ -106,6 +106,7 @@
<ClInclude Include="meta\aix_streamfile.h" /> <ClInclude Include="meta\aix_streamfile.h" />
<ClInclude Include="meta\awc_xma_streamfile.h" /> <ClInclude Include="meta\awc_xma_streamfile.h" />
<ClInclude Include="meta\bar_streamfile.h" /> <ClInclude Include="meta\bar_streamfile.h" />
<ClInclude Include="meta\bgw_streamfile.h" />
<ClInclude Include="meta\ea_eaac_streamfile.h" /> <ClInclude Include="meta\ea_eaac_streamfile.h" />
<ClInclude Include="meta\ea_schl_streamfile.h" /> <ClInclude Include="meta\ea_schl_streamfile.h" />
<ClInclude Include="meta\fsb_interleave_streamfile.h" /> <ClInclude Include="meta\fsb_interleave_streamfile.h" />
@ -184,7 +185,7 @@
<ClCompile Include="meta\pc_adp.c" /> <ClCompile Include="meta\pc_adp.c" />
<ClCompile Include="meta\pc_adp_otns.c" /> <ClCompile Include="meta\pc_adp_otns.c" />
<ClCompile Include="meta\pc_ast.c" /> <ClCompile Include="meta\pc_ast.c" />
<ClCompile Include="meta\pc_snds.c" /> <ClCompile Include="meta\raw_snds.c" />
<ClCompile Include="meta\ps2_2pfs.c" /> <ClCompile Include="meta\ps2_2pfs.c" />
<ClCompile Include="meta\ps2_hsf.c" /> <ClCompile Include="meta\ps2_hsf.c" />
<ClCompile Include="meta\ps2_iab.c" /> <ClCompile Include="meta\ps2_iab.c" />
@ -391,7 +392,7 @@
<ClCompile Include="meta\ps2_hgc1.c" /> <ClCompile Include="meta\ps2_hgc1.c" />
<ClCompile Include="meta\ikm.c" /> <ClCompile Include="meta\ikm.c" />
<ClCompile Include="meta\ps2_ild.c" /> <ClCompile Include="meta\ps2_ild.c" />
<ClCompile Include="meta\ps2_int.c" /> <ClCompile Include="meta\raw_int.c" />
<ClCompile Include="meta\ps2_joe.c" /> <ClCompile Include="meta\ps2_joe.c" />
<ClCompile Include="meta\jstm.c" /> <ClCompile Include="meta\jstm.c" />
<ClCompile Include="meta\ps2_kces.c" /> <ClCompile Include="meta\ps2_kces.c" />
@ -442,7 +443,7 @@
<ClCompile Include="meta\ffdl.c" /> <ClCompile Include="meta\ffdl.c" />
<ClCompile Include="meta\seb.c" /> <ClCompile Include="meta\seb.c" />
<ClCompile Include="meta\ea_swvr.c" /> <ClCompile Include="meta\ea_swvr.c" />
<ClCompile Include="meta\raw.c" /> <ClCompile Include="meta\raw_pcm.c" />
<ClCompile Include="meta\redspark.c" /> <ClCompile Include="meta\redspark.c" />
<ClCompile Include="meta\rfrm.c" /> <ClCompile Include="meta\rfrm.c" />
<ClCompile Include="meta\riff.c" /> <ClCompile Include="meta\riff.c" />
@ -508,9 +509,9 @@
<ClCompile Include="meta\xavs.c" /> <ClCompile Include="meta\xavs.c" />
<ClCompile Include="meta\xbox_hlwav.c" /> <ClCompile Include="meta\xbox_hlwav.c" />
<ClCompile Include="meta\xbox_ims.c" /> <ClCompile Include="meta\xbox_ims.c" />
<ClCompile Include="meta\xbox_wavm.c" /> <ClCompile Include="meta\raw_wavm.c" />
<ClCompile Include="meta\xbox_xmu.c" /> <ClCompile Include="meta\xmu.c" />
<ClCompile Include="meta\xbox_xvas.c" /> <ClCompile Include="meta\xvas.c" />
<ClCompile Include="meta\x360_pasx.c" /> <ClCompile Include="meta\x360_pasx.c" />
<ClCompile Include="meta\xma.c" /> <ClCompile Include="meta\xma.c" />
<ClCompile Include="meta\xnb.c" /> <ClCompile Include="meta\xnb.c" />
@ -567,6 +568,7 @@
<ClCompile Include="coding\ptadpcm_decoder.c" /> <ClCompile Include="coding\ptadpcm_decoder.c" />
<ClCompile Include="coding\sassc_decoder.c" /> <ClCompile Include="coding\sassc_decoder.c" />
<ClCompile Include="coding\sdx2_decoder.c" /> <ClCompile Include="coding\sdx2_decoder.c" />
<ClCompile Include="coding\ubi_adpcm_decoder.c" />
<ClCompile Include="coding\vorbis_custom_decoder.c" /> <ClCompile Include="coding\vorbis_custom_decoder.c" />
<ClCompile Include="coding\vorbis_custom_utils_fsb.c" /> <ClCompile Include="coding\vorbis_custom_utils_fsb.c" />
<ClCompile Include="coding\vorbis_custom_utils_ogl.c" /> <ClCompile Include="coding\vorbis_custom_utils_ogl.c" />

View File

@ -86,6 +86,9 @@
<ClInclude Include="meta\bar_streamfile.h"> <ClInclude Include="meta\bar_streamfile.h">
<Filter>meta\Header Files</Filter> <Filter>meta\Header Files</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="meta\bgw_streamfile.h">
<Filter>meta\Header Files</Filter>
</ClInclude>
<ClInclude Include="meta\ea_eaac_streamfile.h"> <ClInclude Include="meta\ea_eaac_streamfile.h">
<Filter>meta\Header Files</Filter> <Filter>meta\Header Files</Filter>
</ClInclude> </ClInclude>
@ -709,7 +712,7 @@
<ClCompile Include="meta\ps2_ild.c"> <ClCompile Include="meta\ps2_ild.c">
<Filter>meta\Source Files</Filter> <Filter>meta\Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="meta\ps2_int.c"> <ClCompile Include="meta\raw_int.c">
<Filter>meta\Source Files</Filter> <Filter>meta\Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="meta\ps2_joe.c"> <ClCompile Include="meta\ps2_joe.c">
@ -862,7 +865,7 @@
<ClCompile Include="meta\ea_swvr.c"> <ClCompile Include="meta\ea_swvr.c">
<Filter>meta\Source Files</Filter> <Filter>meta\Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="meta\raw.c"> <ClCompile Include="meta\raw_pcm.c">
<Filter>meta\Source Files</Filter> <Filter>meta\Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="meta\redspark.c"> <ClCompile Include="meta\redspark.c">
@ -1063,13 +1066,13 @@
<ClCompile Include="meta\xbox_ims.c"> <ClCompile Include="meta\xbox_ims.c">
<Filter>meta\Source Files</Filter> <Filter>meta\Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="meta\xbox_wavm.c"> <ClCompile Include="meta\raw_wavm.c">
<Filter>meta\Source Files</Filter> <Filter>meta\Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="meta\xbox_xmu.c"> <ClCompile Include="meta\xmu.c">
<Filter>meta\Source Files</Filter> <Filter>meta\Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="meta\xbox_xvas.c"> <ClCompile Include="meta\xvas.c">
<Filter>meta\Source Files</Filter> <Filter>meta\Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="meta\xss.c"> <ClCompile Include="meta\xss.c">
@ -1219,6 +1222,9 @@
<ClCompile Include="coding\sdx2_decoder.c"> <ClCompile Include="coding\sdx2_decoder.c">
<Filter>coding\Source Files</Filter> <Filter>coding\Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="coding\ubi_adpcm_decoder.c">
<Filter>coding\Source Files</Filter>
</ClCompile>
<ClCompile Include="coding\ws_decoder.c"> <ClCompile Include="coding\ws_decoder.c">
<Filter>coding\Source Files</Filter> <Filter>coding\Source Files</Filter>
</ClCompile> </ClCompile>
@ -1414,7 +1420,7 @@
<ClCompile Include="meta\vawx.c"> <ClCompile Include="meta\vawx.c">
<Filter>meta\Source Files</Filter> <Filter>meta\Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="meta\pc_snds.c"> <ClCompile Include="meta\raw_snds.c">
<Filter>meta\Source Files</Filter> <Filter>meta\Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="meta\sqex_scd.c"> <ClCompile Include="meta\sqex_scd.c">

View File

@ -71,9 +71,11 @@ fail:
/* ************************************** */ /* ************************************** */
typedef struct { typedef struct {
/* keep track of these tables so they can be closed when done */
utf_context *Header; utf_context *Header;
utf_context *CueNameTable; utf_context *CueNameTable;
utf_context *CueTable; utf_context *CueTable;
utf_context *BlockTable;
utf_context *SequenceTable; utf_context *SequenceTable;
utf_context *TrackTable; utf_context *TrackTable;
utf_context *TrackEventTable; utf_context *TrackEventTable;
@ -81,42 +83,22 @@ typedef struct {
utf_context *SynthTable; utf_context *SynthTable;
utf_context *WaveformTable; utf_context *WaveformTable;
char name[1024]; /* config */
int is_memory; int is_memory;
int target_waveid;
int has_TrackEventTable; int has_TrackEventTable;
int has_CommandTable; int has_CommandTable;
int16_t CueNameIndex; /* to avoid infinite/circular references (AtomViewer crashes otherwise) */
const char* CueName; int synth_depth;
int16_t CueIndex; int sequence_depth;
int16_t ReferenceIndex;
int8_t ReferenceType;
int16_t NumTracks; /* name stuff */
uint32_t TrackIndex_offset; int16_t cuename_index;
uint32_t TrackIndex_size; const char * cuename_name;
int16_t TrackIndex; int awbname_count;
int16_t EventIndex; int16_t awbname_list[255];
uint32_t Command_offset; char name[1024];
uint32_t Command_size;
int16_t SynthIndex_count;
int16_t SynthIndex_list[255];
int16_t SynthIndex;
int8_t SynthType;
uint32_t ReferenceItems_offset;
uint32_t ReferenceItems_size;
int ReferenceItems_count;
int16_t ReferenceItems_list[255];
int16_t ReferenceItem;
int16_t AwbId;
int8_t AwbStreaming;
int is_wave_found;
int AwbName_count;
int16_t AwbName_list[255];
} acb_header; } acb_header;
@ -138,286 +120,406 @@ fail:
return 0; return 0;
} }
static int load_acb_cue_info(STREAMFILE *acbFile, acb_header* acb) {
/* read Cue[CueNameIndex] */ static void add_acb_name(STREAMFILE *acbFile, acb_header* acb, int8_t Waveform_Streaming) {
if (!utf_query_s16(acbFile, acb->CueNameTable, acb->CueNameIndex, "CueIndex", &acb->CueIndex))
goto fail;
if (!utf_query_string(acbFile, acb->CueNameTable, acb->CueNameIndex, "CueName", &acb->CueName))
goto fail;
//;VGM_LOG("ACB: CueName[%i]: CueIndex=%i, CueName=%s\n", acb->CueNameIndex, acb->CueIndex, acb->CueName);
/* read Cue[CueIndex] */
if (!load_utf_subtable(acbFile, acb, &acb->CueTable, "CueTable", NULL))
goto fail;
if (!utf_query_s8 (acbFile, acb->CueTable, acb->CueIndex, "ReferenceType", &acb->ReferenceType))
goto fail;
if (!utf_query_s16(acbFile, acb->CueTable, acb->CueIndex, "ReferenceIndex", &acb->ReferenceIndex))
goto fail;
//;VGM_LOG("ACB: Cue[%i]: ReferenceType=%i, ReferenceIndex=%i\n", acb->CueIndex, acb->ReferenceType, acb->ReferenceIndex);
return 1;
fail:
return 0;
}
static int load_acb_sequence(STREAMFILE *acbFile, acb_header* acb) {
/* read Sequence[ReferenceIndex] */
if (!load_utf_subtable(acbFile, acb, &acb->SequenceTable, "SequenceTable", NULL))
goto fail;
if (!utf_query_s16(acbFile, acb->SequenceTable, acb->ReferenceIndex, "NumTracks", &acb->NumTracks))
goto fail;
if (!utf_query_data(acbFile, acb->SequenceTable, acb->ReferenceIndex, "TrackIndex", &acb->TrackIndex_offset, &acb->TrackIndex_size))
goto fail;
//;VGM_LOG("ACB: Sequence[%i]: NumTracks=%i, TrackIndex={%x, %x}\n", acb->ReferenceIndex, acb->NumTracks, acb->TrackIndex_offset,acb->TrackIndex_size);
if (acb->NumTracks * 0x02 > acb->TrackIndex_size) { /* padding may exist */
VGM_LOG("ACB: unknown TrackIndex size\n");
goto fail;
}
return 1;
fail:
return 0;
}
static int load_acb_track_command(STREAMFILE *acbFile, acb_header* acb) {
/* read Track[TrackIndex] */
if (!load_utf_subtable(acbFile, acb, &acb->TrackTable, "TrackTable", NULL))
goto fail;
if (!utf_query_s16(acbFile, acb->TrackTable, acb->TrackIndex, "EventIndex", &acb->EventIndex))
goto fail;
//;VGM_LOG("ACB: Track[%i]: EventIndex=%i\n", acb->TrackIndex, acb->EventIndex);
/* depending on version next stuff varies a bit, check by table existence */
if (acb->has_TrackEventTable) {
/* read TrackEvent[EventIndex] */
if (!load_utf_subtable(acbFile, acb, &acb->TrackEventTable, "TrackEventTable", NULL))
goto fail;
if (!utf_query_data(acbFile, acb->TrackEventTable, acb->EventIndex, "Command", &acb->Command_offset, &acb->Command_size))
goto fail;
//;VGM_LOG("ACB: TrackEvent[%i]: Command={%x,%x}\n", acb->EventIndex, acb->Command_offset,acb->Command_size);
}
else if (acb->has_CommandTable) {
/* read Command[EventIndex] */
if (!load_utf_subtable(acbFile, acb, &acb->CommandTable, "CommandTable", NULL))
goto fail;
if (!utf_query_data(acbFile, acb->CommandTable, acb->EventIndex, "Command", &acb->Command_offset, &acb->Command_size))
goto fail;
//;VGM_LOG("ACB: Command[%i]: Command={%x,%x}\n", acb->EventIndex, acb->Command_offset,acb->Command_size);
}
else {
VGM_LOG("ACB: unknown command table\n");
}
/* read Command (some kind of multiple TLVs, this seems ok) */
{
uint32_t offset = acb->Command_offset;
uint32_t max_offset = acb->Command_offset + acb->Command_size;
uint16_t code, subcode, subindex;
uint8_t size;
acb->SynthIndex_count = 0;
while (offset < max_offset) {
code = read_u16be(offset + 0x00, acbFile);
size = read_u8 (offset + 0x02, acbFile);
offset += 0x03;
if (code == 0x07D0) {
if (size < 0x04) {
VGM_LOG("ACB: subcommand with unknown size\n");
break;
}
subcode = read_u16be(offset + 0x00, acbFile);
subindex = read_u16be(offset + 0x02, acbFile);
/* reference to Synth/Waveform like those in Synth? */
if (subcode != 0x02) {
//todo some like Yakuza Kiwami 2 usen.acb/Yakuza 6 haichi_amb_siren.acb use 0x03
// ('random' type pointing to Sequence, see Synth)
VGM_LOG("ACB: subcommand with unknown subcode at %x\n", offset);
break;
}
acb->SynthIndex_list[acb->SynthIndex_count] = subindex;
acb->SynthIndex_count++;
if (acb->SynthIndex_count >= 254)
acb->ReferenceItems_count = 254; /* ??? */
//;VGM_LOG("ACB: subcommand index %i found\n", subindex);
}
/* 0x07D1 comes suspiciously often paired with 0x07D0 too */
offset += size;
}
}
return 1;
fail:
return 0;
}
static int load_acb_synth(STREAMFILE *acbFile, acb_header* acb) {
int i;
/* read Synth[SynthIndex] */
if (!load_utf_subtable(acbFile, acb, &acb->SynthTable, "SynthTable", NULL))
goto fail;
if (!utf_query_s8(acbFile, acb->SynthTable, acb->SynthIndex, "Type", &acb->SynthType))
goto fail;
if (!utf_query_data(acbFile, acb->SynthTable, acb->SynthIndex, "ReferenceItems", &acb->ReferenceItems_offset, &acb->ReferenceItems_size))
goto fail;
//;VGM_LOG("ACB: Synth[%i]: ReferenceItems={%x,%x}\n", acb->SynthIndex, acb->ReferenceItems_offset, acb->ReferenceItems_size);
acb->ReferenceItems_count = acb->ReferenceItems_size / 0x04;
if (acb->ReferenceItems_count >= 254)
acb->ReferenceItems_count = 254; /* ??? */
/* ReferenceType 2 uses Synth.Type, while 3 always sets it to 0 and uses Sequence.Type instead
* they probably affect which item in the reference list is picked:
* 0: polyphonic
* 1: sequential
* 2: shuffle
* 3: random
* 4: no repeat
* 5: switch game variable
* 6: combo sequential
* 7: switch selector
* 8: track transition by selector
* other: undefined?
*/
for (i = 0; i < acb->ReferenceItems_count; i++) {
uint16_t type, subtype, index, subindex;
uint32_t suboffset, subsize;
type = read_u16be(acb->ReferenceItems_offset + i*0x04 + 0x00, acbFile);
index = read_u16be(acb->ReferenceItems_offset + i*0x04 + 0x02, acbFile);
//;VGM_LOG("ACB: Synth reference type=%x, index=%x\n", type, index);
switch(type) {
case 0x00: /* no reference */
acb->ReferenceItems_count = 0;
break;
case 0x01: /* Waveform reference (most common) */
acb->ReferenceItems_list[i] = index;
break;
case 0x02: /* Synth reference (rare, found in Sonic Lost World with ReferenceType 2) */
if (!utf_query_data(acbFile, acb->SynthTable, index, "ReferenceItems", &suboffset, &subsize))
goto fail;
/* assuming only 1:1 references are ok */
if (subsize != 0x04) {
VGM_LOG("ACB: unknown Synth subreference size\n");
break;
}
subtype = read_u16be(suboffset + 0x00, acbFile);
subindex = read_u16be(suboffset + 0x02, acbFile);
/* AtomViewer crashes if it points to another to Synth */
if (subtype != 0x01) {
VGM_LOG("ACB: unknown Synth subreference type\n");
break;
}
acb->ReferenceItems_list[i] = subindex;
//;VGM_LOG("ACB: Synth subreference type=%x, index=%x\n", subtype, subindex);
break;
case 0x03: /* random Synths with % in TrackValues (rare, found in Sonic Lost World with ReferenceType 2) */
//todo fix: points to N Sequences (in turn pointing to Tracks > Synths) ex. se_phantom_asteroid.acb
default: /* undefined/crashes AtomViewer */
VGM_LOG("ACB: unknown Synth reference type\n");
acb->ReferenceItems_count = 0;
break;
}
}
return 1;
fail:
return 0;
}
static int load_acb_waveform(STREAMFILE *acbFile, acb_header* acb, int waveid) {
/* read Waveform[ReferenceItem] */
if (!load_utf_subtable(acbFile, acb, &acb->WaveformTable, "WaveformTable", NULL))
goto fail;
if (!utf_query_s8(acbFile, acb->WaveformTable, acb->ReferenceItem, "Streaming", &acb->AwbStreaming))
goto fail;
if (!utf_query_s16(acbFile, acb->WaveformTable, acb->ReferenceItem, "Id", &acb->AwbId)) { /* older versions use Id */
if (acb->is_memory) {
if (!utf_query_s16(acbFile, acb->WaveformTable, acb->ReferenceItem, "MemoryAwbId", &acb->AwbId))
goto fail;
} else {
if (!utf_query_s16(acbFile, acb->WaveformTable, acb->ReferenceItem, "StreamAwbId", &acb->AwbId))
goto fail;
}
}
//;VGM_LOG("ACB: Waveform[%i]: AwbId=%i, AwbStreaming=%i\n", acb->ReferenceItem, acb->AwbId, acb->AwbStreaming);
acb->is_wave_found = 0; /* reset */
if (acb->AwbId != waveid)
return 1;
/* 0=memory, 1=streaming, 2=memory (preload)+stream */
if ((acb->is_memory && acb->AwbStreaming == 1) || (!acb->is_memory && acb->AwbStreaming == 0))
return 1;
acb->is_wave_found = 1;
return 1;
fail:
return 0;
}
static void add_acb_name(STREAMFILE *acbFile, acb_header* acb) {
//todo safe string ops //todo safe string ops
/* aaand finally get name (phew) */
/* ignore name repeats */ /* ignore name repeats */
if (acb->AwbName_count) { if (acb->awbname_count) {
int i; int i;
for (i = 0; i < acb->AwbName_count; i++) { for (i = 0; i < acb->awbname_count; i++) {
if (acb->AwbName_list[i] == acb->CueNameIndex) if (acb->awbname_list[i] == acb->cuename_index)
return; return;
} }
} }
/* since waveforms can be reused by cues multiple names are a thing */ /* since waveforms can be reused by cues multiple names are a thing */
if (acb->AwbName_count) { if (acb->awbname_count) {
strcat(acb->name, "; "); strcat(acb->name, "; ");
strcat(acb->name, acb->CueName); strcat(acb->name, acb->cuename_name);
} }
else { else {
strcpy(acb->name, acb->CueName); strcpy(acb->name, acb->cuename_name);
} }
if (acb->AwbStreaming == 2 && acb->is_memory) { if (Waveform_Streaming == 2 && acb->is_memory) {
strcat(acb->name, " [pre]"); strcat(acb->name, " [pre]");
} }
acb->AwbName_list[acb->AwbName_count] = acb->CueNameIndex; acb->awbname_list[acb->awbname_count] = acb->cuename_index;
acb->AwbName_count++; acb->awbname_count++;
if (acb->AwbName_count >= 254) if (acb->awbname_count >= 254)
acb->AwbName_count = 254; /* ??? */ acb->awbname_count = 254; /* ??? */
//;VGM_LOG("ACB: found cue for waveid=%i: %s\n", acb->AwbId, acb->CueName); //;VGM_LOG("ACB: found cue for waveid=%i: %s\n", acb->target_waveid, acb->cuename_name);
}
static int load_acb_waveform(STREAMFILE *acbFile, acb_header* acb, int16_t Index) {
int16_t Waveform_Id;
int8_t Waveform_Streaming;
/* read Waveform[Index] */
if (!load_utf_subtable(acbFile, acb, &acb->WaveformTable, "WaveformTable", NULL))
goto fail;
if (!utf_query_s16(acbFile, acb->WaveformTable, Index, "Id", &Waveform_Id)) { /* older versions use Id */
if (acb->is_memory) {
if (!utf_query_s16(acbFile, acb->WaveformTable, Index, "MemoryAwbId", &Waveform_Id))
goto fail;
} else {
if (!utf_query_s16(acbFile, acb->WaveformTable, Index, "StreamAwbId", &Waveform_Id))
goto fail;
}
}
if (!utf_query_s8(acbFile, acb->WaveformTable, Index, "Streaming", &Waveform_Streaming))
goto fail;
//;VGM_LOG("ACB: Waveform[%i]: Id=%i, Streaming=%i\n", Index, Waveform_Id, Waveform_Streaming);
/* not found but valid */
if (Waveform_Id != acb->target_waveid)
return 1;
/* must match our target's (0=memory, 1=streaming, 2=memory (prefetch)+stream) */
if ((acb->is_memory && Waveform_Streaming == 1) || (!acb->is_memory && Waveform_Streaming == 0))
return 1;
/* aaand finally get name (phew) */
add_acb_name(acbFile, acb, Waveform_Streaming);
return 1;
fail:
return 0;
}
/* define here for Synths pointing to Sequences */
static int load_acb_sequence(STREAMFILE *acbFile, acb_header* acb, int16_t Index);
static int load_acb_synth(STREAMFILE *acbFile, acb_header* acb, int16_t Index) {
int i, count;
int8_t Synth_Type;
uint32_t Synth_ReferenceItems_offset;
uint32_t Synth_ReferenceItems_size;
/* read Synth[Index] */
if (!load_utf_subtable(acbFile, acb, &acb->SynthTable, "SynthTable", NULL))
goto fail;
if (!utf_query_s8(acbFile, acb->SynthTable, Index, "Type", &Synth_Type))
goto fail;
if (!utf_query_data(acbFile, acb->SynthTable, Index, "ReferenceItems", &Synth_ReferenceItems_offset, &Synth_ReferenceItems_size))
goto fail;
//;VGM_LOG("ACB: Synth[%i]: Type=%x, ReferenceItems={%x,%x}\n", Index, Synth_Type, Synth_ReferenceItems_offset, Synth_ReferenceItems_size);
acb->synth_depth++;
if (acb->synth_depth > 2) {
VGM_LOG("ACB: Synth depth too high\n");
goto fail; /* max Synth > Synth > Waveform (ex. Yakuza 6) */
}
/* Cue.ReferenceType 2 uses Synth.Type, while 3 always sets it to 0 and uses Sequence.Type instead
* Both look the same and probably affect which item in the ReferenceItems list is picked:
* - 0: polyphonic (1 item)
* - 1: sequential (1 to N?)
* - 2: shuffle (1 from N?)
* - 3: random (1 from N?)
* - 4: no repeat
* - 5: switch game variable
* - 6: combo sequential
* - 7: switch selector
* - 8: track transition by selector
* - other: undefined?
* Since we want to find all possible Waveforms that could match our id, we ignore Type and just parse all ReferenceItems.
*/
count = Synth_ReferenceItems_size / 0x04;
for (i = 0; i < count; i++) {
uint16_t Synth_ReferenceItem_type = read_u16be(Synth_ReferenceItems_offset + i*0x04 + 0x00, acbFile);
uint16_t Synth_ReferenceItem_index = read_u16be(Synth_ReferenceItems_offset + i*0x04 + 0x02, acbFile);
//;VGM_LOG("ACB: Synth.ReferenceItem: type=%x, index=%x\n", Synth_ReferenceItem_type, Synth_ReferenceItem_index);
switch(Synth_ReferenceItem_type) {
case 0x00: /* no reference */
count = 0;
break;
case 0x01: /* Waveform (most common) */
if (!load_acb_waveform(acbFile, acb, Synth_ReferenceItem_index))
goto fail;
break;
case 0x02: /* Synth, possibly random (rare, found in Sonic Lost World with ReferenceType 2) */
if (!load_acb_synth(acbFile, acb, Synth_ReferenceItem_index))
goto fail;
break;
case 0x03: /* Sequence of Synths w/ % in Synth.TrackValues (rare, found in Sonic Lost World with ReferenceType 2) */
if (!load_acb_sequence(acbFile, acb, Synth_ReferenceItem_index))
goto fail;
break;
case 0x06: /* this seems to point to Synth but results don't make sense (rare, from Sonic Lost World) */
default: /* undefined/crashes AtomViewer */
VGM_LOG("ACB: unknown Synth.ReferenceItem type %x at %x + %x\n", Synth_ReferenceItem_type, Synth_ReferenceItems_offset, Synth_ReferenceItems_size);
count = 0; /* force end without failing */
break;
}
}
acb->synth_depth--;
return 1;
fail:
return 0;
}
static int load_acb_track_event_command(STREAMFILE *acbFile, acb_header* acb, int16_t Index) {
int16_t Track_EventIndex;
uint32_t Track_Command_offset;
uint32_t Track_Command_size;
/* read Track[Index] */
if (!load_utf_subtable(acbFile, acb, &acb->TrackTable, "TrackTable", NULL))
goto fail;
if (!utf_query_s16(acbFile, acb->TrackTable, Index, "EventIndex", &Track_EventIndex))
goto fail;
//;VGM_LOG("ACB: Track[%i]: EventIndex=%i\n", Index, Track_EventIndex);
/* next link varies with version, check by table existence */
if (acb->has_CommandTable) { /* <=v1.27 */
/* read Command[EventIndex] */
if (!load_utf_subtable(acbFile, acb, &acb->CommandTable, "CommandTable", NULL))
goto fail;
if (!utf_query_data(acbFile, acb->CommandTable, Track_EventIndex, "Command", &Track_Command_offset, &Track_Command_size))
goto fail;
//;VGM_LOG("ACB: Command[%i]: Command={%x,%x}\n", Track_EventIndex, Track_Command_offset,Track_Command_size);
}
else if (acb->has_TrackEventTable) { /* >=v1.28 */
/* read TrackEvent[EventIndex] */
if (!load_utf_subtable(acbFile, acb, &acb->TrackEventTable, "TrackEventTable", NULL))
goto fail;
if (!utf_query_data(acbFile, acb->TrackEventTable, Track_EventIndex, "Command", &Track_Command_offset, &Track_Command_size))
goto fail;
//;VGM_LOG("ACB: TrackEvent[%i]: Command={%x,%x}\n", Track_EventIndex, Track_Command_offset,Track_Command_size);
}
else {
VGM_LOG("ACB: unknown command table\n");
goto fail;
}
/* read Command (some kind of multiple TLVs, this seems ok) */
{
uint32_t offset = Track_Command_offset;
uint32_t max_offset = Track_Command_offset + Track_Command_size;
uint16_t tlv_code, tlv_type, tlv_index;
uint8_t tlv_size;
while (offset < max_offset) {
tlv_code = read_u16be(offset + 0x00, acbFile);
tlv_size = read_u8 (offset + 0x02, acbFile);
offset += 0x03;
if (tlv_code == 0x07D0) {
if (tlv_size < 0x04) {
VGM_LOG("ACB: TLV with unknown size\n");
break;
}
tlv_type = read_u16be(offset + 0x00, acbFile);
tlv_index = read_u16be(offset + 0x02, acbFile);
//;VGM_LOG("ACB: TLV at %x: type %x, index=%x\n", offset, tlv_type, tlv_index);
/* probably same as Synth_ReferenceItem_type */
switch(tlv_type) {
case 0x02: /* Synth (common) */
if (!load_acb_synth(acbFile, acb, tlv_index))
goto fail;
break;
case 0x03: /* Sequence of Synths (common, ex. Yakuza 6, Yakuza Kiwami 2) */
if (!load_acb_sequence(acbFile, acb, tlv_index))
goto fail;
break;
default:
VGM_LOG("ACB: unknown TLV type %x at %x + %x\n", tlv_type, offset, tlv_size);
max_offset = 0; /* force end without failing */
break;
}
}
/* 0x07D1 comes suspiciously often paired with 0x07D0 too */
offset += tlv_size;
}
}
return 1;
fail:
return 0;
}
static int load_acb_sequence(STREAMFILE *acbFile, acb_header* acb, int16_t Index) {
int i;
int16_t Sequence_NumTracks;
uint32_t Sequence_TrackIndex_offset;
uint32_t Sequence_TrackIndex_size;
/* read Sequence[Index] */
if (!load_utf_subtable(acbFile, acb, &acb->SequenceTable, "SequenceTable", NULL))
goto fail;
if (!utf_query_s16(acbFile, acb->SequenceTable, Index, "NumTracks", &Sequence_NumTracks))
goto fail;
if (!utf_query_data(acbFile, acb->SequenceTable, Index, "TrackIndex", &Sequence_TrackIndex_offset, &Sequence_TrackIndex_size))
goto fail;
//;VGM_LOG("ACB: Sequence[%i]: NumTracks=%i, TrackIndex={%x, %x}\n", Index, Sequence_NumTracks, Sequence_TrackIndex_offset,Sequence_TrackIndex_size);
acb->sequence_depth++;
if (acb->sequence_depth > 3) {
VGM_LOG("ACB: Sequence depth too high\n");
goto fail; /* max Sequence > Sequence > Sequence > Synth > Waveform (ex. Yakuza 6) */
}
if (Sequence_NumTracks * 0x02 > Sequence_TrackIndex_size) { /* padding may exist */
VGM_LOG("ACB: wrong Sequence.TrackIndex size\n");
goto fail;
}
/* read Tracks inside Sequence */
for (i = 0; i < Sequence_NumTracks; i++) {
int16_t Sequence_TrackIndex_index = read_s16be(Sequence_TrackIndex_offset + i*0x02, acbFile);
if (!load_acb_track_event_command(acbFile, acb, Sequence_TrackIndex_index))
goto fail;
}
acb->sequence_depth--;
return 1;
fail:
return 0;
}
static int load_acb_block(STREAMFILE *acbFile, acb_header* acb, int16_t Index) {
int i;
int16_t Block_NumTracks;
uint32_t Block_TrackIndex_offset;
uint32_t Block_TrackIndex_size;
/* read Block[Index] */
if (!load_utf_subtable(acbFile, acb, &acb->BlockTable, "BlockTable", NULL))
goto fail;
if (!utf_query_s16(acbFile, acb->BlockTable, Index, "NumTracks", &Block_NumTracks))
goto fail;
if (!utf_query_data(acbFile, acb->BlockTable, Index, "TrackIndex", &Block_TrackIndex_offset, &Block_TrackIndex_size))
goto fail;
//;VGM_LOG("ACB: Block[%i]: NumTracks=%i, TrackIndex={%x, %x}\n", Index, Block_NumTracks, Block_TrackIndex_offset,Block_TrackIndex_size);
if (Block_NumTracks * 0x02 > Block_TrackIndex_size) { /* padding may exist */
VGM_LOG("ACB: wrong Block.TrackIndex size\n");
goto fail;
}
/* read Tracks inside Block */
for (i = 0; i < Block_NumTracks; i++) {
int16_t Block_TrackIndex_index = read_s16be(Block_TrackIndex_offset + i*0x02, acbFile);
if (!load_acb_track_event_command(acbFile, acb, Block_TrackIndex_index))
goto fail;
}
return 1;
fail:
return 0;
}
static int load_acb_cue(STREAMFILE *acbFile, acb_header* acb, int16_t Index) {
int8_t Cue_ReferenceType;
int16_t Cue_ReferenceIndex;
/* read Cue[Index] */
if (!load_utf_subtable(acbFile, acb, &acb->CueTable, "CueTable", NULL))
goto fail;
if (!utf_query_s8 (acbFile, acb->CueTable, Index, "ReferenceType", &Cue_ReferenceType))
goto fail;
if (!utf_query_s16(acbFile, acb->CueTable, Index, "ReferenceIndex", &Cue_ReferenceIndex))
goto fail;
//;VGM_LOG("ACB: Cue[%i]: ReferenceType=%i, ReferenceIndex=%i\n", Index, Cue_ReferenceType, Cue_ReferenceIndex);
/* usually older games use older references but not necessarily */
switch(Cue_ReferenceType) {
case 1: /* Cue > Waveform (ex. PES 2015) */
if (!load_acb_waveform(acbFile, acb, Cue_ReferenceIndex))
goto fail;
break;
case 2: /* Cue > Synth > Waveform (ex. Ukiyo no Roushi) */
if (!load_acb_synth(acbFile, acb, Cue_ReferenceIndex))
goto fail;
break;
case 3: /* Cue > Sequence > Track > Command > Synth > Waveform (ex. Valkyrie Profile anatomia, Yakuza Kiwami 2) */
if (!load_acb_sequence(acbFile, acb, Cue_ReferenceIndex))
goto fail;
break;
case 8: /* Cue > Block > Track > Command > Synth > Waveform (ex. Sonic Lost World, rare) */
if (!load_acb_block(acbFile, acb, Cue_ReferenceIndex))
goto fail;
break;
default:
VGM_LOG("ACB: unknown Cue.ReferenceType=%x, Cue.ReferenceIndex=%x\n", Cue_ReferenceType, Cue_ReferenceIndex);
break; /* ignore and continue */
}
return 1;
fail:
return 0;
}
static int load_acb_cuename(STREAMFILE *acbFile, acb_header* acb, int16_t Index) {
int16_t CueName_CueIndex;
const char* CueName_CueName;
/* read CueName[Index] */
if (!load_utf_subtable(acbFile, acb, &acb->CueNameTable, "CueNameTable", NULL))
goto fail;
if (!utf_query_s16(acbFile, acb->CueNameTable, Index, "CueIndex", &CueName_CueIndex))
goto fail;
if (!utf_query_string(acbFile, acb->CueNameTable, Index, "CueName", &CueName_CueName))
goto fail;
//;VGM_LOG("ACB: CueName[%i]: CueIndex=%i, CueName=%s\n", Index, CueName_CueIndex, CueName_CueName);
/* save as will be needed if references waveform */
acb->cuename_index = Index;
acb->cuename_name = CueName_CueName;
if (!load_acb_cue(acbFile, acb, CueName_CueIndex))
goto fail;
return 1;
fail:
return 0;
} }
void load_acb_wave_name(STREAMFILE *acbFile, VGMSTREAM* vgmstream, int waveid, int is_memory) { void load_acb_wave_name(STREAMFILE *acbFile, VGMSTREAM* vgmstream, int waveid, int is_memory) {
acb_header acb = {0}; acb_header acb = {0};
int CueName_rows, CueName_i, TrackIndex_i, ReferenceItems_i, SynthIndex_i; int i, CueName_rows;
if (!acbFile || !vgmstream || waveid < 0) if (!acbFile || !vgmstream || waveid < 0)
return; return;
@ -427,126 +529,50 @@ void load_acb_wave_name(STREAMFILE *acbFile, VGMSTREAM* vgmstream, int waveid, i
* Multiple cues can use the same wave (meaning multiple names), and one cue may use multiple waves. * Multiple cues can use the same wave (meaning multiple names), and one cue may use multiple waves.
* There is no easy way to map cue name <> wave name so basically we parse the whole thing. * There is no easy way to map cue name <> wave name so basically we parse the whole thing.
* *
* .acb cues are created in CRI Atom Craft roughly like this: * .acb are created in CRI Atom Craft, where user defines N Cues with CueName each, then link somehow
* - user creates N Cues with CueName * to a Waveform (.awb=streamed or memory .acb=internal, data 'material' encoded in some format),
* - Cues define Sequences of Tracks * depending on reference types. Typical links are:
* - depending on reference types: * - CueName > Cue > Waveform (type 1)
* - Track points directly to Waveform (type 1) * - CueName > Cue > Synth > Waveform (type 2)
* - Track points to Synth then to Waveform (type 2) * - CueName > Cue > Sequence > Track > Command > Synth > Waveform (type 3, <=v1.27)
* - Track points to Commands with binary Command that points to Synth then to Waveform (type 3 <=v1.27) * - CueName > Cue > Sequence > Track > Command > Synth > Synth > Waveform (type 3, <=v1.27)
* - Track points to TrackEvent with binary Command that points to Synth then to Waveform (type 3 >=v1.28) * - CueName > Cue > Sequence > Track > TrackEvent > Command > Synth > Waveform (type 3, >=v1.28)
* (games may use multiple versions and reference types) * - CueName > Cue > Sequence > Track > TrackEvent > Command > Synth > Synth > Waveform (type 3, >=v1.28)
* - Waveforms are audio materials encoded in some format * - CueName > Cue > Sequence > Track > TrackEvent > Command > Sequence > (...) > Synth > Waveform (type 3, >=v1.28)
* - Waveforms are saved into .awb, that can be streamed (loaded manually) or internal, * - CueName > Cue > Block > Track > Command > Synth > Synth > Waveform (type 8)
* and has a checksum/hash to validate * - others should be possible but haven't been observed
* - there is a version value (sometimes written incorrectly) and string, * Atom Craft may only target certain .acb versions so some links are later removed
* Atom Craft may only target certain .acb versions * Not all cues to point to though Waveforms, as some are just config events/commands.
* .acb link to .awb by name (loaded manually), though they have a checksum/hash to validate.
*/ */
//;VGM_LOG("ACB: find waveid=%i\n", waveid); //;VGM_LOG("ACB: find waveid=%i\n", waveid);
acb.Header = utf_open(acbFile, 0x00, NULL, NULL); acb.Header = utf_open(acbFile, 0x00, NULL, NULL);
if (!acb.Header) goto done; if (!acb.Header) goto fail;
acb.target_waveid = waveid;
acb.is_memory = is_memory; acb.is_memory = is_memory;
acb.has_TrackEventTable = utf_query_data(acbFile, acb.Header, 0, "TrackEventTable", NULL,NULL); acb.has_TrackEventTable = utf_query_data(acbFile, acb.Header, 0, "TrackEventTable", NULL,NULL);
acb.has_CommandTable = utf_query_data(acbFile, acb.Header, 0, "CommandTable", NULL,NULL); acb.has_CommandTable = utf_query_data(acbFile, acb.Header, 0, "CommandTable", NULL,NULL);
/* read CueName[i] */ /* read all possible cue names and find which waveids are referenced by it */
if (!load_utf_subtable(acbFile, &acb, &acb.CueNameTable, "CueNameTable", &CueName_rows)) goto done; if (!load_utf_subtable(acbFile, &acb, &acb.CueNameTable, "CueNameTable", &CueName_rows))
//;VGM_LOG("ACB: CueNames=%i\n", CueName_rows); goto fail;
for (i = 0; i < CueName_rows; i++) {
for (CueName_i = 0; CueName_i < CueName_rows; CueName_i++) { if (!load_acb_cuename(acbFile, &acb, i))
acb.CueNameIndex = CueName_i; goto fail;
if (!load_acb_cue_info(acbFile, &acb))
goto done;
/* meaning of Cue.ReferenceIndex */
switch(acb.ReferenceType) {
case 1: { /* Cue > Waveform (ex. PES 2015) */
acb.ReferenceItem = acb.ReferenceIndex;
if (!load_acb_waveform(acbFile, &acb, waveid))
goto done;
if (acb.is_wave_found)
add_acb_name(acbFile, &acb);
break;
}
case 2: { /* Cue > Synth > Waveform (ex. Ukiyo no Roushi) */
acb.SynthIndex = acb.ReferenceIndex;
if (!load_acb_synth(acbFile, &acb))
goto done;
for (ReferenceItems_i = 0; ReferenceItems_i < acb.ReferenceItems_count; ReferenceItems_i++) {
acb.ReferenceItem = acb.ReferenceItems_list[ReferenceItems_i];
if (!load_acb_waveform(acbFile, &acb, waveid))
goto done;
if (acb.is_wave_found)
add_acb_name(acbFile, &acb);
}
break;
}
case 3: { /* Cue > Sequence > Track > Command > Synth > Waveform (ex. Valkyrie Profile anatomia, Yakuza Kiwami 2) */
if (!load_acb_sequence(acbFile, &acb))
goto done;
/* read Tracks inside Sequence */
for (TrackIndex_i = 0; TrackIndex_i < acb.NumTracks; TrackIndex_i++) {
acb.TrackIndex = read_s16be(acb.TrackIndex_offset + TrackIndex_i*0x02, acbFile);
if (!load_acb_track_command(acbFile, &acb))
goto done;
for (SynthIndex_i = 0; SynthIndex_i < acb.SynthIndex_count; SynthIndex_i++) {
acb.SynthIndex = acb.SynthIndex_list[SynthIndex_i];
if (!load_acb_synth(acbFile, &acb))
goto done;
for (ReferenceItems_i = 0; ReferenceItems_i < acb.ReferenceItems_count; ReferenceItems_i++) {
acb.ReferenceItem = acb.ReferenceItems_list[ReferenceItems_i];
if (!load_acb_waveform(acbFile, &acb, waveid))
goto done;
if (acb.is_wave_found)
add_acb_name(acbFile, &acb);
}
}
}
break;
}
case 8: //todo found on Sonic Lost World (maybe not references wave?)
default:
VGM_LOG("ACB: unknown reference type\n");
break;
}
//if (acb.AwbId_count > 0)
// break;
} }
/* meh copy */ /* meh copy */
if (acb.AwbName_count > 0) { if (acb.awbname_count > 0) {
strncpy(vgmstream->stream_name, acb.name, STREAM_NAME_SIZE); strncpy(vgmstream->stream_name, acb.name, STREAM_NAME_SIZE);
vgmstream->stream_name[STREAM_NAME_SIZE - 1] = '\0'; vgmstream->stream_name[STREAM_NAME_SIZE - 1] = '\0';
} }
done: fail:
utf_close(acb.Header); utf_close(acb.Header);
utf_close(acb.CueNameTable); utf_close(acb.CueNameTable);
utf_close(acb.CueTable); utf_close(acb.CueTable);

View File

@ -1,8 +1,6 @@
#include "meta.h" #include "meta.h"
#include "../coding/coding.h" #include "../coding/coding.h"
#include "bgw_streamfile.h"
static STREAMFILE* setup_bgw_atrac3_streamfile(STREAMFILE *streamFile, off_t subfile_offset, size_t subfile_size, size_t frame_size, int channels);
/* BGW - from Final Fantasy XI (PC) music files */ /* BGW - from Final Fantasy XI (PC) music files */
@ -220,61 +218,3 @@ fail:
close_vgmstream(vgmstream); close_vgmstream(vgmstream);
return NULL; return NULL;
} }
#define BGW_KEY_MAX (0xC0*2)
typedef struct {
uint8_t key[BGW_KEY_MAX];
size_t key_size;
} bgw_decryption_data;
/* Encrypted ATRAC3 info from Moogle Toolbox (https://sourceforge.net/projects/mogbox/) */
static size_t bgw_decryption_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length, bgw_decryption_data* data) {
size_t bytes_read;
int i;
bytes_read = streamfile->read(streamfile, dest, offset, length);
/* decrypt data (xor) */
for (i = 0; i < bytes_read; i++) {
dest[i] ^= data->key[(offset + i) % data->key_size];
}
return bytes_read;
}
static STREAMFILE* setup_bgw_atrac3_streamfile(STREAMFILE *streamFile, off_t subfile_offset, size_t subfile_size, size_t frame_size, int channels) {
STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL;
bgw_decryption_data io_data = {0};
size_t io_data_size = sizeof(bgw_decryption_data);
int ch;
/* setup decryption with key (first frame + modified channel header) */
if (frame_size*channels == 0 || frame_size*channels > BGW_KEY_MAX) goto fail;
io_data.key_size = read_streamfile(io_data.key, subfile_offset, frame_size*channels, streamFile);
for (ch = 0; ch < channels; ch++) {
uint32_t xor = get_32bitBE(io_data.key + frame_size*ch);
put_32bitBE(io_data.key + frame_size*ch, xor ^ 0xA0024E9F);
}
/* setup subfile */
new_streamFile = open_wrap_streamfile(streamFile);
if (!new_streamFile) goto fail;
temp_streamFile = new_streamFile;
new_streamFile = open_clamp_streamfile(temp_streamFile, subfile_offset,subfile_size);
if (!new_streamFile) goto fail;
temp_streamFile = new_streamFile;
new_streamFile = open_io_streamfile(temp_streamFile, &io_data,io_data_size, bgw_decryption_read,NULL);
if (!new_streamFile) goto fail;
temp_streamFile = new_streamFile;
return temp_streamFile;
fail:
close_streamfile(temp_streamFile);
return NULL;
}

66
src/meta/bgw_streamfile.h Normal file
View File

@ -0,0 +1,66 @@
#ifndef _BGW_STREAMFILE_H_
#define _BGW_STREAMFILE_H_
#include "../streamfile.h"
#define BGW_KEY_MAX (0xC0*2)
typedef struct {
uint8_t key[BGW_KEY_MAX];
size_t key_size;
} bgw_decryption_data;
/* Encrypted ATRAC3 info from Moogle Toolbox (https://sourceforge.net/projects/mogbox/) */
static size_t bgw_decryption_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length, bgw_decryption_data* data) {
size_t bytes_read;
int i;
bytes_read = streamfile->read(streamfile, dest, offset, length);
/* decrypt data (xor) */
for (i = 0; i < bytes_read; i++) {
dest[i] ^= data->key[(offset + i) % data->key_size];
}
//todo: a few files (music069.bgw, music071.bgw, music900.bgw) have the last frames unencrypted,
// though they are blank and encoder ignores wrongly decrypted frames and outputs blank samples as well
return bytes_read;
}
static STREAMFILE* setup_bgw_atrac3_streamfile(STREAMFILE *streamFile, off_t subfile_offset, size_t subfile_size, size_t frame_size, int channels) {
STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL;
bgw_decryption_data io_data = {0};
size_t io_data_size = sizeof(bgw_decryption_data);
int ch;
/* setup decryption with key (first frame + modified channel header) */
if (frame_size*channels == 0 || frame_size*channels > BGW_KEY_MAX) goto fail;
io_data.key_size = read_streamfile(io_data.key, subfile_offset, frame_size*channels, streamFile);
for (ch = 0; ch < channels; ch++) {
uint32_t xor = get_32bitBE(io_data.key + frame_size*ch);
put_32bitBE(io_data.key + frame_size*ch, xor ^ 0xA0024E9F);
}
/* setup subfile */
new_streamFile = open_wrap_streamfile(streamFile);
if (!new_streamFile) goto fail;
temp_streamFile = new_streamFile;
new_streamFile = open_clamp_streamfile(temp_streamFile, subfile_offset,subfile_size);
if (!new_streamFile) goto fail;
temp_streamFile = new_streamFile;
new_streamFile = open_io_streamfile(temp_streamFile, &io_data,io_data_size, bgw_decryption_read,NULL);
if (!new_streamFile) goto fail;
temp_streamFile = new_streamFile;
return temp_streamFile;
fail:
close_streamfile(temp_streamFile);
return NULL;
}
#endif /* _BGW_STREAMFILE_H_ */

View File

@ -72,7 +72,7 @@ VGMSTREAM * init_vgmstream_xa(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_ps2_rxws(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_ps2_rxws(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_ps2_rxw(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_ps2_rxw(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_ps2_int(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_raw_int(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_ps2_exst(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_ps2_exst(STREAMFILE *streamFile);
@ -84,7 +84,7 @@ VGMSTREAM * init_vgmstream_mib_mih(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_ps2_mic(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_ps2_mic(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_raw(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_raw_pcm(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_vag(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_vag(STREAMFILE *streamFile);
@ -94,7 +94,7 @@ VGMSTREAM * init_vgmstream_ps2_ild(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_ps2_pnb(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_ps2_pnb(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_xbox_wavm(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_raw_wavm(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_ngc_str(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_ngc_str(STREAMFILE *streamFile);
@ -266,9 +266,9 @@ VGMSTREAM * init_vgmstream_dec(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_vs(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_vs(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_xbox_xmu(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_xmu(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_xbox_xvas(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_xvas(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_ngc_bh2pcm(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_ngc_bh2pcm(STREAMFILE *streamFile);
@ -516,7 +516,7 @@ VGMSTREAM * init_vgmstream_lsf_n1nj4n(STREAMFILE* streamFile);
VGMSTREAM * init_vgmstream_vawx(STREAMFILE* streamFile); VGMSTREAM * init_vgmstream_vawx(STREAMFILE* streamFile);
VGMSTREAM * init_vgmstream_pc_snds(STREAMFILE* streamFile); VGMSTREAM * init_vgmstream_raw_snds(STREAMFILE* streamFile);
VGMSTREAM * init_vgmstream_ps2_wmus(STREAMFILE* streamFile); VGMSTREAM * init_vgmstream_ps2_wmus(STREAMFILE* streamFile);

View File

@ -1,63 +0,0 @@
#include "meta.h"
#include "../util.h"
/* .snds - from Incredibles PC */
VGMSTREAM * init_vgmstream_pc_snds(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
char filename[PATH_LIMIT];
size_t file_size;
int i;
/* check extension, case insensitive */
/* this is all we have to go on, snds is completely headerless */
streamFile->get_name(streamFile,filename,sizeof(filename));
if (strcasecmp("snds",filename_extension(filename))) goto fail;
file_size = get_streamfile_size(streamFile);
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(2,0);
if (!vgmstream) goto fail;
vgmstream->sample_rate = 48000;
/* file seems to be mistakenly 1/8 too long */
vgmstream->num_samples = file_size*8/9;
/* check for 32 0 bytes where the padding should start */
for (i = 0; i < 8; i++)
{
if (read_32bitBE(vgmstream->num_samples+i*4,streamFile) != 0)
{
/* not padding? just play the whole file */
vgmstream->num_samples = file_size;
break;
}
}
vgmstream->coding_type = coding_SNDS_IMA;
vgmstream->layout_type = layout_none;
vgmstream->meta_type = meta_PC_SNDS;
/* open the file for reading */
vgmstream->ch[0].streamfile = vgmstream->ch[1].streamfile =
streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE);
if (!vgmstream->ch[0].streamfile) goto fail;
vgmstream->ch[0].channel_start_offset=
vgmstream->ch[0].offset=
vgmstream->ch[1].channel_start_offset=
vgmstream->ch[1].offset=0;
return vgmstream;
/* clean up anything we may have opened */
fail:
if (vgmstream) close_vgmstream(vgmstream);
return NULL;
}

View File

@ -1,59 +0,0 @@
#include "meta.h"
#include "../util.h"
/* RAW
RAW format is native 44khz PCM file
Nothing more :P ...
2008-05-17 - Fastelbja : First version ...
*/
VGMSTREAM * init_vgmstream_raw(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
char filename[PATH_LIMIT];
int i;
/* check extension, case insensitive */
streamFile->get_name(streamFile,filename,sizeof(filename));
if (strcasecmp("raw",filename_extension(filename))) goto fail;
/* No check to do as they are raw pcm */
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(2,0);
if (!vgmstream) goto fail;
/* fill in the vital statistics */
vgmstream->channels = 2;
vgmstream->sample_rate = 44100;
vgmstream->coding_type = coding_PCM16LE;
vgmstream->num_samples = (int32_t)(get_streamfile_size(streamFile)/4);
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = 2;
vgmstream->meta_type = meta_RAW;
/* open the file for reading by each channel */
{
STREAMFILE *chstreamfile;
/* have both channels use the same buffer, as interleave is so small */
chstreamfile = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE);
if (!chstreamfile) goto fail;
for (i=0;i<2;i++) {
vgmstream->ch[i].streamfile = chstreamfile;
vgmstream->ch[i].channel_start_offset=
vgmstream->ch[i].offset=(off_t)(i*vgmstream->interleave_block_size);
}
}
return vgmstream;
/* clean up anything we may have opened */
fail:
if (vgmstream) close_vgmstream(vgmstream);
return NULL;
}

View File

@ -2,7 +2,7 @@
#include "../coding/coding.h" #include "../coding/coding.h"
/* raw PCM file assumed by extension [PaRappa The Rapper 2 (PS2)? , Amplitude (PS2)?] */ /* raw PCM file assumed by extension [PaRappa The Rapper 2 (PS2)? , Amplitude (PS2)?] */
VGMSTREAM * init_vgmstream_ps2_int(STREAMFILE *streamFile) { VGMSTREAM * init_vgmstream_raw_int(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL; VGMSTREAM * vgmstream = NULL;
off_t start_offset; off_t start_offset;
int channel_count; int channel_count;
@ -16,18 +16,9 @@ VGMSTREAM * init_vgmstream_ps2_int(STREAMFILE *streamFile) {
else else
channel_count = 4; channel_count = 4;
/* try to skip known .int (a horrible idea this parser exists) */ /* ignore .int PS-ADPCM */
{ if (ps_check_format(streamFile, 0x00, 0x10000))
/* ignore A2M .int */ goto fail;
if (read_32bitBE(0x00,streamFile) == 0x41324D00) /* "A2M\0" */
goto fail;
/* ignore EXST .int */
if (read_32bitBE(0x10,streamFile) == 0x0C020000 &&
read_32bitBE(0x20,streamFile) == 0x0C020000 &&
read_32bitBE(0x30,streamFile) == 0x0C020000 &&
read_32bitBE(0x40,streamFile) == 0x0C020000) /* check a few empty PS-ADPCM frames */
goto fail;
}
start_offset = 0x00; start_offset = 0x00;
@ -36,8 +27,8 @@ VGMSTREAM * init_vgmstream_ps2_int(STREAMFILE *streamFile) {
vgmstream = allocate_vgmstream(channel_count,0); vgmstream = allocate_vgmstream(channel_count,0);
if (!vgmstream) goto fail; if (!vgmstream) goto fail;
vgmstream->meta_type = meta_RAW_INT;
vgmstream->sample_rate = 48000; vgmstream->sample_rate = 48000;
vgmstream->meta_type = meta_PS2_RAW;
vgmstream->num_samples = pcm_bytes_to_samples(get_streamfile_size(streamFile), vgmstream->channels, 16); vgmstream->num_samples = pcm_bytes_to_samples(get_streamfile_size(streamFile), vgmstream->channels, 16);
vgmstream->coding_type = coding_PCM16LE; vgmstream->coding_type = coding_PCM16LE;
vgmstream->layout_type = layout_interleave; vgmstream->layout_type = layout_interleave;
@ -48,6 +39,6 @@ VGMSTREAM * init_vgmstream_ps2_int(STREAMFILE *streamFile) {
return vgmstream; return vgmstream;
fail: fail:
if (vgmstream) close_vgmstream(vgmstream); close_vgmstream(vgmstream);
return NULL; return NULL;
} }

38
src/meta/raw_pcm.c Normal file
View File

@ -0,0 +1,38 @@
#include "meta.h"
#include "../coding/coding.h"
/* RAW - RAW format is native 44khz PCM file */
VGMSTREAM * init_vgmstream_raw_pcm(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
off_t start_offset;
int loop_flag, channel_count;
/* checks */
if (!check_extensions(streamFile, "raw"))
goto fail;
channel_count = 2;
loop_flag = 0;
start_offset = 0x00;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count, loop_flag);
if (!vgmstream) goto fail;
vgmstream->meta_type = meta_RAW_PCM;
vgmstream->sample_rate = 44100;
vgmstream->num_samples = pcm_bytes_to_samples(get_streamfile_size(streamFile), channel_count, 16);
vgmstream->coding_type = coding_PCM16LE;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = 0x02;
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
goto fail;
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;
}

51
src/meta/raw_snds.c Normal file
View File

@ -0,0 +1,51 @@
#include "meta.h"
/* .snds - from Heavy Iron's The Incredibles (PC) */
VGMSTREAM * init_vgmstream_raw_snds(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
off_t start_offset;
int loop_flag, channel_count;
size_t file_size;
int i;
/* checks */
if (!check_extensions(streamFile, "snds"))
goto fail;
loop_flag = 0;
channel_count = 2;
start_offset = 0;
file_size = get_streamfile_size(streamFile);
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count, loop_flag);
if (!vgmstream) goto fail;
vgmstream->meta_type = meta_RAW_SNDS;
vgmstream->sample_rate = 48000;
/* file seems to be mistakenly 1/8 too long, check for 32 0 bytes where the padding should start */
vgmstream->num_samples = file_size*8/9;
for (i = 0; i < 8; i++) {
if (read_32bitBE(vgmstream->num_samples+i*4,streamFile) != 0) {
vgmstream->num_samples = file_size; /* no padding? just play the whole file */
break;
}
}
vgmstream->coding_type = coding_SNDS_IMA;
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

@ -2,7 +2,7 @@
#include "../coding/coding.h" #include "../coding/coding.h"
/* WAVM - headerless format which can be found on XBOX */ /* WAVM - headerless format which can be found on XBOX */
VGMSTREAM * init_vgmstream_xbox_wavm(STREAMFILE *streamFile) { VGMSTREAM * init_vgmstream_raw_wavm(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL; VGMSTREAM * vgmstream = NULL;
off_t start_offset = 0; off_t start_offset = 0;
int loop_flag, channel_count; int loop_flag, channel_count;
@ -19,13 +19,12 @@ VGMSTREAM * init_vgmstream_xbox_wavm(STREAMFILE *streamFile) {
vgmstream = allocate_vgmstream(channel_count,loop_flag); vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail; if (!vgmstream) goto fail;
vgmstream->meta_type = meta_RAW_WAVM;
vgmstream->sample_rate = 44100; vgmstream->sample_rate = 44100;
vgmstream->num_samples = xbox_ima_bytes_to_samples(get_streamfile_size(streamFile), vgmstream->channels); vgmstream->num_samples = xbox_ima_bytes_to_samples(get_streamfile_size(streamFile), vgmstream->channels);
vgmstream->coding_type = coding_XBOX_IMA; vgmstream->coding_type = coding_XBOX_IMA;
vgmstream->layout_type = layout_none; vgmstream->layout_type = layout_none;
vgmstream->meta_type = meta_XBOX_WAVM;
if (!vgmstream_open_stream(vgmstream, streamFile, start_offset)) if (!vgmstream_open_stream(vgmstream, streamFile, start_offset))
goto fail; goto fail;

View File

@ -483,25 +483,24 @@ static VGMSTREAM * init_vgmstream_ubi_sb_base(ubi_sb_header *sb, STREAMFILE *str
break; break;
case UBI_ADPCM: case UBI_ADPCM:
/* todo custom Ubi 4/6-bit ADPCM (4b for music/stereo and 6b for voices) /* custom Ubi 4/6-bit ADPCM used in early games:
* - ~0x30: stream header (varies with versions). * - Splinter Cell (PC): 4-bit w/ 2ch (music), 6-bit w/ 1ch (sfx)
* contains frame sizes (normal/last), channels, sometimes sample rate/samples/etc. * - Batman: Vengeance (PC): 4-bit w/ 2ch (music), 6-bit w/ 1ch (sfx)
* - 0x34 per channel: channel header, starts with 0x02, contains ADPCM info * - Myst IV (PC/Xbox): 4bit-1ch (amb), some files only (ex. sfx_si_puzzle_stream.sb2)
* - 0x602/902: frame data divided into 2 chunks (with a padding byte) * - possibly others */
* for each chunk frame data is read as LE int32, then divided into codes
* (some 6b span two int32). stereo interleaves 4 codes per channel.
* data is decoded using 3 tables and ops to adjust sample and calculate next step
* (6b decoding is much simpler than 4b, that applies more extra ops).
*
* used in:
* - Batman: Vengeance (PC)
* - Splinter Cell (PC)
* - some Myst IV (PC/Xbox) banks (ex. puzzle_si_splintercell.sb2)
* - Splinter Cell Essentials (PSP) voices (header has extra 0x08 with fixed value)
*/
VGM_LOG("UBI SB: unsupported Ubi ADPCM found\n"); /* skip extra header (some kind of id?) found in Myst IV */
goto fail; if (read_32bitBE(start_offset + 0x00, streamData) != 0x08000000 &&
read_32bitBE(start_offset + 0x08, streamData) == 0x08000000) {
start_offset += 0x08;
sb->stream_size -= 0x08;
}
vgmstream->codec_data = init_ubi_adpcm(streamData, start_offset, vgmstream->channels);
if (!vgmstream->codec_data) goto fail;
vgmstream->coding_type = coding_UBI_ADPCM;
vgmstream->layout_type = layout_none;
break;
case RAW_PCM: case RAW_PCM:
vgmstream->coding_type = coding_PCM16LE; /* always LE */ vgmstream->coding_type = coding_PCM16LE; /* always LE */
@ -1044,9 +1043,9 @@ static VGMSTREAM * init_vgmstream_ubi_sb_header(ubi_sb_header *sb, STREAMFILE* s
goto fail; goto fail;
} }
//;VGM_LOG("UBI SB: target at %x + %x, extra=%x, name=%s, g=%i, t=%i\n", ;VGM_LOG("UBI SB: target at %x + %x, extra=%x, name=%s, g=%i, t=%i\n",
// (uint32_t)sb->header_offset, sb->cfg.section2_entry_size, (uint32_t)sb->extra_offset, sb->resource_name, sb->group_id, sb->stream_type); (uint32_t)sb->header_offset, sb->cfg.section2_entry_size, (uint32_t)sb->extra_offset, sb->resource_name, sb->group_id, sb->stream_type);
//;VGM_LOG("UBI SB: stream offset=%x, size=%x, name=%s\n", (uint32_t)sb->stream_offset, sb->stream_size, sb->is_external ? sb->resource_name : "internal" ); ;VGM_LOG("UBI SB: stream offset=%x, size=%x, name=%s\n", (uint32_t)sb->stream_offset, sb->stream_size, sb->is_external ? sb->resource_name : "internal" );
switch(sb->type) { switch(sb->type) {
case UBI_AUDIO: case UBI_AUDIO:

View File

@ -1,8 +1,8 @@
#include "meta.h" #include "meta.h"
#include "../coding/coding.h" #include "../coding/coding.h"
/* XMU- found in Alter Echo (Xbox) */ /* XMU - found in Alter Echo (Xbox) */
VGMSTREAM * init_vgmstream_xbox_xmu(STREAMFILE *streamFile) { VGMSTREAM * init_vgmstream_xmu(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL; VGMSTREAM * vgmstream = NULL;
size_t start_offset; size_t start_offset;
int loop_flag, channel_count; int loop_flag, channel_count;
@ -25,6 +25,7 @@ VGMSTREAM * init_vgmstream_xbox_xmu(STREAMFILE *streamFile) {
vgmstream = allocate_vgmstream(channel_count,loop_flag); vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail; if (!vgmstream) goto fail;
vgmstream->meta_type = meta_XMU;
vgmstream->sample_rate = read_32bitLE(0x10,streamFile); vgmstream->sample_rate = read_32bitLE(0x10,streamFile);
vgmstream->num_samples = xbox_ima_bytes_to_samples(data_size, vgmstream->channels); vgmstream->num_samples = xbox_ima_bytes_to_samples(data_size, vgmstream->channels);
vgmstream->loop_start_sample = 0; vgmstream->loop_start_sample = 0;
@ -32,7 +33,6 @@ VGMSTREAM * init_vgmstream_xbox_xmu(STREAMFILE *streamFile) {
vgmstream->coding_type = coding_XBOX_IMA; vgmstream->coding_type = coding_XBOX_IMA;
vgmstream->layout_type = layout_none; vgmstream->layout_type = layout_none;
vgmstream->meta_type = meta_XBOX_XMU;
if (!vgmstream_open_stream(vgmstream, streamFile, start_offset)) if (!vgmstream_open_stream(vgmstream, streamFile, start_offset))
goto fail; goto fail;

View File

@ -3,7 +3,7 @@
#include "../coding/coding.h" #include "../coding/coding.h"
/* XVAS - found in TMNT 2 & TMNT 3 (Xbox) */ /* XVAS - found in TMNT 2 & TMNT 3 (Xbox) */
VGMSTREAM * init_vgmstream_xbox_xvas(STREAMFILE *streamFile) { VGMSTREAM * init_vgmstream_xvas(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL; VGMSTREAM * vgmstream = NULL;
off_t start_offset; off_t start_offset;
int loop_flag, channel_count; int loop_flag, channel_count;
@ -28,7 +28,7 @@ VGMSTREAM * init_vgmstream_xbox_xvas(STREAMFILE *streamFile) {
vgmstream = allocate_vgmstream(channel_count,loop_flag); vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail; if (!vgmstream) goto fail;
vgmstream->meta_type = meta_XBOX_XVAS; vgmstream->meta_type = meta_XVAS;
vgmstream->sample_rate = read_32bitLE(0x0c,streamFile); vgmstream->sample_rate = read_32bitLE(0x0c,streamFile);
vgmstream->num_samples = xbox_ima_bytes_to_samples(data_size, vgmstream->channels); vgmstream->num_samples = xbox_ima_bytes_to_samples(data_size, vgmstream->channels);
if(loop_flag) { if(loop_flag) {

View File

@ -1021,8 +1021,6 @@ void mixing_macro_downmix(VGMSTREAM* vgmstream, int max /*, mapping_t output_map
/* read output mapping (ex. 2.0) and find channel */ /* read output mapping (ex. 2.0) and find channel */
if (!(output_mapping & (1<<mp_out))) if (!(output_mapping & (1<<mp_out)))
continue; continue;
VGM_LOG("i=%i,j=%i, ch_out=%i, ch_in=%i + %i, %f\n", mp_in,mp_out,ch_out, max, ch_in, matrix[mp_out][mp_in]);
mixing_push_add(vgmstream, ch_out, max + ch_in, matrix[mp_in][mp_out]); mixing_push_add(vgmstream, ch_out, max + ch_in, matrix[mp_in][mp_out]);
ch_out++; ch_out++;

View File

@ -62,9 +62,9 @@ static inline int get_low_nibble_signed(uint8_t n) {
} }
static inline int clamp16(int32_t val) { static inline int clamp16(int32_t val) {
if (val>32767) return 32767; if (val > 32767) return 32767;
if (val<-32768) return -32768; else if (val < -32768) return -32768;
return val; else return val;
} }
static inline int round10(int val) { static inline int round10(int val) {

View File

@ -54,7 +54,6 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = {
init_vgmstream_seb, init_vgmstream_seb,
init_vgmstream_ps2_ild, init_vgmstream_ps2_ild,
init_vgmstream_ps2_pnb, init_vgmstream_ps2_pnb,
init_vgmstream_xbox_wavm,
init_vgmstream_ngc_str, init_vgmstream_ngc_str,
init_vgmstream_ea_schl, init_vgmstream_ea_schl,
init_vgmstream_caf, init_vgmstream_caf,
@ -134,8 +133,8 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = {
init_vgmstream_vs, init_vgmstream_vs,
init_vgmstream_dc_str, init_vgmstream_dc_str,
init_vgmstream_dc_str_v2, init_vgmstream_dc_str_v2,
init_vgmstream_xbox_xmu, init_vgmstream_xmu,
init_vgmstream_xbox_xvas, init_vgmstream_xvas,
init_vgmstream_ngc_bh2pcm, init_vgmstream_ngc_bh2pcm,
init_vgmstream_sat_sap, init_vgmstream_sat_sap,
init_vgmstream_dc_idvi, init_vgmstream_dc_idvi,
@ -480,11 +479,12 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = {
/* lowest priority metas (should go after all metas, and TXTH should go before raw formats) */ /* lowest priority metas (should go after all metas, and TXTH should go before raw formats) */
init_vgmstream_txth, /* proper parsers should supersede TXTH, once added */ init_vgmstream_txth, /* proper parsers should supersede TXTH, once added */
init_vgmstream_ps2_int, /* .int raw PS-ADPCM */ init_vgmstream_raw_int, /* .int raw PCM */
init_vgmstream_ps_headerless, /* tries to detect a bunch of PS-ADPCM formats */ init_vgmstream_ps_headerless, /* tries to detect a bunch of PS-ADPCM formats */
init_vgmstream_pc_snds, /* .snds PC, after ps_headerless */ init_vgmstream_raw_snds, /* .snds raw SNDS IMA (*after* ps_headerless) */
init_vgmstream_s14_sss, /* .raw siren14 */ init_vgmstream_raw_wavm, /* .wavm raw xbox */
init_vgmstream_raw, /* .raw PCM */ init_vgmstream_raw_pcm, /* .raw raw PCM */
init_vgmstream_s14_sss, /* .s14/sss raw siren14 */
#ifdef VGM_USE_FFMPEG #ifdef VGM_USE_FFMPEG
init_vgmstream_ffmpeg, /* may play anything incorrectly, since FFmpeg doesn't check extensions */ init_vgmstream_ffmpeg, /* may play anything incorrectly, since FFmpeg doesn't check extensions */
#endif #endif
@ -650,6 +650,10 @@ void reset_vgmstream(VGMSTREAM * vgmstream) {
reset_hca(vgmstream->codec_data); reset_hca(vgmstream->codec_data);
} }
if (vgmstream->coding_type == coding_UBI_ADPCM) {
reset_ubi_adpcm(vgmstream->codec_data);
}
if (vgmstream->coding_type == coding_EA_MT) { if (vgmstream->coding_type == coding_EA_MT) {
reset_ea_mt(vgmstream); reset_ea_mt(vgmstream);
} }
@ -814,6 +818,11 @@ void close_vgmstream(VGMSTREAM * vgmstream) {
vgmstream->codec_data = NULL; vgmstream->codec_data = NULL;
} }
if (vgmstream->coding_type == coding_UBI_ADPCM) {
free_ubi_adpcm(vgmstream->codec_data);
vgmstream->codec_data = NULL;
}
if (vgmstream->coding_type == coding_EA_MT) { if (vgmstream->coding_type == coding_EA_MT) {
free_ea_mt(vgmstream->codec_data, vgmstream->channels); free_ea_mt(vgmstream->codec_data, vgmstream->channels);
vgmstream->codec_data = NULL; vgmstream->codec_data = NULL;
@ -1251,6 +1260,8 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) {
return (vgmstream->interleave_block_size - 0x06)*2 + 2; return (vgmstream->interleave_block_size - 0x06)*2 + 2;
case coding_PTADPCM: case coding_PTADPCM:
return (vgmstream->interleave_block_size - 0x05)*2 + 2; return (vgmstream->interleave_block_size - 0x05)*2 + 2;
case coding_UBI_ADPCM:
return 0; /* varies per mode */
case coding_EA_MT: case coding_EA_MT:
return 0; /* 432, but variable in looped files */ return 0; /* 432, but variable in looped files */
case coding_CRI_HCA: case coding_CRI_HCA:
@ -1435,6 +1446,8 @@ int get_vgmstream_frame_size(VGMSTREAM * vgmstream) {
return vgmstream->interleave_block_size; return vgmstream->interleave_block_size;
case coding_PTADPCM: case coding_PTADPCM:
return vgmstream->interleave_block_size; return vgmstream->interleave_block_size;
case coding_UBI_ADPCM:
return 0; /* varies per mode? */
case coding_EA_MT: case coding_EA_MT:
return 0; /* variable (frames of bit counts or PCM frames) */ return 0; /* variable (frames of bit counts or PCM frames) */
#ifdef VGM_USE_ATRAC9 #ifdef VGM_USE_ATRAC9
@ -2102,6 +2115,10 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to
} }
break; break;
case coding_UBI_ADPCM:
decode_ubi_adpcm(vgmstream, buffer+samples_written*vgmstream->channels, samples_to_do);
break;
case coding_EA_MT: case coding_EA_MT:
for (ch = 0; ch < vgmstream->channels; ch++) { for (ch = 0; ch < vgmstream->channels; ch++) {
decode_ea_mt(vgmstream, buffer+samples_written*vgmstream->channels+ch, decode_ea_mt(vgmstream, buffer+samples_written*vgmstream->channels+ch,
@ -2183,6 +2200,10 @@ int vgmstream_do_loop(VGMSTREAM * vgmstream) {
loop_hca(vgmstream->codec_data, vgmstream->loop_sample); loop_hca(vgmstream->codec_data, vgmstream->loop_sample);
} }
if (vgmstream->coding_type == coding_UBI_ADPCM) {
seek_ubi_adpcm(vgmstream->codec_data, vgmstream->loop_sample);
}
if (vgmstream->coding_type == coding_EA_MT) { if (vgmstream->coding_type == coding_EA_MT) {
seek_ea_mt(vgmstream, vgmstream->loop_sample); seek_ea_mt(vgmstream, vgmstream->loop_sample);
} }

View File

@ -177,6 +177,7 @@ typedef enum {
coding_ACM, /* InterPlay ACM */ coding_ACM, /* InterPlay ACM */
coding_NWA, /* VisualArt's NWA */ coding_NWA, /* VisualArt's NWA */
coding_CIRCUS_ADPCM, /* Circus 8-bit ADPCM */ coding_CIRCUS_ADPCM, /* Circus 8-bit ADPCM */
coding_UBI_ADPCM, /* Ubisoft 4/6-bit ADPCM */
coding_EA_MT, /* Electronic Arts MicroTalk (linear-predictive speech codec) */ coding_EA_MT, /* Electronic Arts MicroTalk (linear-predictive speech codec) */
@ -341,7 +342,7 @@ typedef enum {
meta_PS2_SShd, /* .ADS with SShd header */ meta_PS2_SShd, /* .ADS with SShd header */
meta_NPS, meta_NPS,
meta_PS2_RXWS, /* Sony games (Genji, Okage Shadow King, Arc The Lad Twilight of Spirits) */ meta_PS2_RXWS, /* Sony games (Genji, Okage Shadow King, Arc The Lad Twilight of Spirits) */
meta_PS2_RAW, /* RAW Interleaved Format */ meta_RAW_INT,
meta_PS2_EXST, /* Shadow of Colossus EXST */ meta_PS2_EXST, /* Shadow of Colossus EXST */
meta_PS2_SVAG, /* Konami SVAG */ meta_PS2_SVAG, /* Konami SVAG */
meta_PS_HEADERLESS, /* headerless PS-ADPCM */ meta_PS_HEADERLESS, /* headerless PS-ADPCM */
@ -440,24 +441,19 @@ typedef enum {
meta_PS2_XA2_RRP, /* RC Revenge Pro */ meta_PS2_XA2_RRP, /* RC Revenge Pro */
meta_NGC_DSP_KONAMI, /* Konami DSP header, found in various games */ meta_NGC_DSP_KONAMI, /* Konami DSP header, found in various games */
meta_UBI_CKD, /* Ubisoft CKD RIFF header (Rayman Origins Wii) */ meta_UBI_CKD, /* Ubisoft CKD RIFF header (Rayman Origins Wii) */
meta_RAW_WAVM,
meta_XBOX_WAVM, /* XBOX WAVM File */
meta_XBOX_WVS, /* XBOX WVS */ meta_XBOX_WVS, /* XBOX WVS */
meta_NGC_WVS, /* Metal Arms - Glitch in the System */ meta_NGC_WVS, /* Metal Arms - Glitch in the System */
meta_XBOX_MATX, /* XBOX MATX */ meta_XBOX_MATX, /* XBOX MATX */
meta_XBOX_XMU, /* XBOX XMU */ meta_XMU,
meta_XBOX_XVAS, /* XBOX VAS */ meta_XVAS,
meta_EA_SCHL, /* Electronic Arts SCHl with variable header */ meta_EA_SCHL, /* Electronic Arts SCHl with variable header */
meta_EA_SCHL_fixed, /* Electronic Arts SCHl with fixed header */ meta_EA_SCHL_fixed, /* Electronic Arts SCHl with fixed header */
meta_EA_BNK, /* Electronic Arts BNK */ meta_EA_BNK, /* Electronic Arts BNK */
meta_EA_1SNH, /* Electronic Arts 1SNh/EACS */ meta_EA_1SNH, /* Electronic Arts 1SNh/EACS */
meta_EA_EACS, meta_EA_EACS,
meta_RAW_PCM,
meta_RAW, /* RAW PCM file */
meta_GENH, /* generic header */ meta_GENH, /* generic header */
meta_AIFC, /* Audio Interchange File Format AIFF-C */ meta_AIFC, /* Audio Interchange File Format AIFF-C */
meta_AIFF, /* Audio Interchange File Format */ meta_AIFF, /* Audio Interchange File Format */
meta_STR_SNDS, /* .str with SNDS blocks and SHDR header */ meta_STR_SNDS, /* .str with SNDS blocks and SHDR header */
@ -557,7 +553,7 @@ typedef enum {
meta_VS_STR, /* The Bouncer */ meta_VS_STR, /* The Bouncer */
meta_LSF_N1NJ4N, /* .lsf n1nj4n Fastlane Street Racing (iPhone) */ meta_LSF_N1NJ4N, /* .lsf n1nj4n Fastlane Street Racing (iPhone) */
meta_VAWX, /* feelplus: No More Heroes Heroes Paradise, Moon Diver */ meta_VAWX, /* feelplus: No More Heroes Heroes Paradise, Moon Diver */
meta_PC_SNDS, /* Incredibles PC .snds */ meta_RAW_SNDS,
meta_PS2_WMUS, /* The Warriors (PS2) */ meta_PS2_WMUS, /* The Warriors (PS2) */
meta_HYPERSCAN_KVAG, /* Hyperscan KVAG/BVG */ meta_HYPERSCAN_KVAG, /* Hyperscan KVAG/BVG */
meta_IOS_PSND, /* Crash Bandicoot Nitro Kart 2 (iOS) */ meta_IOS_PSND, /* Crash Bandicoot Nitro Kart 2 (iOS) */
@ -1252,6 +1248,8 @@ typedef struct {
#endif #endif
#endif #endif
typedef struct ubi_adpcm_codec_data ubi_adpcm_codec_data;
typedef struct ea_mt_codec_data ea_mt_codec_data; typedef struct ea_mt_codec_data ea_mt_codec_data;

View File

@ -1,4 +1,11 @@
#!/bin/sh #!/bin/sh
DIR="`dirname "$0"`"
VERSION="`git describe --always | tr : _`" # get current git version, redirect stderr to stdin, change : to _
echo "$VERSION" VERSION=$(git describe --always 2>&1 | tr : _ )
# ignore git stderr "fatal:
if case $VERSION in fatal*) ;; *) false;; esac; then
echo ""
else
echo "$VERSION"
fi;