Updated VGMStream to r1050-2946-g1e583645
parent
05386bce3a
commit
da72f8bbdb
|
@ -592,6 +592,9 @@
|
|||
83F0AA5F21E2028C004BBC04 /* smp.c in Sources */ = {isa = PBXBuildFile; fileRef = 83F0AA5C21E2028B004BBC04 /* smp.c */; };
|
||||
83F0AA6021E2028C004BBC04 /* vsv_streamfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 83F0AA5D21E2028B004BBC04 /* vsv_streamfile.h */; };
|
||||
83F0AA6121E2028C004BBC04 /* vsv.c in Sources */ = {isa = PBXBuildFile; fileRef = 83F0AA5E21E2028C004BBC04 /* vsv.c */; };
|
||||
83F1EE2D245D4FB20076E182 /* imuse_decoder.c in Sources */ = {isa = PBXBuildFile; fileRef = 83F1EE28245D4FB10076E182 /* imuse_decoder.c */; };
|
||||
83F1EE2E245D4FB20076E182 /* vadpcm_decoder.c in Sources */ = {isa = PBXBuildFile; fileRef = 83F1EE2C245D4FB20076E182 /* vadpcm_decoder.c */; };
|
||||
83F1EE30245D4FC10076E182 /* imuse.c in Sources */ = {isa = PBXBuildFile; fileRef = 83F1EE2F245D4FC10076E182 /* imuse.c */; };
|
||||
83F5F8831908D0A400C8E65F /* fsb5.c in Sources */ = {isa = PBXBuildFile; fileRef = 83F5F8821908D0A400C8E65F /* fsb5.c */; };
|
||||
83FBD506235D31F800D35BCD /* riff_ogg_streamfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 83FBD502235D31F700D35BCD /* riff_ogg_streamfile.h */; };
|
||||
83FC176D23AC58D100E1025F /* xma_ue3.c in Sources */ = {isa = PBXBuildFile; fileRef = 83FC176A23AC58D100E1025F /* xma_ue3.c */; };
|
||||
|
@ -1294,6 +1297,9 @@
|
|||
83F0AA5C21E2028B004BBC04 /* smp.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = smp.c; sourceTree = "<group>"; };
|
||||
83F0AA5D21E2028B004BBC04 /* vsv_streamfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = vsv_streamfile.h; sourceTree = "<group>"; };
|
||||
83F0AA5E21E2028C004BBC04 /* vsv.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = vsv.c; sourceTree = "<group>"; };
|
||||
83F1EE28245D4FB10076E182 /* imuse_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = imuse_decoder.c; sourceTree = "<group>"; };
|
||||
83F1EE2C245D4FB20076E182 /* vadpcm_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = vadpcm_decoder.c; sourceTree = "<group>"; };
|
||||
83F1EE2F245D4FC10076E182 /* imuse.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = imuse.c; sourceTree = "<group>"; };
|
||||
83F412871E932F9A002E37D0 /* Vorbis.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Vorbis.xcodeproj; path = ../Vorbis/macosx/Vorbis.xcodeproj; sourceTree = "<group>"; };
|
||||
83F5F8821908D0A400C8E65F /* fsb5.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = fsb5.c; sourceTree = "<group>"; };
|
||||
83FBD502235D31F700D35BCD /* riff_ogg_streamfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = riff_ogg_streamfile.h; sourceTree = "<group>"; };
|
||||
|
@ -1495,6 +1501,7 @@
|
|||
836F6DE818BDC2180095E648 /* g7221_decoder.c */,
|
||||
832389511D224C0800482226 /* hca_decoder.c */,
|
||||
836F6DEA18BDC2180095E648 /* ima_decoder.c */,
|
||||
83F1EE28245D4FB10076E182 /* imuse_decoder.c */,
|
||||
836F6DEB18BDC2180095E648 /* l5_555_decoder.c */,
|
||||
836F6DEC18BDC2180095E648 /* lsf_decoder.c */,
|
||||
83709E0B1ECBC1C3005C03D3 /* mc3_decoder.c */,
|
||||
|
@ -1527,6 +1534,7 @@
|
|||
836F6DFC18BDC2180095E648 /* sdx2_decoder.c */,
|
||||
8373341023F60C7A00DE14DC /* tgcadpcm_decoder.c */,
|
||||
837CEA7623487E2400E62A4A /* ubi_adpcm_decoder.c */,
|
||||
83F1EE2C245D4FB20076E182 /* vadpcm_decoder.c */,
|
||||
839E21D61F2EDAF000EE54D7 /* vorbis_custom_data_fsb.h */,
|
||||
839E21DC1F2EDAF000EE54D7 /* vorbis_custom_data_wwise.h */,
|
||||
839E21D71F2EDAF000EE54D7 /* vorbis_custom_decoder.c */,
|
||||
|
@ -1717,6 +1725,7 @@
|
|||
83C7280922BC893C00678B4A /* ikm.c */,
|
||||
837CEAE623487F2B00E62A4A /* ima.c */,
|
||||
832BF81121E05149006F50F1 /* imc.c */,
|
||||
83F1EE2F245D4FC10076E182 /* imuse.c */,
|
||||
836F6E5518BDC2180095E648 /* ios_psnd.c */,
|
||||
83AFABBB23795202002F3947 /* isb.c */,
|
||||
836F6E5618BDC2180095E648 /* ish_isd.c */,
|
||||
|
@ -2304,6 +2313,7 @@
|
|||
8373341B23F60C7B00DE14DC /* g7221_decoder_lib.c in Sources */,
|
||||
836F705318BDC2190095E648 /* streamfile.c in Sources */,
|
||||
836F6F7418BDC2190095E648 /* bgw.c in Sources */,
|
||||
83F1EE2D245D4FB20076E182 /* imuse_decoder.c in Sources */,
|
||||
836F6F7218BDC2190095E648 /* baf.c in Sources */,
|
||||
83F5F8831908D0A400C8E65F /* fsb5.c in Sources */,
|
||||
836F6FD418BDC2190095E648 /* ps2_dxh.c in Sources */,
|
||||
|
@ -2399,6 +2409,7 @@
|
|||
834FE107215C79ED000A5D3D /* mib_mih.c in Sources */,
|
||||
836F703618BDC2190095E648 /* thp.c in Sources */,
|
||||
836F6F7818BDC2190095E648 /* Cstr.c in Sources */,
|
||||
83F1EE30245D4FC10076E182 /* imuse.c in Sources */,
|
||||
836F6F1E18BDC2190095E648 /* acm_decoder.c in Sources */,
|
||||
836F6FD818BDC2190095E648 /* ps2_gbts.c in Sources */,
|
||||
831BA61A1EAC61A500CF89B0 /* ps2_vds_vdm.c in Sources */,
|
||||
|
@ -2759,6 +2770,7 @@
|
|||
8306B0B920984552000302D4 /* blocked_matx.c in Sources */,
|
||||
83A21F7B201D895B000F04B9 /* blocked_xvag.c in Sources */,
|
||||
836F6FBA18BDC2190095E648 /* ngc_ymf.c in Sources */,
|
||||
83F1EE2E245D4FB20076E182 /* vadpcm_decoder.c in Sources */,
|
||||
836F705018BDC2190095E648 /* ydsp.c in Sources */,
|
||||
8306B0B720984552000302D4 /* blocked_str_snds.c in Sources */,
|
||||
836F702718BDC2190095E648 /* sat_baka.c in Sources */,
|
||||
|
|
|
@ -61,6 +61,11 @@ void decode_ngc_dtk(VGMSTREAMCHANNEL *stream, sample_t *outbuf, int channelspaci
|
|||
/* ngc_afc_decoder */
|
||||
void decode_ngc_afc(VGMSTREAMCHANNEL *stream, sample_t *outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
|
||||
|
||||
/* vadpcm_decoder */
|
||||
void decode_vadpcm(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int order);
|
||||
//int32_t vadpcm_bytes_to_samples(size_t bytes, int channels);
|
||||
void vadpcm_read_coefs_be(VGMSTREAM* vgmstream, STREAMFILE* sf, off_t offset, int order, int entries, int ch);
|
||||
|
||||
/* pcm_decoder */
|
||||
void decode_pcm16le(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
|
||||
void decode_pcm16be(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
|
||||
|
@ -205,6 +210,14 @@ void seek_ubi_adpcm(ubi_adpcm_codec_data *data, int32_t num_sample);
|
|||
void free_ubi_adpcm(ubi_adpcm_codec_data *data);
|
||||
int ubi_adpcm_get_samples(ubi_adpcm_codec_data *data);
|
||||
|
||||
/* imuse_decoder */
|
||||
typedef struct imuse_codec_data imuse_codec_data;
|
||||
imuse_codec_data *init_imuse(STREAMFILE* sf, int channels);
|
||||
void decode_imuse(VGMSTREAM* vgmstream, sample_t* outbuf, int32_t samples_to_do);
|
||||
void reset_imuse(imuse_codec_data* data);
|
||||
void seek_imuse(imuse_codec_data* data, int32_t num_sample);
|
||||
void free_imuse(imuse_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);
|
||||
|
|
|
@ -0,0 +1,662 @@
|
|||
#include "coding.h"
|
||||
|
||||
|
||||
/* LucasArts' iMUSE decoder, mainly for VIMA (like IMA but with variable frame and code sizes).
|
||||
* Reverse engineered from various .exe
|
||||
*
|
||||
* Info:
|
||||
* - https://github.com/scummvm/scummvm/blob/master/engines/scumm/imuse_digi/dimuse_codecs.cpp (V1)
|
||||
* - https://wiki.multimedia.cx/index.php/VIMA (V2)
|
||||
* - https://github.com/residualvm/residualvm/tree/master/engines/grim/imuse
|
||||
* https://github.com/residualvm/residualvm/tree/master/engines/grim/movie/codecs (V2)
|
||||
*/
|
||||
|
||||
static const int16_t step_table[] = { /* same as IMA */
|
||||
7, 8, 9, 10, 11, 12, 13, 14,
|
||||
16, 17, 19, 21, 23, 25, 28, 31,
|
||||
34, 37, 41, 45, 50, 55, 60, 66,
|
||||
73, 80, 88, 97, 107, 118, 130, 143,
|
||||
157, 173, 190, 209, 230, 253, 279, 307,
|
||||
337, 371, 408, 449, 494, 544, 598, 658,
|
||||
724, 796, 876, 963, 1060, 1166, 1282, 1411,
|
||||
1552, 1707, 1878, 2066, 2272, 2499, 2749, 3024,
|
||||
3327, 3660, 4026, 4428, 4871, 5358, 5894, 6484,
|
||||
7132, 7845, 8630, 9493, 10442,11487,12635,13899,
|
||||
15289,16818,18500,20350,22385,24623,27086,29794,
|
||||
32767,
|
||||
};
|
||||
|
||||
/* pre-calculated in V1:
|
||||
for (i = 0; i < 89; i++) {
|
||||
int counter = (4 * step_table[i] / 7) >> 1;
|
||||
int size = 1;
|
||||
while (counter > 0) {
|
||||
size++;
|
||||
counter >>= 1;
|
||||
}
|
||||
code_size_table[i] = clamp(size, 3, 8) - 1
|
||||
}
|
||||
*/
|
||||
static const uint8_t code_size_table_v1[89] = {
|
||||
2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6,
|
||||
6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
|
||||
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
|
||||
};
|
||||
static const uint8_t code_size_table_v2[89] = {
|
||||
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
|
||||
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6,
|
||||
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
|
||||
};
|
||||
|
||||
static const int8_t index_table2b[4] = {
|
||||
-1, 4,
|
||||
-1, 4,
|
||||
};
|
||||
static const int8_t index_table3b_v1[8] = {
|
||||
-1,-1, 2, 8,
|
||||
-1,-1, 2, 8,
|
||||
};
|
||||
static const int8_t index_table3b_v2[8] = {
|
||||
-1,-1, 2, 6,
|
||||
-1,-1, 2, 6,
|
||||
};
|
||||
static const int8_t index_table4b[16] = {
|
||||
-1,-1,-1,-1, 1, 2, 4, 6,
|
||||
-1,-1,-1,-1, 1, 2, 4, 6,
|
||||
};
|
||||
static const int8_t index_table5b_v1[32] = {
|
||||
-1,-1,-1,-1,-1,-1,-1,-1, 1, 2, 4, 6, 8,12,16,32,
|
||||
-1,-1,-1,-1,-1,-1,-1,-1, 1, 2, 4, 6, 8,12,16,32,
|
||||
};
|
||||
static const int8_t index_table5b_v2[32] = {
|
||||
-1,-1,-1,-1,-1,-1,-1,-1, 1, 1, 1, 2, 2, 4, 5, 6,
|
||||
-1,-1,-1,-1,-1,-1,-1,-1, 1, 1, 1, 2, 2, 4, 5, 6,
|
||||
};
|
||||
static const int8_t index_table6b_v1[64] = {
|
||||
-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1, 1, 2, 4, 6, 8,10,12,14, 16,18,20,22,24,26,28,32,
|
||||
-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1, 1, 2, 4, 6, 8,10,12,14, 16,18,20,22,24,26,28,32,
|
||||
};
|
||||
static const int8_t index_table6b_v2[64] = {
|
||||
-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 4, 4, 4, 5, 5, 6, 6,
|
||||
-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 4, 4, 4, 5, 5, 6, 6,
|
||||
};
|
||||
static const int8_t index_table7b_v1[128] = {
|
||||
-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1,
|
||||
1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,16, 17,18,19,20,21,22,23,24, 25,26,27,28,29,30,31,32,
|
||||
-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1,
|
||||
1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,16, 17,18,19,20,21,22,23,24, 25,26,27,28,29,30,31,32,
|
||||
};
|
||||
static const int8_t index_table7b_v2[128] = {
|
||||
-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6,
|
||||
-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6,
|
||||
};
|
||||
|
||||
static const int8_t* index_tables_v1[8] = {
|
||||
NULL,
|
||||
NULL,
|
||||
index_table2b,
|
||||
index_table3b_v1,
|
||||
index_table4b,
|
||||
index_table5b_v1,
|
||||
index_table6b_v1,
|
||||
index_table7b_v1,
|
||||
};
|
||||
/* seems V2 doesn't actually use <4b, nor mirrored parts, even though they are defined */
|
||||
static const int8_t* index_tables_v2[8] = {
|
||||
NULL,
|
||||
NULL,
|
||||
index_table2b,
|
||||
index_table3b_v2,
|
||||
index_table4b,
|
||||
index_table5b_v2,
|
||||
index_table6b_v2,
|
||||
index_table7b_v2,
|
||||
};
|
||||
|
||||
|
||||
#define MAX_CHANNELS 2
|
||||
#define MAX_BLOCK_SIZE 0x2000
|
||||
#define MAX_BLOCK_COUNT 0x10000 /* arbitrary max */
|
||||
|
||||
/* ************************** */
|
||||
|
||||
typedef struct {
|
||||
/*const*/ int16_t* samples;
|
||||
int filled;
|
||||
int channels;
|
||||
//todo may be more useful with filled/full? use 2 mark methods?
|
||||
} sbuf_t;
|
||||
|
||||
/* copy, move and mark consumed samples */
|
||||
static void sbuf_consume(sample_t** p_outbuf, int32_t* p_samples_to_do, sbuf_t* sbuf) {
|
||||
int samples_consume;
|
||||
|
||||
samples_consume = *p_samples_to_do;
|
||||
if (samples_consume > sbuf->filled)
|
||||
samples_consume = sbuf->filled;
|
||||
|
||||
/* memcpy is safe when filled/samples_copy is 0 (but must pass non-NULL bufs) */
|
||||
memcpy(*p_outbuf, sbuf->samples, samples_consume * sbuf->channels * sizeof(int16_t));
|
||||
|
||||
sbuf->samples += samples_consume * sbuf->channels;
|
||||
sbuf->filled -= samples_consume;
|
||||
|
||||
*p_outbuf += samples_consume * sbuf->channels;
|
||||
*p_samples_to_do -= samples_consume;
|
||||
}
|
||||
|
||||
static int clamp_s32(int val, int min, int max) {
|
||||
if (val > max)
|
||||
return max;
|
||||
else if (val < min)
|
||||
return min;
|
||||
return val;
|
||||
}
|
||||
|
||||
/* ************************** */
|
||||
|
||||
typedef enum { COMP, MCMP } imuse_type_t;
|
||||
struct imuse_codec_data {
|
||||
/* config */
|
||||
imuse_type_t type;
|
||||
int channels;
|
||||
|
||||
size_t block_count;
|
||||
struct block_entry_t {
|
||||
uint32_t offset; /* from file start */
|
||||
uint32_t size;
|
||||
uint32_t flags;
|
||||
uint32_t data;
|
||||
} *block_table;
|
||||
|
||||
uint16_t adpcm_table[64 * 89];
|
||||
|
||||
/* state */
|
||||
sbuf_t sbuf;
|
||||
int current_block;
|
||||
int16_t samples[MAX_BLOCK_SIZE / sizeof(int16_t) * MAX_CHANNELS];
|
||||
};
|
||||
|
||||
|
||||
imuse_codec_data* init_imuse(STREAMFILE* sf, int channels) {
|
||||
int i, j;
|
||||
off_t offset, data_offset;
|
||||
imuse_codec_data* data = NULL;
|
||||
|
||||
if (channels > MAX_CHANNELS)
|
||||
goto fail;
|
||||
|
||||
data = calloc(1, sizeof(struct imuse_codec_data));
|
||||
if (!data) goto fail;
|
||||
|
||||
data->channels = channels;
|
||||
|
||||
/* read index table */
|
||||
if (read_u32be(0x00,sf) == 0x434F4D50) { /* "COMP" */
|
||||
data->block_count = read_u32be(0x04,sf);
|
||||
if (data->block_count > MAX_BLOCK_COUNT) goto fail;
|
||||
/* 08: base codec? */
|
||||
/* 0c: some size? */
|
||||
|
||||
data->block_table = calloc(data->block_count, sizeof(struct block_entry_t));
|
||||
if (!data->block_table) goto fail;
|
||||
|
||||
offset = 0x10;
|
||||
for (i = 0; i < data->block_count; i++) {
|
||||
struct block_entry_t* entry = &data->block_table[i];
|
||||
|
||||
entry->offset = read_u32be(offset + 0x00, sf);
|
||||
entry->size = read_u32be(offset + 0x04, sf);
|
||||
entry->flags = read_u32be(offset + 0x08, sf);
|
||||
/* 0x0c: null */
|
||||
entry->data = MAX_BLOCK_SIZE;
|
||||
/* blocks decode into fixed size, that may include header */
|
||||
|
||||
if (entry->size > MAX_BLOCK_SIZE) {
|
||||
VGM_LOG("IMUSE: block size too big\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (entry->flags != 0x0D && entry->flags != 0x0F) { /* VIMA */
|
||||
VGM_LOG("IMUSE: unknown codec\n");
|
||||
goto fail; /* others are bunch of mini-codecs (ex. The Dig) */
|
||||
}
|
||||
|
||||
offset += 0x10;
|
||||
}
|
||||
|
||||
/* detect type */
|
||||
{
|
||||
uint32_t id = read_u32be(data->block_table[0].offset + 0x02, sf);
|
||||
if (id == 0x694D5553) { /* "iMUS" header [The Curse of Monkey Island (PC)] */
|
||||
data->type = COMP;
|
||||
} else {
|
||||
goto fail; /* no header [The Dig (PC)] */
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (read_u32be(0x00,sf) == 0x4D434D50) { /* "MCMP" */
|
||||
data->block_count = read_u16be(0x04,sf);
|
||||
if (data->block_count > MAX_BLOCK_COUNT) goto fail;
|
||||
|
||||
data->block_table = calloc(data->block_count, sizeof(struct block_entry_t));
|
||||
if (!data->block_table) goto fail;
|
||||
|
||||
/* pre-calculate for simpler logic */
|
||||
data_offset = 0x06 + data->block_count * 0x09;
|
||||
data_offset += 0x02 + read_u16be(data_offset + 0x00, sf); /* mini text header */
|
||||
|
||||
offset = 0x06;
|
||||
for (i = 0; i < data->block_count; i++) {
|
||||
struct block_entry_t* entry = &data->block_table[i];
|
||||
|
||||
entry->flags = read_u8 (offset + 0x00, sf);
|
||||
entry->data = read_u32be(offset + 0x01, sf);
|
||||
entry->size = read_u32be(offset + 0x05, sf);
|
||||
entry->offset = data_offset;
|
||||
/* blocks of data and audio are separate */
|
||||
|
||||
if (entry->data > MAX_BLOCK_SIZE || entry->size > MAX_BLOCK_SIZE) {
|
||||
VGM_LOG("IMUSE: block size too big\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (entry->flags != 0x00 && entry->flags != 0x01) { /* data or VIMA */
|
||||
VGM_LOG("IMUSE: unknown codec\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
offset += 0x09;
|
||||
data_offset += entry->size;
|
||||
}
|
||||
|
||||
data->type = MCMP; /* with header [Grim Fandango (multi)] */
|
||||
|
||||
/* there are iMUS or RIFF headers but affect parser */
|
||||
}
|
||||
else {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* iMUSE pre-calculates main decode ops as a table, looks similar to standard IMA expand */
|
||||
for (i = 0; i < 64; i++) {
|
||||
for (j = 0; j < 89; j++) {
|
||||
int counter = 32;
|
||||
int value = 0;
|
||||
int step = step_table[j];
|
||||
while (counter > 0) {
|
||||
if (counter & i)
|
||||
value += step;
|
||||
step >>= 1;
|
||||
counter >>= 1;
|
||||
}
|
||||
|
||||
data->adpcm_table[i + j * 64] = value; /* non sequential: all 64 [0]s, [1]s ... [88]s */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
reset_imuse(data);
|
||||
|
||||
return data;
|
||||
|
||||
fail:
|
||||
free_imuse(data);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* **************************************** */
|
||||
|
||||
static void decode_vima1(STREAMFILE* sf, sbuf_t* sbuf, uint8_t* buf, size_t data_left, int block_num, uint16_t* adpcm_table) {
|
||||
int ch, i, j, s;
|
||||
int bitpos;
|
||||
int adpcm_history[MAX_CHANNELS] = {0};
|
||||
int adpcm_step_index[MAX_CHANNELS] = {0};
|
||||
int chs = sbuf->channels;
|
||||
|
||||
/* read header (outside decode in original code) */
|
||||
{
|
||||
int pos = 0;
|
||||
size_t copy_size;
|
||||
|
||||
/* decodes BLOCK_SIZE bytes (not samples), including copy_size if exists, but not first 16b
|
||||
* or ADPCM headers. ADPCM setup must be set to 0 if headers weren't read. */
|
||||
copy_size = get_u16be(buf + pos);
|
||||
pos += 0x02;
|
||||
|
||||
if (block_num == 0 && copy_size > 0) {
|
||||
/* iMUS header (always in first block) */
|
||||
pos += copy_size;
|
||||
data_left -= copy_size;
|
||||
}
|
||||
else if (copy_size > 0) {
|
||||
/* presumably PCM data (not seen) */
|
||||
for (i = 0, j = pos; i < copy_size / sizeof(sample_t); i++, j += 2) {
|
||||
sbuf->samples[i] = get_s16le(buf + j);
|
||||
}
|
||||
sbuf->filled += copy_size / chs / sizeof(sample_t);
|
||||
|
||||
pos += copy_size;
|
||||
data_left -= copy_size;
|
||||
|
||||
VGM_LOG("IMUS: found PCM block %i\n", block_num);
|
||||
}
|
||||
else {
|
||||
/* ADPCM header (never in first block) */
|
||||
for (i = 0; i < chs; i++) {
|
||||
adpcm_step_index[i] = get_u8 (buf + pos + 0x00);
|
||||
//adpcm_step[i] = get_s32be(buf + pos + 0x01); /* same as step_table[step_index] */
|
||||
adpcm_history[i] = get_s32be(buf + pos + 0x05);
|
||||
pos += 0x09;
|
||||
|
||||
adpcm_step_index[i] = clamp_s32(adpcm_step_index[i], 0, 88); /* not originally */
|
||||
}
|
||||
}
|
||||
|
||||
bitpos = pos * 8;
|
||||
}
|
||||
|
||||
|
||||
/* decode ADPCM data after header
|
||||
* (stereo layout: all samples from L, then all for R) */
|
||||
for (ch = 0; ch < chs; ch++) {
|
||||
int sample, step_index;
|
||||
int samples_to_do;
|
||||
int samples_left = data_left / sizeof(int16_t);
|
||||
int first_sample = sbuf->filled * chs + ch;
|
||||
|
||||
if (chs == 1) {
|
||||
samples_to_do = samples_left;
|
||||
} else {
|
||||
/* L has +1 code for aligment in first block, must be read to reach R (code seems empty).
|
||||
* Not sure if COMI uses decoded bytes or decoded samples (returns samples_left / channels)
|
||||
* though but the latter makes more sense. */
|
||||
if (ch == 0)
|
||||
samples_to_do = (samples_left + 1) / chs;
|
||||
else
|
||||
samples_to_do = (samples_left + 0) / chs;
|
||||
}
|
||||
|
||||
//;VGM_ASSERT((samples_left + 1) / 2 != (samples_left + 0) / 2, "IMUSE: sample diffs\n");
|
||||
|
||||
step_index = adpcm_step_index[ch];
|
||||
sample = adpcm_history[ch];
|
||||
|
||||
for (i = 0, s = first_sample; i < samples_to_do; i++, s += chs) {
|
||||
int code_size, code, sign_mask, data_mask, delta;
|
||||
|
||||
if (bitpos >= MAX_BLOCK_SIZE * 8) {
|
||||
VGM_LOG("IMUSE: wrong bit offset\n");
|
||||
break;
|
||||
}
|
||||
|
||||
code_size = code_size_table_v1[step_index];
|
||||
|
||||
/* get bit thing from COMI (reads closest 16b then masks + shifts as needed), BE layout */
|
||||
code = get_u16be(buf + (bitpos >> 3)); //ok
|
||||
code = (code << (bitpos & 7)) & 0xFFFF;
|
||||
code = code >> (16 - code_size);
|
||||
bitpos += code_size;
|
||||
|
||||
sign_mask = (1 << (code_size - 1));
|
||||
data_mask = sign_mask - 1; /* done with a LUT in COMI */
|
||||
|
||||
delta = adpcm_table[(step_index * 64) + (((code & data_mask) << (7 - code_size)))];
|
||||
delta += step_table[step_index] >> (code_size - 1);
|
||||
if (code & sign_mask)
|
||||
delta = -delta;
|
||||
|
||||
sample += delta;
|
||||
sample = clamp16(sample);
|
||||
sbuf->samples[s] = sample;
|
||||
|
||||
step_index += index_tables_v1[code_size][code];
|
||||
step_index = clamp_s32(step_index, 0, 88);
|
||||
}
|
||||
}
|
||||
|
||||
sbuf->filled += data_left / sizeof(int16_t) / chs;
|
||||
}
|
||||
|
||||
static int decode_block1(STREAMFILE* sf, imuse_codec_data* data, uint8_t* block, size_t data_left) {
|
||||
int block_num = data->current_block;
|
||||
|
||||
switch(data->block_table[block_num].flags) {
|
||||
case 0x0D:
|
||||
case 0x0F:
|
||||
decode_vima1(sf, &data->sbuf, block, data_left, block_num, data->adpcm_table);
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void decode_data2(STREAMFILE* sf, sbuf_t* sbuf, uint8_t* buf, size_t data_left, int block_num) {
|
||||
int i, j;
|
||||
int channels = sbuf->channels;
|
||||
|
||||
if (block_num == 0) {
|
||||
/* iMUS header (always in first block, not shared with audio data unlike V1) */
|
||||
sbuf->filled = 0;
|
||||
}
|
||||
else {
|
||||
/* presumably PCM data (not seen) */
|
||||
for (i = 0, j = 0; i < data_left / sizeof(sample_t); i++, j += 2) {
|
||||
sbuf->samples[i] = get_s16le(buf + j);
|
||||
}
|
||||
sbuf->filled += data_left / channels / sizeof(sample_t);
|
||||
|
||||
VGM_LOG("IMUS: found PCM block %i\n", block_num);
|
||||
}
|
||||
}
|
||||
|
||||
static void decode_vima2(STREAMFILE* sf, sbuf_t* sbuf, uint8_t* buf, size_t data_left, uint16_t* adpcm_table) {
|
||||
int ch, i, s;
|
||||
int bitpos;
|
||||
int adpcm_history[MAX_CHANNELS] = {0};
|
||||
int adpcm_step_index[MAX_CHANNELS] = {0};
|
||||
int chs = sbuf->channels;
|
||||
uint16_t word;
|
||||
int pos = 0;
|
||||
|
||||
|
||||
/* read ADPCM header */
|
||||
{
|
||||
|
||||
for (i = 0; i < chs; i++) {
|
||||
adpcm_step_index[i] = get_u8 (buf + pos + 0x00);
|
||||
adpcm_history[i] = get_s16be(buf + pos + 0x01);
|
||||
pos += 0x03;
|
||||
|
||||
/* checked as < 0 and only for first index, means "stereo" */
|
||||
if (adpcm_step_index[i] & 0x80) {
|
||||
adpcm_step_index[i] = (~adpcm_step_index[i]) & 0xFF;
|
||||
if (chs != 2) return;
|
||||
}
|
||||
|
||||
/* not originally done but in case of garbage data */
|
||||
adpcm_step_index[i] = clamp_s32(adpcm_step_index[i], 0, 88);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bitpos = 0;
|
||||
word = get_u16be(buf + pos); /* originally with a rolling buf, use index to validate overflow */
|
||||
pos += 0x02;
|
||||
|
||||
/* decode ADPCM data after header
|
||||
* (stereo layout: all samples from L, then all for R) */
|
||||
for (ch = 0; ch < chs; ch++) {
|
||||
int sample, step_index;
|
||||
int samples_to_do;
|
||||
int samples_left = data_left / sizeof(int16_t);
|
||||
int first_sample = sbuf->filled * chs + ch;
|
||||
|
||||
samples_to_do = samples_left / chs;
|
||||
|
||||
step_index = adpcm_step_index[ch];
|
||||
sample = adpcm_history[ch];
|
||||
|
||||
for (i = 0, s = first_sample; i < samples_to_do; i++, s += chs) {
|
||||
int code_size, code, sign_mask, data_mask, delta;
|
||||
|
||||
if (pos >= MAX_BLOCK_SIZE) {
|
||||
VGM_LOG("IMUSE: wrong pos offset\n");
|
||||
break;
|
||||
}
|
||||
|
||||
code_size = code_size_table_v2[step_index];
|
||||
sign_mask = (1 << (code_size - 1));
|
||||
data_mask = (sign_mask - 1);
|
||||
|
||||
/* get bit thing, masks current code and moves 'upwards' word after reading 8 bits */
|
||||
bitpos += code_size;
|
||||
code = (word >> (16 - bitpos)) & (sign_mask | data_mask);
|
||||
if (bitpos > 7) {
|
||||
word = (word << 8) | buf[pos++];
|
||||
bitpos -= 8;
|
||||
}
|
||||
|
||||
/* clean sign stuff for next tests */
|
||||
if (code & sign_mask)
|
||||
code ^= sign_mask;
|
||||
else
|
||||
sign_mask = 0;
|
||||
|
||||
/* all bits set mean 'keyframe' = read next sample */
|
||||
if (code == data_mask) {
|
||||
sample = (int16_t)(word << bitpos);
|
||||
word = (word << 8) | buf[pos++];
|
||||
sample |= (word >> (8 - bitpos)) & 0xFF;
|
||||
word = (word << 8) | buf[pos++];
|
||||
}
|
||||
else {
|
||||
delta = adpcm_table[(step_index * 64) + ((code << (7 - code_size)))];
|
||||
if (code)
|
||||
delta += step_table[step_index] >> (code_size - 1);
|
||||
if (sign_mask)
|
||||
delta = -delta;
|
||||
|
||||
sample += delta;
|
||||
sample = clamp16(sample);
|
||||
}
|
||||
|
||||
sbuf->samples[s] = sample;
|
||||
|
||||
step_index += index_tables_v2[code_size][code];
|
||||
step_index = clamp_s32(step_index, 0, 88);
|
||||
}
|
||||
}
|
||||
|
||||
sbuf->filled += data_left / sizeof(int16_t) / chs;
|
||||
}
|
||||
|
||||
static int decode_block2(STREAMFILE* sf, imuse_codec_data* data, uint8_t* block, size_t data_left) {
|
||||
int block_num = data->current_block;
|
||||
|
||||
switch(data->block_table[block_num].flags) {
|
||||
case 0x00:
|
||||
decode_data2(sf, &data->sbuf, block, data_left, block_num);
|
||||
break;
|
||||
|
||||
case 0x01:
|
||||
decode_vima2(sf, &data->sbuf, block, data_left, data->adpcm_table);
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/* decodes a whole block into sample buffer, all at once due to L/R layout and VBR data */
|
||||
static int decode_block(STREAMFILE* sf, imuse_codec_data* data) {
|
||||
int ok;
|
||||
uint8_t block[MAX_BLOCK_SIZE];
|
||||
size_t data_left;
|
||||
|
||||
data->sbuf.samples = data->samples;
|
||||
data->sbuf.channels = data->channels;
|
||||
|
||||
if (data->current_block >= data->block_count) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* read block */
|
||||
{
|
||||
off_t offset = data->block_table[data->current_block].offset;
|
||||
size_t size = data->block_table[data->current_block].size;
|
||||
|
||||
read_streamfile(block, offset, size, sf);
|
||||
|
||||
data_left = data->block_table[data->current_block].data;
|
||||
}
|
||||
|
||||
switch(data->type) {
|
||||
case COMP:
|
||||
ok = decode_block1(sf, data, block, data_left);
|
||||
break;
|
||||
|
||||
case MCMP:
|
||||
ok = decode_block2(sf, data, block, data_left);
|
||||
break;
|
||||
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* block fully read */
|
||||
data->current_block++;
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
|
||||
void decode_imuse(VGMSTREAM* vgmstream, sample_t* outbuf, int32_t samples_to_do) {
|
||||
imuse_codec_data* data = vgmstream->codec_data;
|
||||
STREAMFILE* sf = vgmstream->ch[0].streamfile;
|
||||
int ok;
|
||||
|
||||
|
||||
while (samples_to_do > 0) {
|
||||
sbuf_t* sbuf = &data->sbuf;
|
||||
|
||||
if (sbuf->filled == 0) {
|
||||
ok = decode_block(sf, data);
|
||||
if (!ok) goto fail;
|
||||
}
|
||||
|
||||
sbuf_consume(&outbuf, &samples_to_do, sbuf);
|
||||
}
|
||||
|
||||
return;
|
||||
fail:
|
||||
//todo fill silence
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
void free_imuse(imuse_codec_data* data) {
|
||||
if (!data) return;
|
||||
|
||||
free(data->block_table);
|
||||
free(data);
|
||||
}
|
||||
|
||||
void seek_imuse(imuse_codec_data* data, int32_t num_sample) {
|
||||
if (!data) return;
|
||||
|
||||
//todo find closest block, set skip count
|
||||
|
||||
reset_imuse(data);
|
||||
}
|
||||
|
||||
void reset_imuse(imuse_codec_data* data) {
|
||||
if (!data) return;
|
||||
|
||||
data->current_block = 0;
|
||||
data->sbuf.filled = 0;
|
||||
}
|
|
@ -0,0 +1,155 @@
|
|||
#include "coding.h"
|
||||
#include "../util.h"
|
||||
|
||||
|
||||
/* Decodes Silicon Graphics' N64 VADPCM, big brother of GC ADPCM.
|
||||
* Has external coefs like DSP, but allows tables up to 8 groups of 8 coefs (code book) and also
|
||||
* up to 8 history samples (order). In practice order must be 2, while files use 2~4 tables, so it
|
||||
* ends up being an overcomplex XA. Code respects this quirky configurable hist for doc purposes though.
|
||||
*
|
||||
* This code is based on N64SoundListTool decoding (by Ice Mario), with bits of the official SDK vadpcm tool
|
||||
* decompilation. I can't get proper sound from the later though, so not too sure about accuracy of this
|
||||
* implementation. Output sounds correct though.
|
||||
*/
|
||||
|
||||
void decode_vadpcm(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int order) {
|
||||
uint8_t frame[0x09] = {0};
|
||||
off_t frame_offset;
|
||||
int frames_in, sample_count = 0;
|
||||
size_t bytes_per_frame, samples_per_frame;
|
||||
int i, j, k, o;
|
||||
int scale, index;
|
||||
|
||||
int codes[16]; /* AKA ix */
|
||||
int16_t hist[8] = {0};
|
||||
int16_t out[16];
|
||||
int16_t* coefs;
|
||||
|
||||
|
||||
VGM_ASSERT_ONCE(order != 2, "VADPCM: wrong order=%i\n", order);
|
||||
if (order != 2) /* only 2 allowed "in the current implementation" */
|
||||
order = 2;
|
||||
|
||||
/* up to 8 (hist[0]=oldest) but only uses latest 2 (order=2), so we don't save the whole thing ATM */
|
||||
hist[6] = stream->adpcm_history2_16;
|
||||
hist[7] = stream->adpcm_history1_16;
|
||||
|
||||
|
||||
/* external interleave (fixed size), mono */
|
||||
bytes_per_frame = 0x09;
|
||||
samples_per_frame = (bytes_per_frame - 0x01) * 2; /* always 16 */
|
||||
frames_in = first_sample / samples_per_frame;
|
||||
first_sample = first_sample % samples_per_frame;
|
||||
|
||||
/* parse frame header */
|
||||
frame_offset = stream->offset + bytes_per_frame * frames_in;
|
||||
read_streamfile(frame, frame_offset, bytes_per_frame, stream->streamfile); /* ignore EOF errors */
|
||||
scale = (frame[0] >> 4) & 0xF;
|
||||
index = (frame[0] >> 0) & 0xF;
|
||||
|
||||
scale = 1 << scale;
|
||||
|
||||
VGM_ASSERT_ONCE(index > 8, "DSP: incorrect index at %x\n", (uint32_t)frame_offset);
|
||||
if (index > 8) /* assumed */
|
||||
index = 8;
|
||||
coefs = &stream->vadpcm_coefs[index * (order*8) + 0];
|
||||
|
||||
|
||||
/* read and pre-scale all nibbles, since groups of 8 are needed */
|
||||
for (i = 0, j = 0; i < 16; i += 2, j++) {
|
||||
int n0 = (frame[j+1] >> 4) & 0xF;
|
||||
int n1 = (frame[j+1] >> 0) & 0xF;
|
||||
|
||||
/* sign extend */
|
||||
if (n0 & 8)
|
||||
n0 = n0 - 16;
|
||||
if (n1 & 8)
|
||||
n1 = n1 - 16;
|
||||
|
||||
codes[i+0] = n0 * scale;
|
||||
codes[i+1] = n1 * scale;
|
||||
}
|
||||
|
||||
/* decode 2 sub-frames of 8 samples (maybe like this since N64 MIPS has registers to spare?) */
|
||||
for (j = 0; j < 2; j++) {
|
||||
/* SDK dec code and N64ST copy 8 codes to a tmp buf and has some complex logic to move out buf
|
||||
* around, but since that's useless N64 asm would be much more optimized... hopefully */
|
||||
int* sf_codes = &codes[j*8];
|
||||
int16_t* sf_out = &out[j*8];
|
||||
|
||||
/* works with 8 samples at a time, related in twisted ways */
|
||||
for( i = 0; i < 8; i++) {
|
||||
int sample, delta = 0;
|
||||
|
||||
/* in practice: delta = coefs[0][i] * hist[6] + coefs[1][i] * hist[7],
|
||||
* much like XA's coef1*hist1 + coef2*hist2 but with multi coefs */
|
||||
for (o = 0; o < order; o++) {
|
||||
delta += coefs[o*8 + i] * hist[(8 - order) + o];
|
||||
}
|
||||
|
||||
/* adds all previous samples */
|
||||
for (k = i-1; k > -1; k--) {
|
||||
for (o = 1; o < order; o++) { /* assumed, since only goes coefs[1][k] */
|
||||
delta += sf_codes[(i-1) - k] * coefs[(o*8) + k];
|
||||
}
|
||||
}
|
||||
|
||||
/* scale-filter thing (also seen in DSP) */
|
||||
sample = (sf_codes[i] << 11);
|
||||
sample = (sample + delta) >> 11;
|
||||
if (sample > 32767)
|
||||
sample = 32767;
|
||||
else if (sample < -32768)
|
||||
sample = -32768;
|
||||
|
||||
sf_out[i] = sample;
|
||||
}
|
||||
|
||||
/* save subframe hist */
|
||||
for (i = 8 - order; i < 8; i++) {
|
||||
hist[i] = sf_out[i];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* copy samples last, since the whole thing is kinda complex to worry about half copying and stuff */
|
||||
for (i = first_sample; i < first_sample + samples_to_do; i++) {
|
||||
outbuf[sample_count] = out[i];
|
||||
sample_count += channelspacing;
|
||||
}
|
||||
|
||||
/* update hist once all frame is actually copied */
|
||||
if (first_sample + sample_count == samples_per_frame) {
|
||||
stream->adpcm_history2_16 = hist[6];
|
||||
stream->adpcm_history1_16 = hist[7];
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
int32_t vadpcm_bytes_to_samples(size_t bytes, int channels) {
|
||||
if (channels <= 0) return 0;
|
||||
return bytes / channels / 0x09 * 16;
|
||||
}
|
||||
*/
|
||||
|
||||
/* Reads code book, linearly unlike original SDK, that does some strange reordering and pre-scaling
|
||||
* to reduce some loops. Format is 8 coefs per 'order' per 'entries' (max 8, but order is always 2). So:
|
||||
* - i: table index (selectable filter tables on every decoded frame)
|
||||
* - j: order index (coefs for prev N hist samples)
|
||||
* - k: coef index (multiplication coefficient for 8 samples in a sub-frame)
|
||||
* coefs[i * (order*8) + j * 8 + k * order] = coefs[i][j][k] */
|
||||
void vadpcm_read_coefs_be(VGMSTREAM* vgmstream, STREAMFILE* sf, off_t offset, int order, int entries, int ch) {
|
||||
int i;
|
||||
|
||||
if (entries > 8)
|
||||
entries = 8;
|
||||
VGM_ASSERT(order != 2, "VADPCM: wrong order %i found\n", order);
|
||||
if (order != 2)
|
||||
order = 2;
|
||||
|
||||
/* assumes all channels use same coefs, never seen non-mono files */
|
||||
for (i = 0; i < entries * order * 8; i++) {
|
||||
vgmstream->ch[ch].vadpcm_coefs[i] = read_s16be(offset + i*2, sf);
|
||||
}
|
||||
vgmstream->codec_config = order;
|
||||
}
|
|
@ -222,6 +222,7 @@ static const char* extension_list[] = {
|
|||
"ilv", //txth/reserved [Star Wars Episode III (PS2)]
|
||||
"ima",
|
||||
"imc",
|
||||
"imx",
|
||||
"int",
|
||||
"is14",
|
||||
"isb",
|
||||
|
@ -329,6 +330,7 @@ static const char* extension_list[] = {
|
|||
"mxst",
|
||||
"myspd",
|
||||
|
||||
"n64",
|
||||
"naac",
|
||||
"ndp",
|
||||
"ngca",
|
||||
|
@ -684,6 +686,7 @@ static const coding_info coding_info_list[] = {
|
|||
{coding_NGC_DSP_subint, "Nintendo DSP 4-bit ADPCM (subinterleave)"},
|
||||
{coding_NGC_DTK, "Nintendo DTK 4-bit ADPCM"},
|
||||
{coding_NGC_AFC, "Nintendo AFC 4-bit ADPCM"},
|
||||
{coding_VADPCM, "Silicon Graphics VADPCM 4-bit ADPCM"},
|
||||
|
||||
{coding_G721, "CCITT G.721 4-bit ADPCM"},
|
||||
|
||||
|
@ -764,6 +767,7 @@ static const coding_info coding_info_list[] = {
|
|||
{coding_NWA, "VisualArt's NWA DPCM"},
|
||||
{coding_CIRCUS_ADPCM, "Circus 8-bit ADPCM"},
|
||||
{coding_UBI_ADPCM, "Ubisoft 4/6-bit ADPCM"},
|
||||
{coding_IMUSE, "LucasArts iMUSE VIMA ADPCM"},
|
||||
|
||||
{coding_EA_MT, "Electronic Arts MicroTalk"},
|
||||
{coding_CIRCUS_VQ, "Circus VQ"},
|
||||
|
@ -1287,6 +1291,7 @@ static const meta_info meta_info_list[] = {
|
|||
{meta_LRMD, "Sony LRMD header"},
|
||||
{meta_WWISE_FX, "Audiokinetic Wwise FX header"},
|
||||
{meta_DIVA, "DIVA header"},
|
||||
{meta_IMUSE, "LucasArts iMUSE header"},
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -3,21 +3,22 @@
|
|||
#include "../coding/coding.h"
|
||||
|
||||
|
||||
/* for reading integers inexplicably packed into 80-bit ('double extended') floats */
|
||||
static uint32_t read80bitSANE(off_t offset, STREAMFILE *streamFile) {
|
||||
/* for reading integers inexplicably packed into 80-bit ('double extended') floats, AKA:
|
||||
* "80 bit IEEE Standard 754 floating point number (Standard AppleNumeric Environment [SANE] data type Extended)" */
|
||||
static uint32_t read_f80be(off_t offset, STREAMFILE* sf) {
|
||||
uint8_t buf[0x0a];
|
||||
int32_t exponent;
|
||||
int32_t mantissa;
|
||||
int i;
|
||||
|
||||
if (read_streamfile(buf,offset,0x0a,streamFile) != 0x0a)
|
||||
if (read_streamfile(buf, offset, sizeof(buf), sf) != sizeof(buf))
|
||||
return 0;
|
||||
|
||||
exponent = ((buf[0]<<8)|(buf[1]))&0x7fff;
|
||||
exponent = ((buf[0]<<8) | (buf[1])) & 0x7fff;
|
||||
exponent -= 16383;
|
||||
|
||||
mantissa = 0;
|
||||
for (i=0;i<8;i++) {
|
||||
for (i = 0; i < 8; i++) {
|
||||
int32_t shift = exponent-7-i*8;
|
||||
if (shift >= 0)
|
||||
mantissa |= buf[i+2] << shift;
|
||||
|
@ -25,46 +26,58 @@ static uint32_t read80bitSANE(off_t offset, STREAMFILE *streamFile) {
|
|||
mantissa |= buf[i+2] >> -shift;
|
||||
}
|
||||
|
||||
return mantissa*((buf[0]&0x80)?-1:1);
|
||||
return mantissa * ((buf[0]&0x80) ? -1 : 1);
|
||||
}
|
||||
|
||||
static uint32_t find_marker(STREAMFILE *streamFile, off_t mark_offset, int marker_id) {
|
||||
static uint32_t find_marker(STREAMFILE* sf, off_t mark_offset, int marker_id) {
|
||||
uint16_t marker_count;
|
||||
int i;
|
||||
off_t marker_offset;
|
||||
int i;
|
||||
|
||||
marker_count = read_16bitBE(mark_offset+8,streamFile);
|
||||
marker_offset = mark_offset+10;
|
||||
for (i=0;i<marker_count;i++) {
|
||||
marker_count = read_u16be(mark_offset + 0x00,sf);
|
||||
marker_offset = mark_offset + 0x02;
|
||||
for (i = 0; i < marker_count; i++) {
|
||||
int name_length;
|
||||
|
||||
if (read_16bitBE(marker_offset,streamFile) == marker_id)
|
||||
return read_32bitBE(marker_offset+2,streamFile);
|
||||
if (read_u16be(marker_offset + 0x00, sf) == marker_id)
|
||||
return read_u32be(marker_offset + 0x02,sf);
|
||||
|
||||
name_length = (uint8_t)read_8bit(marker_offset+6,streamFile) + 1;
|
||||
name_length = read_u8(marker_offset + 0x06,sf) + 1;
|
||||
if (name_length % 2) name_length++;
|
||||
marker_offset += 6 + name_length;
|
||||
marker_offset += 0x06 + name_length;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int is_str(const char* str, int len, off_t offset, STREAMFILE* sf) {
|
||||
uint8_t buf[0x100];
|
||||
|
||||
/* Audio Interchange File Format AIFF/AIFF-C - from Mac/3DO games */
|
||||
VGMSTREAM * init_vgmstream_aifc(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
off_t start_offset = 0;
|
||||
if (len == 0)
|
||||
len = strlen(str);
|
||||
|
||||
if (len > sizeof(buf))
|
||||
return 0;
|
||||
if (read_streamfile(buf, offset, len, sf) != len)
|
||||
return 0;
|
||||
return memcmp(buf, str, len) == 0; /* memcmp to allow "AB\0\0" */
|
||||
}
|
||||
|
||||
|
||||
/* AIFF/AIFF-C (Audio Interchange File Format - Compressed) - Apple format, from Mac/3DO/other games */
|
||||
VGMSTREAM* init_vgmstream_aifc(STREAMFILE* sf) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
off_t start_offset = 0, coef_offset = 0;
|
||||
size_t file_size;
|
||||
coding_t coding_type = 0;
|
||||
int channel_count = 0, sample_count = 0, sample_size = 0, sample_rate = 0;
|
||||
int channels = 0, sample_count = 0, sample_size = 0, sample_rate = 0;
|
||||
int interleave = 0;
|
||||
|
||||
int loop_flag = 0;
|
||||
int32_t loop_start = 0, loop_end = 0;
|
||||
|
||||
int is_aiff_ext = 0, is_aifc_ext = 0, is_aiff = 0, is_aifc = 0;
|
||||
int fver_found = 0, comm_found = 0, data_found = 0, mark_found = 0, inst_found = 0;
|
||||
off_t mark_offset = -1, inst_offset = -1;
|
||||
int fver_found = 0, comm_found = 0, data_found = 0;
|
||||
off_t mark_offset = 0, inst_offset = 0;
|
||||
|
||||
|
||||
/* checks */
|
||||
|
@ -77,31 +90,34 @@ VGMSTREAM * init_vgmstream_aifc(STREAMFILE *streamFile) {
|
|||
* .adp: Sonic Jam (SAT)
|
||||
* .ai: Dragon Force (SAT)
|
||||
* (extensionless: Doom (3DO)
|
||||
* .fda: Homeworld 2 (PC) */
|
||||
if (check_extensions(streamFile, "aif,laif,")) {
|
||||
* .fda: Homeworld 2 (PC)
|
||||
* .n64: Turok (N64) src */
|
||||
if (check_extensions(sf, "aif,laif,")) {
|
||||
is_aifc_ext = 1;
|
||||
is_aiff_ext = 1;
|
||||
}
|
||||
else if (check_extensions(streamFile, "aifc,laifc,aifcl,afc,cbd2,bgm,fda")) {
|
||||
else if (check_extensions(sf, "aifc,laifc,aifcl,afc,cbd2,bgm,fda,n64")) {
|
||||
is_aifc_ext = 1;
|
||||
}
|
||||
else if (check_extensions(streamFile, "aiff,laiff,acm,adp,ai,aiffl")) {
|
||||
else if (check_extensions(sf, "aiff,laiff,acm,adp,ai,aiffl")) {
|
||||
is_aiff_ext = 1;
|
||||
}
|
||||
else {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
file_size = get_streamfile_size(streamFile);
|
||||
if ((uint32_t)read_32bitBE(0x00,streamFile) != 0x464F524D && /* "FORM" */
|
||||
(uint32_t)read_32bitBE(0x04,streamFile)+0x08 != file_size)
|
||||
file_size = get_streamfile_size(sf);
|
||||
if (read_u32be(0x00,sf) != 0x464F524D && /* "FORM" */
|
||||
read_u32be(0x04,sf)+0x08 != file_size)
|
||||
goto fail;
|
||||
|
||||
if ((uint32_t)read_32bitBE(0x08,streamFile) == 0x41494643) { /* "AIFC" */
|
||||
/* AIFF originally allowed only PCM (non-compressed) audio, so newer AIFC was added,
|
||||
* though some AIFF with other codecs exist */
|
||||
if (read_u32be(0x08,sf) == 0x41494643) { /* "AIFC" */
|
||||
if (!is_aifc_ext) goto fail;
|
||||
is_aifc = 1;
|
||||
}
|
||||
else if ((uint32_t)read_32bitBE(0x08,streamFile) == 0x41494646) { /* "AIFF" */
|
||||
else if (read_u32be(0x08,sf) == 0x41494646) { /* "AIFF" */
|
||||
if (!is_aiff_ext) goto fail;
|
||||
is_aiff = 1;
|
||||
}
|
||||
|
@ -112,73 +128,78 @@ VGMSTREAM * init_vgmstream_aifc(STREAMFILE *streamFile) {
|
|||
|
||||
/* read through chunks to verify format and find metadata */
|
||||
{
|
||||
off_t current_chunk = 0x0c; /* start with first chunk within FORM */
|
||||
off_t offset = 0x0c; /* start with first chunk within FORM */
|
||||
|
||||
while (current_chunk < file_size) {
|
||||
uint32_t chunk_type = read_32bitBE(current_chunk,streamFile);
|
||||
off_t chunk_size = read_32bitBE(current_chunk+0x04,streamFile);
|
||||
while (offset < file_size) {
|
||||
uint32_t chunk_type = read_u32be(offset + 0x00,sf);
|
||||
uint32_t chunk_size = read_u32be(offset + 0x04,sf);
|
||||
|
||||
/* chunks must be padded to an even number of bytes but chunk
|
||||
* size does not include that padding */
|
||||
if (chunk_size % 2) chunk_size++;
|
||||
if (chunk_size % 2)
|
||||
chunk_size++;
|
||||
|
||||
if (current_chunk+8+chunk_size > file_size) goto fail;
|
||||
offset += 0x08;
|
||||
if (offset + chunk_size > file_size)
|
||||
goto fail;
|
||||
|
||||
switch(chunk_type) {
|
||||
case 0x46564552: /* "FVER" (version info) */
|
||||
case 0x46564552: /* "FVER" (version info, required) */
|
||||
if (fver_found) goto fail;
|
||||
if (is_aiff) goto fail; /* plain AIFF shouldn't have */
|
||||
fver_found = 1;
|
||||
|
||||
/* specific size */
|
||||
if (chunk_size != 4) goto fail;
|
||||
|
||||
if (chunk_size != 4)
|
||||
goto fail;
|
||||
/* Version 1 of AIFF-C spec timestamp */
|
||||
if ((uint32_t)read_32bitBE(current_chunk+0x08,streamFile) != 0xA2805140) goto fail;
|
||||
if (read_u32be(offset + 0x00,sf) != 0xA2805140)
|
||||
goto fail;
|
||||
break;
|
||||
|
||||
case 0x434F4D4D: /* "COMM" (main header) */
|
||||
if (comm_found) goto fail;
|
||||
comm_found = 1;
|
||||
|
||||
channel_count = read_16bitBE(current_chunk+8,streamFile);
|
||||
if (channel_count <= 0) goto fail;
|
||||
channels = read_u16be(offset + 0x00,sf);
|
||||
if (channels <= 0) goto fail;
|
||||
|
||||
sample_count = (uint32_t)read_32bitBE(current_chunk+0x0a,streamFile); /* sometimes number of blocks */
|
||||
sample_size = read_16bitBE(current_chunk+0x0e,streamFile);
|
||||
sample_rate = read80bitSANE(current_chunk+0x10,streamFile);
|
||||
sample_count = read_u32be(offset + 0x02,sf); /* sample_frames in theory, depends on codec */
|
||||
sample_size = read_u16be(offset + 0x06,sf);
|
||||
sample_rate = read_f80be(offset + 0x08,sf);
|
||||
|
||||
if (is_aifc) {
|
||||
uint32_t codec = read_32bitBE(current_chunk+0x1a,streamFile);
|
||||
uint32_t codec = read_u32be(offset + 0x12,sf);
|
||||
/* followed by "pascal string": name size + human-readable name (full count padded to even size) */
|
||||
|
||||
switch (codec) {
|
||||
case 0x53445832: /* "SDX2" [3DO games: Super Street Fighter II Turbo (3DO), etc] */
|
||||
/* "2:1 Squareroot-Delta-Exact compression" */
|
||||
coding_type = coding_SDX2;
|
||||
interleave = 0x01;
|
||||
break;
|
||||
|
||||
case 0x43424432: /* "CBD2" [M2 (arcade 3DO) games: IMSA Racing (M2), etc] */
|
||||
/* "2:1 Cuberoot-Delta-Exact compression" */
|
||||
coding_type = coding_CBD2;
|
||||
interleave = 0x01;
|
||||
break;
|
||||
|
||||
case 0x41445034: /* "ADP4" */
|
||||
coding_type = coding_DVI_IMA_int;
|
||||
if (channel_count != 1) break; /* don't know how stereo DVI is laid out */
|
||||
if (channels != 1) break; /* don't know how stereo DVI is laid out */
|
||||
break;
|
||||
|
||||
case 0x696D6134: /* "ima4" [Alida (PC), Lunar SSS (iOS)] */
|
||||
/* "IMA 4:1FLLR" */
|
||||
coding_type = coding_APPLE_IMA4;
|
||||
interleave = 0x22;
|
||||
sample_count = sample_count * ((interleave-0x2)*2);
|
||||
break;
|
||||
|
||||
case 0x434F4D50: { /* "COMP" (generic compression) */
|
||||
uint8_t comp_name[255] = {0};
|
||||
uint8_t comp_size = read_8bit(current_chunk + 0x1e, streamFile);
|
||||
if (comp_size >= sizeof(comp_name) - 1) goto fail;
|
||||
|
||||
read_streamfile(comp_name, current_chunk + 0x1f, comp_size, streamFile);
|
||||
if (memcmp(comp_name, "Relic Codec v1.6", comp_size) == 0) { /* Homeworld 2 (PC) */
|
||||
uint8_t name_size = read_u8(offset + 0x16, sf);
|
||||
|
||||
if (is_str("Relic Codec v1.6", name_size, offset + 0x17, sf)) {
|
||||
coding_type = coding_RELIC;
|
||||
sample_count = sample_count * 512;
|
||||
}
|
||||
|
@ -188,11 +209,19 @@ VGMSTREAM * init_vgmstream_aifc(STREAMFILE *streamFile) {
|
|||
break;
|
||||
}
|
||||
|
||||
case 0x56415043: { /* "VAPC" [N64 (SDK mainly but apparently may exist in ROMs)] */
|
||||
/* "VADPCM ~4-1" */
|
||||
coding_type = coding_VADPCM;
|
||||
|
||||
/* N64 tools don't create FVER, but it's required by the spec (could skip the check though) */
|
||||
fver_found = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
VGM_LOG("AIFC: unknown codec\n");
|
||||
goto fail;
|
||||
}
|
||||
/* string size and human-readable AIFF-C codec follows */
|
||||
}
|
||||
else if (is_aiff) {
|
||||
switch (sample_size) {
|
||||
|
@ -219,30 +248,51 @@ VGMSTREAM * init_vgmstream_aifc(STREAMFILE *streamFile) {
|
|||
if (data_found) goto fail;
|
||||
data_found = 1;
|
||||
|
||||
start_offset = current_chunk + 0x10 + read_32bitBE(current_chunk+0x08,streamFile);
|
||||
/* when "APCM" XA frame size is at 0x0c, fixed to 0x914 */
|
||||
/* 00: offset (for aligment, usually 0)
|
||||
* 04: block size (ex. XA: 0x914) */
|
||||
start_offset = offset + 0x08 + read_u32be(offset + 0x00,sf);
|
||||
break;
|
||||
|
||||
case 0x4D41524B: /* "MARK" (loops) */
|
||||
if (mark_found) goto fail;
|
||||
mark_found = 1;
|
||||
|
||||
mark_offset = current_chunk;
|
||||
mark_offset = offset;
|
||||
break;
|
||||
|
||||
case 0x494E5354: /* "INST" (loops) */
|
||||
if (inst_found) goto fail;
|
||||
inst_found = 1;
|
||||
inst_offset = offset;
|
||||
break;
|
||||
|
||||
inst_offset = current_chunk;
|
||||
case 0x4150504C: /* "APPL" (application specific) */
|
||||
if (is_str("stoc", 0, offset + 0x00, sf)) {
|
||||
uint8_t name_size = read_u8(offset + 0x4, sf);
|
||||
off_t next_offset = offset + 0x04 + align_size_to_block(0x1 + name_size, 0x02);
|
||||
|
||||
/* chunks appears multiple times per substring */
|
||||
if (is_str("VADPCMCODES", name_size, offset + 0x05, sf)) {
|
||||
coef_offset = next_offset;
|
||||
}
|
||||
else if (is_str("VADPCMLOOPS", name_size, offset + 0x05, sf)) {
|
||||
/* goes with inst (spec says app chunks have less priority than inst+mark loops) */
|
||||
int version = read_u16be(next_offset + 0x00, sf);
|
||||
int loops = read_u16be(next_offset + 0x02, sf);
|
||||
if (version != 1 || loops != 1) goto fail;
|
||||
|
||||
loop_start = read_u32be(next_offset + 0x04, sf);
|
||||
loop_end = read_u32be(next_offset + 0x08, sf);
|
||||
loop_flag = read_s32be(next_offset + 0x08, sf) != 0; /*-1 = infinite */
|
||||
/* 0x10: ADPCM state[16] (hists?) */
|
||||
}
|
||||
else {
|
||||
VGM_LOG("AIFC: unknown APPL chunk\n");
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
/* spec says we can skip unrecognized chunks */
|
||||
break;
|
||||
}
|
||||
|
||||
current_chunk += 0x08+chunk_size;
|
||||
offset += chunk_size;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -256,26 +306,25 @@ VGMSTREAM * init_vgmstream_aifc(STREAMFILE *streamFile) {
|
|||
|
||||
|
||||
/* read loop points */
|
||||
if (inst_found && mark_found) {
|
||||
if (inst_offset && mark_offset) {
|
||||
int start_marker;
|
||||
int end_marker;
|
||||
/* use the sustain loop */
|
||||
/* if playMode=ForwardLooping */
|
||||
if (read_16bitBE(inst_offset+16,streamFile) == 1) {
|
||||
start_marker = read_16bitBE(inst_offset+18,streamFile);
|
||||
end_marker = read_16bitBE(inst_offset+20,streamFile);
|
||||
|
||||
/* use the 'sustain loop', if playMode=ForwardLooping */
|
||||
if (read_u16be(inst_offset + 0x08,sf) == 1) {
|
||||
start_marker = read_u16be(inst_offset + 0x0a,sf);
|
||||
end_marker = read_u16be(inst_offset + 0x0c,sf);
|
||||
/* check for sustain markers != 0 (invalid marker no) */
|
||||
if (start_marker && end_marker) {
|
||||
/* find start marker */
|
||||
loop_start = find_marker(streamFile,mark_offset,start_marker);
|
||||
loop_end = find_marker(streamFile,mark_offset,end_marker);
|
||||
loop_start = find_marker(sf, mark_offset, start_marker);
|
||||
loop_end = find_marker(sf, mark_offset, end_marker);
|
||||
|
||||
/* find_marker is type uint32_t as the spec says that's the type
|
||||
* of the position value, but it returns a -1 on error, and the
|
||||
* loop_start and loop_end variables are int32_t, so the error
|
||||
* will become apparent.
|
||||
* We shouldn't have a loop point that overflows an int32_t
|
||||
* anyway. */
|
||||
* We shouldn't have a loop point that overflows an int32_t anyway. */
|
||||
loop_flag = 1;
|
||||
if (loop_start==loop_end)
|
||||
loop_flag = 0;
|
||||
|
@ -287,7 +336,7 @@ VGMSTREAM * init_vgmstream_aifc(STREAMFILE *streamFile) {
|
|||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channel_count,loop_flag);
|
||||
vgmstream = allocate_vgmstream(channels, loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
|
@ -304,18 +353,36 @@ VGMSTREAM * init_vgmstream_aifc(STREAMFILE *streamFile) {
|
|||
break;
|
||||
|
||||
case coding_RELIC: {
|
||||
int bitrate = read_16bitBE(start_offset, streamFile);
|
||||
int bitrate = read_u16be(start_offset, sf);
|
||||
start_offset += 0x02;
|
||||
|
||||
vgmstream->codec_data = init_relic(channel_count, bitrate, sample_rate);
|
||||
vgmstream->codec_data = init_relic(channels, bitrate, sample_rate);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
||||
vgmstream->sample_rate = 44100; /* fixed output */
|
||||
break;
|
||||
}
|
||||
|
||||
case coding_VADPCM:
|
||||
if (channels > 1) goto fail; /* unknown layout */
|
||||
if (coef_offset == 0) goto fail;
|
||||
|
||||
vgmstream->layout_type = layout_none;
|
||||
{
|
||||
int version = read_u16be(coef_offset + 0x00, sf);
|
||||
int order = read_u16be(coef_offset + 0x02, sf);
|
||||
int entries = read_u16be(coef_offset + 0x04, sf);
|
||||
if (version != 1) goto fail;
|
||||
|
||||
vadpcm_read_coefs_be(vgmstream, sf, coef_offset + 0x06, order, entries, 0);
|
||||
}
|
||||
|
||||
//vgmstream->num_samples = vadpcm_bytes_to_samples(data_size, channels); /* unneeded */
|
||||
break;
|
||||
|
||||
default:
|
||||
vgmstream->layout_type = (channel_count > 1) ? layout_interleave : layout_none;
|
||||
vgmstream->layout_type = (channels > 1) ? layout_interleave : layout_none;
|
||||
vgmstream->interleave_block_size = interleave;
|
||||
break;
|
||||
}
|
||||
|
@ -326,7 +393,7 @@ VGMSTREAM * init_vgmstream_aifc(STREAMFILE *streamFile) {
|
|||
vgmstream->meta_type = meta_AIFF;
|
||||
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
|
||||
if (!vgmstream_open_stream(vgmstream, sf, start_offset))
|
||||
goto fail;
|
||||
return vgmstream;
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -168,7 +168,7 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) {
|
|||
is_small = 1; //fsb5.num_samples < 20 * fsb5.sample_rate;
|
||||
|
||||
/* wrong values in some files [Pac-Man CE2 Plus (Switch) pce2p_bgm_ajurika_*.fsb] */
|
||||
ajurika_loops = fsb5.loop_start == 0x3c && fsb5.loop_end == 0x007F007F &&
|
||||
ajurika_loops = fsb5.loop_start == 0x3c && fsb5.loop_end == (0x007F007F + 1) &&
|
||||
fsb5.num_samples > fsb5.loop_end + 10000; /* arbitrary test in case some game does have those */
|
||||
|
||||
fsb5.loop_flag = 1;
|
||||
|
|
|
@ -24,7 +24,11 @@ VGMSTREAM * init_vgmstream_imc(STREAMFILE *streamFile) {
|
|||
start_offset = 0x10;
|
||||
|
||||
/* extra checks since the header is so simple */
|
||||
if (channel_count < 1 || channel_count > 8 || sample_rate < 22000 || sample_rate > 48000)
|
||||
if (channel_count < 1 || channel_count > 8)
|
||||
goto fail;
|
||||
if (sample_rate < 11025 || sample_rate > 48000)
|
||||
/* game can play 11025, 16000, 22050, 32000, 44100, 48000. Anything else will be
|
||||
silent in-game. ST10.IMC subsongs 42-47 use 22000, those are unused silent audio */
|
||||
goto fail;
|
||||
if (interleave*blocks + start_offset != file_size)
|
||||
goto fail;
|
||||
|
|
|
@ -0,0 +1,162 @@
|
|||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
|
||||
|
||||
static int is_id4(const char* test, off_t offset, STREAMFILE* sf) {
|
||||
uint8_t buf[4];
|
||||
if (read_streamfile(buf, offset, sizeof(buf), sf) != sizeof(buf))
|
||||
return 0;
|
||||
return memcmp(buf, test, sizeof(buf)) == 0; /* memcmp to allow "AB\0\0" */
|
||||
}
|
||||
|
||||
/* LucasArts iMUSE (Interactive Music Streaming Engine) formats */
|
||||
VGMSTREAM* init_vgmstream_imuse(STREAMFILE* sf) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
off_t start_offset, name_offset = 0;
|
||||
off_t head_offset, map_offset, offset;
|
||||
size_t map_size, data_bytes;
|
||||
int loop_flag, channels, sample_rate, num_samples;
|
||||
|
||||
|
||||
/* checks */
|
||||
/* .imx: The Curse of Monkey Island (PC)
|
||||
* .imc: Grim Fandango (multi)
|
||||
* .wav: Grim Fandango (multi) RIFF sfx */
|
||||
if (!check_extensions(sf, "imx,imc,wav,lwav"))
|
||||
goto fail;
|
||||
|
||||
|
||||
/* base decoder block table */
|
||||
if (is_id4("COMP", 0x00, sf)) { /* The Curse of Monkey Island (PC), The Dig (PC) */
|
||||
int entries = read_u32be(0x04,sf);
|
||||
head_offset = 0x10 + entries * 0x10 + 0x02; /* base header + table + header size */
|
||||
}
|
||||
else if (is_id4("MCMP", 0x00, sf)) { /* Grim Fandango (multi), Star Wars: X-Wing Alliance (PC) */
|
||||
int entries = read_u16be(0x04,sf);
|
||||
head_offset = 0x06 + entries * 0x09; /* base header + table */
|
||||
head_offset += 0x02 + read_u16be(head_offset, sf); /* + mini text header */
|
||||
}
|
||||
else {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
|
||||
/* "offsets" below seem to count decoded data. Data is divided into variable-sized blocks that usually
|
||||
* return 0x2000 bytes (starting from and including header). File starts with a block table to make
|
||||
* this manageable. Most offsets don't seem to match block or data boundaries so not really sure. */
|
||||
|
||||
/* main header after table */
|
||||
if (is_id4("iMUS", head_offset, sf)) { /* COMP/MCMP */
|
||||
int header_found = 0;
|
||||
|
||||
/* 0x04: decompressed size (header size + pcm bytes) */
|
||||
if (!is_id4("MAP ", head_offset + 0x08, sf))
|
||||
goto fail;
|
||||
map_size = read_u32be(head_offset + 0x0c, sf);
|
||||
map_offset = head_offset + 0x10;
|
||||
|
||||
/* MAP table (commands for interactive use) */
|
||||
offset = map_offset;
|
||||
while (offset < map_offset + map_size) {
|
||||
uint32_t type = read_u32be(offset + 0x00, sf);
|
||||
uint32_t size = read_u32be(offset + 0x04, sf);
|
||||
offset += 0x08;
|
||||
|
||||
switch(type) {
|
||||
case 0x46524D54: /* "FRMT" (header, always first) */
|
||||
if (header_found)
|
||||
goto fail;
|
||||
header_found = 1;
|
||||
/* 00: data offset */
|
||||
/* 04: 0? */
|
||||
/* 08: sample size (16b) */
|
||||
sample_rate = read_u32be(offset + 0x0c,sf);
|
||||
channels = read_u32be(offset + 0x10,sf);
|
||||
break;
|
||||
|
||||
case 0x54455854: /* "TEXT" (info) */
|
||||
/* optional info usually before some REGN: "****"=start, "loop"=loop,
|
||||
* use first TEXT as name, usually filename for music */
|
||||
/* 00: offset */
|
||||
if (!name_offset) /* */
|
||||
name_offset = offset + 0x04; /* null terminated */
|
||||
break;
|
||||
|
||||
/* - SYNC: 'lip' sync info
|
||||
* 00 offset
|
||||
* 04 sync commands until end?
|
||||
* - REGN: section config (at least one?)
|
||||
* 00 offset
|
||||
* 04 size
|
||||
* - JUMP: usually defines a loop, sometimes after a REGN
|
||||
* 00 offset (from iMUS)
|
||||
* 04 size?
|
||||
* 08 number?
|
||||
* 0c size?
|
||||
* - STOP: last command (always?)
|
||||
* 00 offset
|
||||
*/
|
||||
default: /* maybe set REGN as subsongs? + imuse_set_region(vgmstream->data, offset, size) */
|
||||
break;
|
||||
}
|
||||
offset += size;
|
||||
}
|
||||
|
||||
if (!header_found)
|
||||
goto fail;
|
||||
|
||||
if (!is_id4("DATA", head_offset + 0x10 + map_size + 0x00, sf))
|
||||
goto fail;
|
||||
data_bytes = read_u32be(head_offset + 0x10 + map_size + 0x04, sf);
|
||||
|
||||
num_samples = data_bytes / channels / sizeof(int16_t);
|
||||
//num_samples = (read_u32be(head_offset + 0x04,sf) - head_size) / channels / sizeof(int16_t); /* equivalent */
|
||||
}
|
||||
else if (is_id4("RIFF", head_offset, sf)) { /* MCMP voices */
|
||||
/* standard (LE), with fake codec 1 and sizes also in decoded bytes (see above) */
|
||||
|
||||
if (!is_id4("fmt ", head_offset + 0x0c, sf))
|
||||
goto fail;
|
||||
offset = head_offset + 0x14;
|
||||
channels = read_u16le(offset + 0x02,sf);
|
||||
sample_rate = read_u32le(offset + 0x04,sf);
|
||||
|
||||
if (!is_id4("data", head_offset + 0x24, sf))
|
||||
goto fail;
|
||||
data_bytes = read_u32le(head_offset + 0x28, sf);
|
||||
|
||||
num_samples = data_bytes / channels / sizeof(int16_t);
|
||||
}
|
||||
else {
|
||||
goto fail; /* The Dig (PC) has no header, detect? */
|
||||
}
|
||||
|
||||
loop_flag = 0;
|
||||
start_offset = 0;
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channels, loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->meta_type = meta_IMUSE;
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
vgmstream->num_samples = num_samples;
|
||||
|
||||
vgmstream->coding_type = coding_IMUSE;
|
||||
vgmstream->layout_type = layout_none;
|
||||
vgmstream->codec_data = init_imuse(sf, channels);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
|
||||
if (name_offset > 0)
|
||||
read_string(vgmstream->stream_name, STREAM_NAME_SIZE, name_offset, sf);
|
||||
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream,sf,start_offset))
|
||||
goto fail;
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
|
@ -895,4 +895,6 @@ VGMSTREAM* init_vgmstream_encrypted(STREAMFILE* sf);
|
|||
|
||||
VGMSTREAM* init_vgmstream_diva(STREAMFILE* sf);
|
||||
|
||||
VGMSTREAM* init_vgmstream_imuse(STREAMFILE* sf);
|
||||
|
||||
#endif /*_META_H*/
|
||||
|
|
|
@ -493,6 +493,7 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = {
|
|||
init_vgmstream_bkhd,
|
||||
init_vgmstream_bkhd_fx,
|
||||
init_vgmstream_diva,
|
||||
init_vgmstream_imuse,
|
||||
|
||||
/* 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 */
|
||||
|
@ -681,6 +682,10 @@ void reset_vgmstream(VGMSTREAM * vgmstream) {
|
|||
reset_ubi_adpcm(vgmstream->codec_data);
|
||||
}
|
||||
|
||||
if (vgmstream->coding_type == coding_IMUSE) {
|
||||
reset_imuse(vgmstream->codec_data);
|
||||
}
|
||||
|
||||
if (vgmstream->coding_type == coding_EA_MT) {
|
||||
reset_ea_mt(vgmstream);
|
||||
}
|
||||
|
@ -860,6 +865,11 @@ void close_vgmstream(VGMSTREAM * vgmstream) {
|
|||
vgmstream->codec_data = NULL;
|
||||
}
|
||||
|
||||
if (vgmstream->coding_type == coding_IMUSE) {
|
||||
free_imuse(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;
|
||||
|
@ -1146,6 +1156,7 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) {
|
|||
case coding_NGC_DSP_subint:
|
||||
return 14;
|
||||
case coding_NGC_AFC:
|
||||
case coding_VADPCM:
|
||||
return 16;
|
||||
case coding_NGC_DTK:
|
||||
return 28;
|
||||
|
@ -1301,6 +1312,8 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) {
|
|||
return (vgmstream->interleave_block_size - 0x05)*2 + 2;
|
||||
case coding_UBI_ADPCM:
|
||||
return 0; /* varies per mode */
|
||||
case coding_IMUSE:
|
||||
return 0; /* varies per frame */
|
||||
case coding_EA_MT:
|
||||
return 0; /* 432, but variable in looped files */
|
||||
case coding_CIRCUS_VQ:
|
||||
|
@ -1345,6 +1358,7 @@ int get_vgmstream_frame_size(VGMSTREAM * vgmstream) {
|
|||
case coding_NGC_DSP_subint:
|
||||
return 0x08 * vgmstream->channels;
|
||||
case coding_NGC_AFC:
|
||||
case coding_VADPCM:
|
||||
return 0x09;
|
||||
case coding_NGC_DTK:
|
||||
return 0x20;
|
||||
|
@ -1493,6 +1507,8 @@ int get_vgmstream_frame_size(VGMSTREAM * vgmstream) {
|
|||
return vgmstream->interleave_block_size;
|
||||
case coding_UBI_ADPCM:
|
||||
return 0; /* varies per mode? */
|
||||
case coding_IMUSE:
|
||||
return 0; /* varies per frame */
|
||||
case coding_EA_MT:
|
||||
return 0; /* variable (frames of bit counts or PCM frames) */
|
||||
#ifdef VGM_USE_ATRAC9
|
||||
|
@ -1708,6 +1724,14 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to
|
|||
vgmstream->channels,vgmstream->samples_into_block,samples_to_do);
|
||||
}
|
||||
break;
|
||||
case coding_VADPCM: {
|
||||
int order = vgmstream->codec_config;
|
||||
for (ch = 0; ch < vgmstream->channels; ch++) {
|
||||
decode_vadpcm(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch,
|
||||
vgmstream->channels,vgmstream->samples_into_block,samples_to_do, order);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case coding_PSX:
|
||||
for (ch = 0; ch < vgmstream->channels; ch++) {
|
||||
decode_psx(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch,
|
||||
|
@ -2170,6 +2194,10 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to
|
|||
decode_ubi_adpcm(vgmstream, buffer+samples_written*vgmstream->channels, samples_to_do);
|
||||
break;
|
||||
|
||||
case coding_IMUSE:
|
||||
decode_imuse(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,
|
||||
|
@ -2263,6 +2291,10 @@ int vgmstream_do_loop(VGMSTREAM * vgmstream) {
|
|||
seek_ubi_adpcm(vgmstream->codec_data, vgmstream->loop_sample);
|
||||
}
|
||||
|
||||
if (vgmstream->coding_type == coding_IMUSE) {
|
||||
seek_imuse(vgmstream->codec_data, vgmstream->loop_sample);
|
||||
}
|
||||
|
||||
if (vgmstream->coding_type == coding_EA_MT) {
|
||||
seek_ea_mt(vgmstream, vgmstream->loop_sample);
|
||||
}
|
||||
|
@ -2828,8 +2860,11 @@ int get_vgmstream_average_bitrate(VGMSTREAM * vgmstream) {
|
|||
* - opens its own streamfile from on a base one. One streamfile per channel may be open (to improve read/seeks).
|
||||
* Should be called in metas before returning the VGMSTREAM.
|
||||
*/
|
||||
int vgmstream_open_stream(VGMSTREAM * vgmstream, STREAMFILE *streamFile, off_t start_offset) {
|
||||
STREAMFILE * file = NULL;
|
||||
int vgmstream_open_stream(VGMSTREAM* vgmstream, STREAMFILE* sf, off_t start_offset) {
|
||||
return vgmstream_open_stream_bf(vgmstream, sf, start_offset, 0);
|
||||
}
|
||||
int vgmstream_open_stream_bf(VGMSTREAM* vgmstream, STREAMFILE* sf, off_t start_offset, int force_multibuffer) {
|
||||
STREAMFILE* file = NULL;
|
||||
char filename[PATH_LIMIT];
|
||||
int ch;
|
||||
int use_streamfile_per_channel = 0;
|
||||
|
@ -2921,6 +2956,11 @@ int vgmstream_open_stream(VGMSTREAM * vgmstream, STREAMFILE *streamFile, off_t s
|
|||
use_streamfile_per_channel = 1;
|
||||
}
|
||||
|
||||
/* for hard-to-detect fixed offsets or full interleave */
|
||||
if (force_multibuffer) {
|
||||
use_streamfile_per_channel = 1;
|
||||
}
|
||||
|
||||
/* for mono or codecs like IMA (XBOX, MS IMA, MS ADPCM) where channels work with the same bytes */
|
||||
if (vgmstream->layout_type == layout_none) {
|
||||
use_same_offset_per_channel = 1;
|
||||
|
@ -2932,17 +2972,16 @@ int vgmstream_open_stream(VGMSTREAM * vgmstream, STREAMFILE *streamFile, off_t s
|
|||
is_stereo_codec = 1;
|
||||
}
|
||||
|
||||
if (streamFile == NULL || start_offset < 0) {
|
||||
if (sf == NULL || start_offset < 0) {
|
||||
VGM_LOG("VGMSTREAM: buggy code (null streamfile / wrong start_offset)\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
|
||||
get_streamfile_name(streamFile,filename,sizeof(filename));
|
||||
get_streamfile_name(sf, filename, sizeof(filename));
|
||||
/* open the file for reading by each channel */
|
||||
{
|
||||
if (!use_streamfile_per_channel) {
|
||||
file = open_streamfile(streamFile,filename);
|
||||
file = open_streamfile(sf, filename);
|
||||
if (!file) goto fail;
|
||||
}
|
||||
|
||||
|
@ -2960,13 +2999,13 @@ int vgmstream_open_stream(VGMSTREAM * vgmstream, STREAMFILE *streamFile, off_t s
|
|||
/* open new one if needed, useful to avoid jumping around when each channel data is too apart
|
||||
* (don't use when data is close as it'd make buffers read the full file multiple times) */
|
||||
if (use_streamfile_per_channel) {
|
||||
file = open_streamfile(streamFile,filename);
|
||||
file = open_streamfile(sf,filename);
|
||||
if (!file) goto fail;
|
||||
}
|
||||
|
||||
vgmstream->ch[ch].streamfile = file;
|
||||
vgmstream->ch[ch].channel_start_offset =
|
||||
vgmstream->ch[ch].offset = offset;
|
||||
vgmstream->ch[ch].channel_start_offset = offset;
|
||||
vgmstream->ch[ch].offset = offset;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -104,6 +104,7 @@ typedef enum {
|
|||
coding_NGC_DSP_subint, /* Nintendo DSP ADPCM with frame subinterframe */
|
||||
coding_NGC_DTK, /* Nintendo DTK ADPCM (hardware disc), also called TRK or ADP */
|
||||
coding_NGC_AFC, /* Nintendo AFC ADPCM */
|
||||
coding_VADPCM, /* Silicon Graphics VADPCM */
|
||||
|
||||
coding_G721, /* CCITT G.721 */
|
||||
|
||||
|
@ -176,6 +177,7 @@ typedef enum {
|
|||
coding_OKI16, /* OKI 4-bit ADPCM with 16-bit output and modified expand */
|
||||
coding_OKI4S, /* OKI 4-bit ADPCM with 16-bit output and cuadruple step */
|
||||
coding_PTADPCM, /* Platinum 4-bit ADPCM */
|
||||
coding_IMUSE, /* LucasArts iMUSE Variable ADPCM */
|
||||
|
||||
/* others */
|
||||
coding_SDX2, /* SDX2 2:1 Squareroot-Delta-Exact compression DPCM */
|
||||
|
@ -736,6 +738,7 @@ typedef enum {
|
|||
meta_LRMD,
|
||||
meta_WWISE_FX,
|
||||
meta_DIVA,
|
||||
meta_IMUSE,
|
||||
} meta_t;
|
||||
|
||||
/* standard WAVEFORMATEXTENSIBLE speaker positions */
|
||||
|
@ -793,14 +796,15 @@ typedef struct {
|
|||
/* format specific */
|
||||
|
||||
/* adpcm */
|
||||
int16_t adpcm_coef[16]; /* for formats with decode coefficients built in */
|
||||
int32_t adpcm_coef_3by32[0x60]; /* for Level-5 0x555 */
|
||||
int16_t adpcm_coef[16]; /* formats with decode coefficients built in (DSP, some ADX) */
|
||||
int32_t adpcm_coef_3by32[0x60]; /* Level-5 0x555 */
|
||||
int16_t vadpcm_coefs[8*2*8]; /* VADPCM: max 8 groups * max 2 order * fixed 8 subframe coefs */
|
||||
union {
|
||||
int16_t adpcm_history1_16; /* previous sample */
|
||||
int16_t adpcm_history1_16; /* previous sample */
|
||||
int32_t adpcm_history1_32;
|
||||
};
|
||||
union {
|
||||
int16_t adpcm_history2_16; /* previous previous sample */
|
||||
int16_t adpcm_history2_16; /* previous previous sample */
|
||||
int32_t adpcm_history2_32;
|
||||
};
|
||||
union {
|
||||
|
@ -815,8 +819,8 @@ typedef struct {
|
|||
double adpcm_history1_double;
|
||||
double adpcm_history2_double;
|
||||
|
||||
int adpcm_step_index; /* for IMA */
|
||||
int adpcm_scale; /* for MS ADPCM */
|
||||
int adpcm_step_index; /* for IMA */
|
||||
int adpcm_scale; /* for MS ADPCM */
|
||||
|
||||
/* state for G.721 decoder, sort of big but we might as well keep it around */
|
||||
struct g72x_state g72x_state;
|
||||
|
@ -1379,6 +1383,7 @@ int vgmstream_do_loop(VGMSTREAM * vgmstream);
|
|||
/* Open the stream for reading at offset (taking into account layouts, channels and so on).
|
||||
* Returns 0 on failure */
|
||||
int vgmstream_open_stream(VGMSTREAM * vgmstream, STREAMFILE *streamFile, off_t start_offset);
|
||||
int vgmstream_open_stream_bf(VGMSTREAM * vgmstream, STREAMFILE *streamFile, off_t start_offset, int force_multibuffer);
|
||||
|
||||
/* Get description info */
|
||||
void get_vgmstream_coding_description(VGMSTREAM *vgmstream, char *out, size_t out_size);
|
||||
|
|
Loading…
Reference in New Issue