mirror of
https://github.com/vgmstream/vgmstream.git
synced 2024-12-01 01:27:20 +01:00
commit
a349c26be6
@ -247,7 +247,7 @@ recent versions (1.4.x) you can configure plugin priority.
|
||||
### Channel issues
|
||||
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
|
||||
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
|
||||
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
|
||||
|
@ -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);
|
||||
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_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);
|
||||
|
@ -1,32 +1,32 @@
|
||||
#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 */
|
||||
-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,
|
||||
-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,
|
||||
-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,
|
||||
-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,
|
||||
-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,
|
||||
-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,
|
||||
-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,
|
||||
-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,
|
||||
-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,
|
||||
-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,
|
||||
-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,
|
||||
-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,
|
||||
-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,
|
||||
-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,
|
||||
-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,
|
||||
-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,
|
||||
-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,
|
||||
-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,
|
||||
-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,
|
||||
-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,
|
||||
-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,
|
||||
-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,
|
||||
-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,
|
||||
-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,
|
||||
/* rest is 0s */
|
||||
};
|
||||
|
||||
|
582
src/coding/ubi_adpcm_decoder.c
Normal file
582
src/coding/ubi_adpcm_decoder.c
Normal 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;
|
||||
}
|
@ -693,6 +693,7 @@ static const coding_info coding_info_list[] = {
|
||||
{coding_ACM, "InterPlay ACM"},
|
||||
{coding_NWA, "VisualArt's NWA DPCM"},
|
||||
{coding_CIRCUS_ADPCM, "Circus 8-bit ADPCM"},
|
||||
{coding_UBI_ADPCM, "Ubisoft 4/6-bit ADPCM"},
|
||||
|
||||
{coding_EA_MT, "Electronic Arts MicroTalk"},
|
||||
|
||||
@ -806,7 +807,7 @@ static const meta_info meta_info_list[] = {
|
||||
{meta_FWAV, "Nintendo FWAV header"},
|
||||
{meta_XA, "Sony XA 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_DSP_STM, "Intelligent Systems STM 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_IDSP_TT, "Traveller's Tales IDSP header"},
|
||||
{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_VAGp, "Sony VAGp 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_PS2_ILD, "ILD header"},
|
||||
{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_EA_SCHL, "Electronic Arts SCHl header (variable)"},
|
||||
{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_DC_STR, "Sega Stream Asset Builder header"},
|
||||
{meta_DC_STR_V2, "variant of Sega Stream Asset Builder header"},
|
||||
{meta_XBOX_XMU, "XMU header"},
|
||||
{meta_XBOX_XVAS, "Konami .XVAS header"},
|
||||
{meta_XMU, "Outrage XMU header"},
|
||||
{meta_XVAS, "Konami .XVAS header"},
|
||||
{meta_PS2_XA2, "Acclaim XA2 Header"},
|
||||
{meta_DC_IDVI, "Capcom IDVI 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_LSF_N1NJ4N, ".lsf !n1nj4n 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_HYPERSCAN_KVAG, "Mattel Hyperscan KVAG"},
|
||||
{meta_IOS_PSND, "PSND Header"},
|
||||
|
@ -252,6 +252,10 @@
|
||||
RelativePath=".\meta\bar_streamfile.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\meta\bgw_streamfile.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\meta\ea_eaac_streamfile.h"
|
||||
>
|
||||
@ -1015,7 +1019,7 @@
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\meta\pc_snds.c"
|
||||
RelativePath=".\meta\raw_snds.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
@ -1135,7 +1139,7 @@
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\meta\ps2_int.c"
|
||||
RelativePath=".\meta\raw_int.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
@ -1399,7 +1403,7 @@
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\meta\raw.c"
|
||||
RelativePath=".\meta\raw_pcm.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
@ -1727,15 +1731,15 @@
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\meta\xbox_wavm.c"
|
||||
RelativePath=".\meta\raw_wavm.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\meta\xbox_xmu.c"
|
||||
RelativePath=".\meta\xmu.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\meta\xbox_xvas.c"
|
||||
RelativePath=".\meta\xvas.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
@ -2034,10 +2038,14 @@
|
||||
RelativePath=".\coding\sassc_decoder.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\coding\sdx2_decoder.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\coding\sdx2_decoder.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\coding\ubi_adpcm_decoder.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\coding\ws_decoder.c"
|
||||
>
|
||||
|
@ -106,6 +106,7 @@
|
||||
<ClInclude Include="meta\aix_streamfile.h" />
|
||||
<ClInclude Include="meta\awc_xma_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_schl_streamfile.h" />
|
||||
<ClInclude Include="meta\fsb_interleave_streamfile.h" />
|
||||
@ -184,7 +185,7 @@
|
||||
<ClCompile Include="meta\pc_adp.c" />
|
||||
<ClCompile Include="meta\pc_adp_otns.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_hsf.c" />
|
||||
<ClCompile Include="meta\ps2_iab.c" />
|
||||
@ -391,7 +392,7 @@
|
||||
<ClCompile Include="meta\ps2_hgc1.c" />
|
||||
<ClCompile Include="meta\ikm.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\jstm.c" />
|
||||
<ClCompile Include="meta\ps2_kces.c" />
|
||||
@ -442,7 +443,7 @@
|
||||
<ClCompile Include="meta\ffdl.c" />
|
||||
<ClCompile Include="meta\seb.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\rfrm.c" />
|
||||
<ClCompile Include="meta\riff.c" />
|
||||
@ -508,9 +509,9 @@
|
||||
<ClCompile Include="meta\xavs.c" />
|
||||
<ClCompile Include="meta\xbox_hlwav.c" />
|
||||
<ClCompile Include="meta\xbox_ims.c" />
|
||||
<ClCompile Include="meta\xbox_wavm.c" />
|
||||
<ClCompile Include="meta\xbox_xmu.c" />
|
||||
<ClCompile Include="meta\xbox_xvas.c" />
|
||||
<ClCompile Include="meta\raw_wavm.c" />
|
||||
<ClCompile Include="meta\xmu.c" />
|
||||
<ClCompile Include="meta\xvas.c" />
|
||||
<ClCompile Include="meta\x360_pasx.c" />
|
||||
<ClCompile Include="meta\xma.c" />
|
||||
<ClCompile Include="meta\xnb.c" />
|
||||
@ -567,6 +568,7 @@
|
||||
<ClCompile Include="coding\ptadpcm_decoder.c" />
|
||||
<ClCompile Include="coding\sassc_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_utils_fsb.c" />
|
||||
<ClCompile Include="coding\vorbis_custom_utils_ogl.c" />
|
||||
|
@ -86,6 +86,9 @@
|
||||
<ClInclude Include="meta\bar_streamfile.h">
|
||||
<Filter>meta\Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="meta\bgw_streamfile.h">
|
||||
<Filter>meta\Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="meta\ea_eaac_streamfile.h">
|
||||
<Filter>meta\Header Files</Filter>
|
||||
</ClInclude>
|
||||
@ -709,7 +712,7 @@
|
||||
<ClCompile Include="meta\ps2_ild.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\ps2_int.c">
|
||||
<ClCompile Include="meta\raw_int.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\ps2_joe.c">
|
||||
@ -862,7 +865,7 @@
|
||||
<ClCompile Include="meta\ea_swvr.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\raw.c">
|
||||
<ClCompile Include="meta\raw_pcm.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\redspark.c">
|
||||
@ -1063,13 +1066,13 @@
|
||||
<ClCompile Include="meta\xbox_ims.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\xbox_wavm.c">
|
||||
<ClCompile Include="meta\raw_wavm.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\xbox_xmu.c">
|
||||
<ClCompile Include="meta\xmu.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\xbox_xvas.c">
|
||||
<ClCompile Include="meta\xvas.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\xss.c">
|
||||
@ -1219,6 +1222,9 @@
|
||||
<ClCompile Include="coding\sdx2_decoder.c">
|
||||
<Filter>coding\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="coding\ubi_adpcm_decoder.c">
|
||||
<Filter>coding\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="coding\ws_decoder.c">
|
||||
<Filter>coding\Source Files</Filter>
|
||||
</ClCompile>
|
||||
@ -1414,7 +1420,7 @@
|
||||
<ClCompile Include="meta\vawx.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\pc_snds.c">
|
||||
<ClCompile Include="meta\raw_snds.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\sqex_scd.c">
|
||||
|
804
src/meta/acb.c
804
src/meta/acb.c
@ -71,9 +71,11 @@ fail:
|
||||
/* ************************************** */
|
||||
|
||||
typedef struct {
|
||||
/* keep track of these tables so they can be closed when done */
|
||||
utf_context *Header;
|
||||
utf_context *CueNameTable;
|
||||
utf_context *CueTable;
|
||||
utf_context *BlockTable;
|
||||
utf_context *SequenceTable;
|
||||
utf_context *TrackTable;
|
||||
utf_context *TrackEventTable;
|
||||
@ -81,42 +83,22 @@ typedef struct {
|
||||
utf_context *SynthTable;
|
||||
utf_context *WaveformTable;
|
||||
|
||||
char name[1024];
|
||||
/* config */
|
||||
int is_memory;
|
||||
int target_waveid;
|
||||
int has_TrackEventTable;
|
||||
int has_CommandTable;
|
||||
|
||||
int16_t CueNameIndex;
|
||||
const char* CueName;
|
||||
int16_t CueIndex;
|
||||
int16_t ReferenceIndex;
|
||||
int8_t ReferenceType;
|
||||
/* to avoid infinite/circular references (AtomViewer crashes otherwise) */
|
||||
int synth_depth;
|
||||
int sequence_depth;
|
||||
|
||||
int16_t NumTracks;
|
||||
uint32_t TrackIndex_offset;
|
||||
uint32_t TrackIndex_size;
|
||||
int16_t TrackIndex;
|
||||
int16_t EventIndex;
|
||||
uint32_t Command_offset;
|
||||
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];
|
||||
/* name stuff */
|
||||
int16_t cuename_index;
|
||||
const char * cuename_name;
|
||||
int awbname_count;
|
||||
int16_t awbname_list[255];
|
||||
char name[1024];
|
||||
|
||||
} acb_header;
|
||||
|
||||
@ -138,286 +120,406 @@ fail:
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int load_acb_cue_info(STREAMFILE *acbFile, acb_header* acb) {
|
||||
|
||||
/* read Cue[CueNameIndex] */
|
||||
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) {
|
||||
static void add_acb_name(STREAMFILE *acbFile, acb_header* acb, int8_t Waveform_Streaming) {
|
||||
//todo safe string ops
|
||||
|
||||
/* aaand finally get name (phew) */
|
||||
|
||||
/* ignore name repeats */
|
||||
if (acb->AwbName_count) {
|
||||
if (acb->awbname_count) {
|
||||
int i;
|
||||
for (i = 0; i < acb->AwbName_count; i++) {
|
||||
if (acb->AwbName_list[i] == acb->CueNameIndex)
|
||||
for (i = 0; i < acb->awbname_count; i++) {
|
||||
if (acb->awbname_list[i] == acb->cuename_index)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* 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, acb->CueName);
|
||||
strcat(acb->name, acb->cuename_name);
|
||||
}
|
||||
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]");
|
||||
}
|
||||
|
||||
acb->AwbName_list[acb->AwbName_count] = acb->CueNameIndex;
|
||||
acb->AwbName_count++;
|
||||
if (acb->AwbName_count >= 254)
|
||||
acb->AwbName_count = 254; /* ??? */
|
||||
acb->awbname_list[acb->awbname_count] = acb->cuename_index;
|
||||
acb->awbname_count++;
|
||||
if (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) {
|
||||
acb_header acb = {0};
|
||||
int CueName_rows, CueName_i, TrackIndex_i, ReferenceItems_i, SynthIndex_i;
|
||||
int i, CueName_rows;
|
||||
|
||||
|
||||
if (!acbFile || !vgmstream || waveid < 0)
|
||||
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.
|
||||
* 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:
|
||||
* - user creates N Cues with CueName
|
||||
* - Cues define Sequences of Tracks
|
||||
* - depending on reference types:
|
||||
* - Track points directly to Waveform (type 1)
|
||||
* - Track points to Synth then to Waveform (type 2)
|
||||
* - Track points to Commands with binary Command that points to Synth then to Waveform (type 3 <=v1.27)
|
||||
* - Track points to TrackEvent with binary Command that points to Synth then to Waveform (type 3 >=v1.28)
|
||||
* (games may use multiple versions and reference types)
|
||||
* - Waveforms are audio materials encoded in some format
|
||||
* - Waveforms are saved into .awb, that can be streamed (loaded manually) or internal,
|
||||
* and has a checksum/hash to validate
|
||||
* - there is a version value (sometimes written incorrectly) and string,
|
||||
* Atom Craft may only target certain .acb versions
|
||||
* .acb are created in CRI Atom Craft, where user defines N Cues with CueName each, then link somehow
|
||||
* to a Waveform (.awb=streamed or memory .acb=internal, data 'material' encoded in some format),
|
||||
* depending on reference types. Typical links are:
|
||||
* - CueName > Cue > Waveform (type 1)
|
||||
* - CueName > Cue > Synth > Waveform (type 2)
|
||||
* - CueName > Cue > Sequence > Track > Command > Synth > Waveform (type 3, <=v1.27)
|
||||
* - CueName > Cue > Sequence > Track > Command > Synth > Synth > Waveform (type 3, <=v1.27)
|
||||
* - CueName > Cue > Sequence > Track > TrackEvent > Command > Synth > Waveform (type 3, >=v1.28)
|
||||
* - CueName > Cue > Sequence > Track > TrackEvent > Command > Synth > Synth > Waveform (type 3, >=v1.28)
|
||||
* - CueName > Cue > Sequence > Track > TrackEvent > Command > Sequence > (...) > Synth > Waveform (type 3, >=v1.28)
|
||||
* - CueName > Cue > Block > Track > Command > Synth > Synth > Waveform (type 8)
|
||||
* - others should be possible but haven't been observed
|
||||
* Atom Craft may only target certain .acb versions so some links are later removed
|
||||
* 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);
|
||||
|
||||
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.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);
|
||||
|
||||
|
||||
/* read CueName[i] */
|
||||
if (!load_utf_subtable(acbFile, &acb, &acb.CueNameTable, "CueNameTable", &CueName_rows)) goto done;
|
||||
//;VGM_LOG("ACB: CueNames=%i\n", CueName_rows);
|
||||
/* 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 fail;
|
||||
for (i = 0; i < CueName_rows; i++) {
|
||||
|
||||
for (CueName_i = 0; CueName_i < CueName_rows; CueName_i++) {
|
||||
acb.CueNameIndex = CueName_i;
|
||||
|
||||
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;
|
||||
if (!load_acb_cuename(acbFile, &acb, i))
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* meh copy */
|
||||
if (acb.AwbName_count > 0) {
|
||||
if (acb.awbname_count > 0) {
|
||||
strncpy(vgmstream->stream_name, acb.name, STREAM_NAME_SIZE);
|
||||
vgmstream->stream_name[STREAM_NAME_SIZE - 1] = '\0';
|
||||
}
|
||||
|
||||
done:
|
||||
fail:
|
||||
utf_close(acb.Header);
|
||||
utf_close(acb.CueNameTable);
|
||||
utf_close(acb.CueTable);
|
||||
|
@ -1,8 +1,6 @@
|
||||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
|
||||
|
||||
static STREAMFILE* setup_bgw_atrac3_streamfile(STREAMFILE *streamFile, off_t subfile_offset, size_t subfile_size, size_t frame_size, int channels);
|
||||
#include "bgw_streamfile.h"
|
||||
|
||||
|
||||
/* BGW - from Final Fantasy XI (PC) music files */
|
||||
@ -220,61 +218,3 @@ fail:
|
||||
close_vgmstream(vgmstream);
|
||||
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
66
src/meta/bgw_streamfile.h
Normal 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_ */
|
@ -72,7 +72,7 @@ VGMSTREAM * init_vgmstream_xa(STREAMFILE *streamFile);
|
||||
VGMSTREAM * init_vgmstream_ps2_rxws(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);
|
||||
|
||||
@ -84,7 +84,7 @@ VGMSTREAM * init_vgmstream_mib_mih(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);
|
||||
|
||||
@ -94,7 +94,7 @@ VGMSTREAM * init_vgmstream_ps2_ild(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);
|
||||
|
||||
@ -266,9 +266,9 @@ VGMSTREAM * init_vgmstream_dec(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);
|
||||
|
||||
@ -516,7 +516,7 @@ VGMSTREAM * init_vgmstream_lsf_n1nj4n(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);
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
@ -2,7 +2,7 @@
|
||||
#include "../coding/coding.h"
|
||||
|
||||
/* 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;
|
||||
off_t start_offset;
|
||||
int channel_count;
|
||||
@ -16,18 +16,9 @@ VGMSTREAM * init_vgmstream_ps2_int(STREAMFILE *streamFile) {
|
||||
else
|
||||
channel_count = 4;
|
||||
|
||||
/* try to skip known .int (a horrible idea this parser exists) */
|
||||
{
|
||||
/* ignore A2M .int */
|
||||
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;
|
||||
}
|
||||
/* ignore .int PS-ADPCM */
|
||||
if (ps_check_format(streamFile, 0x00, 0x10000))
|
||||
goto fail;
|
||||
|
||||
start_offset = 0x00;
|
||||
|
||||
@ -36,8 +27,8 @@ VGMSTREAM * init_vgmstream_ps2_int(STREAMFILE *streamFile) {
|
||||
vgmstream = allocate_vgmstream(channel_count,0);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->meta_type = meta_RAW_INT;
|
||||
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->coding_type = coding_PCM16LE;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
@ -48,6 +39,6 @@ VGMSTREAM * init_vgmstream_ps2_int(STREAMFILE *streamFile) {
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
if (vgmstream) close_vgmstream(vgmstream);
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
38
src/meta/raw_pcm.c
Normal file
38
src/meta/raw_pcm.c
Normal 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
51
src/meta/raw_snds.c
Normal 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;
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
#include "../coding/coding.h"
|
||||
|
||||
/* 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;
|
||||
off_t start_offset = 0;
|
||||
int loop_flag, channel_count;
|
||||
@ -19,13 +19,12 @@ VGMSTREAM * init_vgmstream_xbox_wavm(STREAMFILE *streamFile) {
|
||||
vgmstream = allocate_vgmstream(channel_count,loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->meta_type = meta_RAW_WAVM;
|
||||
vgmstream->sample_rate = 44100;
|
||||
vgmstream->num_samples = xbox_ima_bytes_to_samples(get_streamfile_size(streamFile), vgmstream->channels);
|
||||
|
||||
vgmstream->coding_type = coding_XBOX_IMA;
|
||||
vgmstream->layout_type = layout_none;
|
||||
vgmstream->meta_type = meta_XBOX_WAVM;
|
||||
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream, streamFile, start_offset))
|
||||
goto fail;
|
@ -483,25 +483,24 @@ static VGMSTREAM * init_vgmstream_ubi_sb_base(ubi_sb_header *sb, STREAMFILE *str
|
||||
break;
|
||||
|
||||
case UBI_ADPCM:
|
||||
/* todo custom Ubi 4/6-bit ADPCM (4b for music/stereo and 6b for voices)
|
||||
* - ~0x30: stream header (varies with versions).
|
||||
* contains frame sizes (normal/last), channels, sometimes sample rate/samples/etc.
|
||||
* - 0x34 per channel: channel header, starts with 0x02, contains ADPCM info
|
||||
* - 0x602/902: frame data divided into 2 chunks (with a padding byte)
|
||||
* 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)
|
||||
*/
|
||||
/* custom Ubi 4/6-bit ADPCM used in early games:
|
||||
* - Splinter Cell (PC): 4-bit w/ 2ch (music), 6-bit w/ 1ch (sfx)
|
||||
* - Batman: Vengeance (PC): 4-bit w/ 2ch (music), 6-bit w/ 1ch (sfx)
|
||||
* - Myst IV (PC/Xbox): 4bit-1ch (amb), some files only (ex. sfx_si_puzzle_stream.sb2)
|
||||
* - possibly others */
|
||||
|
||||
VGM_LOG("UBI SB: unsupported Ubi ADPCM found\n");
|
||||
goto fail;
|
||||
/* skip extra header (some kind of id?) found in Myst IV */
|
||||
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:
|
||||
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;
|
||||
}
|
||||
|
||||
//;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);
|
||||
//;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: 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);
|
||||
;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) {
|
||||
case UBI_AUDIO:
|
||||
|
@ -1,8 +1,8 @@
|
||||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
|
||||
/* XMU- found in Alter Echo (Xbox) */
|
||||
VGMSTREAM * init_vgmstream_xbox_xmu(STREAMFILE *streamFile) {
|
||||
/* XMU - found in Alter Echo (Xbox) */
|
||||
VGMSTREAM * init_vgmstream_xmu(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
size_t start_offset;
|
||||
int loop_flag, channel_count;
|
||||
@ -25,6 +25,7 @@ VGMSTREAM * init_vgmstream_xbox_xmu(STREAMFILE *streamFile) {
|
||||
vgmstream = allocate_vgmstream(channel_count,loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->meta_type = meta_XMU;
|
||||
vgmstream->sample_rate = read_32bitLE(0x10,streamFile);
|
||||
vgmstream->num_samples = xbox_ima_bytes_to_samples(data_size, vgmstream->channels);
|
||||
vgmstream->loop_start_sample = 0;
|
||||
@ -32,7 +33,6 @@ VGMSTREAM * init_vgmstream_xbox_xmu(STREAMFILE *streamFile) {
|
||||
|
||||
vgmstream->coding_type = coding_XBOX_IMA;
|
||||
vgmstream->layout_type = layout_none;
|
||||
vgmstream->meta_type = meta_XBOX_XMU;
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream, streamFile, start_offset))
|
||||
goto fail;
|
@ -3,7 +3,7 @@
|
||||
#include "../coding/coding.h"
|
||||
|
||||
/* XVAS - found in TMNT 2 & TMNT 3 (Xbox) */
|
||||
VGMSTREAM * init_vgmstream_xbox_xvas(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * init_vgmstream_xvas(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
off_t start_offset;
|
||||
int loop_flag, channel_count;
|
||||
@ -28,7 +28,7 @@ VGMSTREAM * init_vgmstream_xbox_xvas(STREAMFILE *streamFile) {
|
||||
vgmstream = allocate_vgmstream(channel_count,loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->meta_type = meta_XBOX_XVAS;
|
||||
vgmstream->meta_type = meta_XVAS;
|
||||
vgmstream->sample_rate = read_32bitLE(0x0c,streamFile);
|
||||
vgmstream->num_samples = xbox_ima_bytes_to_samples(data_size, vgmstream->channels);
|
||||
if(loop_flag) {
|
@ -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 */
|
||||
if (!(output_mapping & (1<<mp_out)))
|
||||
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]);
|
||||
|
||||
ch_out++;
|
||||
|
@ -62,9 +62,9 @@ static inline int get_low_nibble_signed(uint8_t n) {
|
||||
}
|
||||
|
||||
static inline int clamp16(int32_t val) {
|
||||
if (val>32767) return 32767;
|
||||
if (val<-32768) return -32768;
|
||||
return val;
|
||||
if (val > 32767) return 32767;
|
||||
else if (val < -32768) return -32768;
|
||||
else return val;
|
||||
}
|
||||
|
||||
static inline int round10(int val) {
|
||||
|
@ -54,7 +54,6 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = {
|
||||
init_vgmstream_seb,
|
||||
init_vgmstream_ps2_ild,
|
||||
init_vgmstream_ps2_pnb,
|
||||
init_vgmstream_xbox_wavm,
|
||||
init_vgmstream_ngc_str,
|
||||
init_vgmstream_ea_schl,
|
||||
init_vgmstream_caf,
|
||||
@ -134,8 +133,8 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = {
|
||||
init_vgmstream_vs,
|
||||
init_vgmstream_dc_str,
|
||||
init_vgmstream_dc_str_v2,
|
||||
init_vgmstream_xbox_xmu,
|
||||
init_vgmstream_xbox_xvas,
|
||||
init_vgmstream_xmu,
|
||||
init_vgmstream_xvas,
|
||||
init_vgmstream_ngc_bh2pcm,
|
||||
init_vgmstream_sat_sap,
|
||||
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) */
|
||||
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_pc_snds, /* .snds PC, after ps_headerless */
|
||||
init_vgmstream_s14_sss, /* .raw siren14 */
|
||||
init_vgmstream_raw, /* .raw PCM */
|
||||
init_vgmstream_raw_snds, /* .snds raw SNDS IMA (*after* ps_headerless) */
|
||||
init_vgmstream_raw_wavm, /* .wavm raw xbox */
|
||||
init_vgmstream_raw_pcm, /* .raw raw PCM */
|
||||
init_vgmstream_s14_sss, /* .s14/sss raw siren14 */
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
init_vgmstream_ffmpeg, /* may play anything incorrectly, since FFmpeg doesn't check extensions */
|
||||
#endif
|
||||
@ -650,6 +650,10 @@ void reset_vgmstream(VGMSTREAM * vgmstream) {
|
||||
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) {
|
||||
reset_ea_mt(vgmstream);
|
||||
}
|
||||
@ -814,6 +818,11 @@ void close_vgmstream(VGMSTREAM * vgmstream) {
|
||||
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) {
|
||||
free_ea_mt(vgmstream->codec_data, vgmstream->channels);
|
||||
vgmstream->codec_data = NULL;
|
||||
@ -1251,6 +1260,8 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) {
|
||||
return (vgmstream->interleave_block_size - 0x06)*2 + 2;
|
||||
case coding_PTADPCM:
|
||||
return (vgmstream->interleave_block_size - 0x05)*2 + 2;
|
||||
case coding_UBI_ADPCM:
|
||||
return 0; /* varies per mode */
|
||||
case coding_EA_MT:
|
||||
return 0; /* 432, but variable in looped files */
|
||||
case coding_CRI_HCA:
|
||||
@ -1435,6 +1446,8 @@ int get_vgmstream_frame_size(VGMSTREAM * vgmstream) {
|
||||
return vgmstream->interleave_block_size;
|
||||
case coding_PTADPCM:
|
||||
return vgmstream->interleave_block_size;
|
||||
case coding_UBI_ADPCM:
|
||||
return 0; /* varies per mode? */
|
||||
case coding_EA_MT:
|
||||
return 0; /* variable (frames of bit counts or PCM frames) */
|
||||
#ifdef VGM_USE_ATRAC9
|
||||
@ -2102,6 +2115,10 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to
|
||||
}
|
||||
break;
|
||||
|
||||
case coding_UBI_ADPCM:
|
||||
decode_ubi_adpcm(vgmstream, buffer+samples_written*vgmstream->channels, samples_to_do);
|
||||
break;
|
||||
|
||||
case coding_EA_MT:
|
||||
for (ch = 0; ch < 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);
|
||||
}
|
||||
|
||||
if (vgmstream->coding_type == coding_UBI_ADPCM) {
|
||||
seek_ubi_adpcm(vgmstream->codec_data, vgmstream->loop_sample);
|
||||
}
|
||||
|
||||
if (vgmstream->coding_type == coding_EA_MT) {
|
||||
seek_ea_mt(vgmstream, vgmstream->loop_sample);
|
||||
}
|
||||
|
@ -177,6 +177,7 @@ typedef enum {
|
||||
coding_ACM, /* InterPlay ACM */
|
||||
coding_NWA, /* VisualArt's NWA */
|
||||
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) */
|
||||
|
||||
@ -341,7 +342,7 @@ typedef enum {
|
||||
meta_PS2_SShd, /* .ADS with SShd header */
|
||||
meta_NPS,
|
||||
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_SVAG, /* Konami SVAG */
|
||||
meta_PS_HEADERLESS, /* headerless PS-ADPCM */
|
||||
@ -440,24 +441,19 @@ typedef enum {
|
||||
meta_PS2_XA2_RRP, /* RC Revenge Pro */
|
||||
meta_NGC_DSP_KONAMI, /* Konami DSP header, found in various games */
|
||||
meta_UBI_CKD, /* Ubisoft CKD RIFF header (Rayman Origins Wii) */
|
||||
|
||||
meta_XBOX_WAVM, /* XBOX WAVM File */
|
||||
meta_RAW_WAVM,
|
||||
meta_XBOX_WVS, /* XBOX WVS */
|
||||
meta_NGC_WVS, /* Metal Arms - Glitch in the System */
|
||||
meta_XBOX_MATX, /* XBOX MATX */
|
||||
meta_XBOX_XMU, /* XBOX XMU */
|
||||
meta_XBOX_XVAS, /* XBOX VAS */
|
||||
|
||||
meta_XMU,
|
||||
meta_XVAS,
|
||||
meta_EA_SCHL, /* Electronic Arts SCHl with variable header */
|
||||
meta_EA_SCHL_fixed, /* Electronic Arts SCHl with fixed header */
|
||||
meta_EA_BNK, /* Electronic Arts BNK */
|
||||
meta_EA_1SNH, /* Electronic Arts 1SNh/EACS */
|
||||
meta_EA_EACS,
|
||||
|
||||
meta_RAW, /* RAW PCM file */
|
||||
|
||||
meta_RAW_PCM,
|
||||
meta_GENH, /* generic header */
|
||||
|
||||
meta_AIFC, /* Audio Interchange File Format AIFF-C */
|
||||
meta_AIFF, /* Audio Interchange File Format */
|
||||
meta_STR_SNDS, /* .str with SNDS blocks and SHDR header */
|
||||
@ -557,7 +553,7 @@ typedef enum {
|
||||
meta_VS_STR, /* The Bouncer */
|
||||
meta_LSF_N1NJ4N, /* .lsf n1nj4n Fastlane Street Racing (iPhone) */
|
||||
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_HYPERSCAN_KVAG, /* Hyperscan KVAG/BVG */
|
||||
meta_IOS_PSND, /* Crash Bandicoot Nitro Kart 2 (iOS) */
|
||||
@ -1252,6 +1248,8 @@ typedef struct {
|
||||
#endif
|
||||
#endif
|
||||
|
||||
typedef struct ubi_adpcm_codec_data ubi_adpcm_codec_data;
|
||||
|
||||
typedef struct ea_mt_codec_data ea_mt_codec_data;
|
||||
|
||||
|
||||
|
13
version.sh
13
version.sh
@ -1,4 +1,11 @@
|
||||
#!/bin/sh
|
||||
DIR="`dirname "$0"`"
|
||||
VERSION="`git describe --always | tr : _`"
|
||||
echo "$VERSION"
|
||||
|
||||
# get current git version, redirect stderr to stdin, change : to _
|
||||
VERSION=$(git describe --always 2>&1 | tr : _ )
|
||||
|
||||
# ignore git stderr "fatal:
|
||||
if case $VERSION in fatal*) ;; *) false;; esac; then
|
||||
echo ""
|
||||
else
|
||||
echo "$VERSION"
|
||||
fi;
|
||||
|
Loading…
Reference in New Issue
Block a user