diff --git a/Frameworks/vgmstream/libvgmstream.xcodeproj/project.pbxproj b/Frameworks/vgmstream/libvgmstream.xcodeproj/project.pbxproj index 6a00d9b7c..6c57c4d87 100644 --- a/Frameworks/vgmstream/libvgmstream.xcodeproj/project.pbxproj +++ b/Frameworks/vgmstream/libvgmstream.xcodeproj/project.pbxproj @@ -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 = ""; }; 83F0AA5D21E2028B004BBC04 /* vsv_streamfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = vsv_streamfile.h; sourceTree = ""; }; 83F0AA5E21E2028C004BBC04 /* vsv.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = vsv.c; sourceTree = ""; }; + 83F1EE28245D4FB10076E182 /* imuse_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = imuse_decoder.c; sourceTree = ""; }; + 83F1EE2C245D4FB20076E182 /* vadpcm_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = vadpcm_decoder.c; sourceTree = ""; }; + 83F1EE2F245D4FC10076E182 /* imuse.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = imuse.c; sourceTree = ""; }; 83F412871E932F9A002E37D0 /* Vorbis.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Vorbis.xcodeproj; path = ../Vorbis/macosx/Vorbis.xcodeproj; sourceTree = ""; }; 83F5F8821908D0A400C8E65F /* fsb5.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = fsb5.c; sourceTree = ""; }; 83FBD502235D31F700D35BCD /* riff_ogg_streamfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = riff_ogg_streamfile.h; sourceTree = ""; }; @@ -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 */, diff --git a/Frameworks/vgmstream/vgmstream/src/coding/coding.h b/Frameworks/vgmstream/vgmstream/src/coding/coding.h index a2158fc74..7367d292e 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/coding.h +++ b/Frameworks/vgmstream/vgmstream/src/coding/coding.h @@ -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); diff --git a/Frameworks/vgmstream/vgmstream/src/coding/imuse_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/imuse_decoder.c new file mode 100644 index 000000000..ab88416b7 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/coding/imuse_decoder.c @@ -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; +} diff --git a/Frameworks/vgmstream/vgmstream/src/coding/vadpcm_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/vadpcm_decoder.c new file mode 100644 index 000000000..d2f59ad87 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/coding/vadpcm_decoder.c @@ -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; +} diff --git a/Frameworks/vgmstream/vgmstream/src/formats.c b/Frameworks/vgmstream/vgmstream/src/formats.c index 4e60da24a..f924c7e98 100644 --- a/Frameworks/vgmstream/vgmstream/src/formats.c +++ b/Frameworks/vgmstream/vgmstream/src/formats.c @@ -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"}, }; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/aifc.c b/Frameworks/vgmstream/vgmstream/src/meta/aifc.c index 58546894c..08a3b6e90 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/aifc.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/aifc.c @@ -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 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; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ea_schl.c b/Frameworks/vgmstream/vgmstream/src/meta/ea_schl.c index 261f0a7b2..6e3411a7f 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ea_schl.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ea_schl.c @@ -32,7 +32,8 @@ #define EA_CODEC1_VAG 0x01 // unsure #define EA_CODEC1_EAXA 0x07 #define EA_CODEC1_MT10 0x09 -//#define EA_CODEC1_N64 ? +#define EA_CODEC1_N64 0x64 /* unknown but probably before MT10 */ + #define EA_CODEC2_NONE -1 #define EA_CODEC2_MT10 0x04 @@ -48,6 +49,7 @@ #define EA_CODEC2_MT5 0x16 #define EA_CODEC2_EALAYER3 0x17 #define EA_CODEC2_ATRAC3PLUS 0x1B +#define EA_CODEC2_N64 0x64 /* unknown but probably before MT10 */ /* Block headers, SCxy - where x is block ID and y is endianness flag (always 'l'?) */ #define EA_BLOCKID_HEADER 0x5343486C /* "SCHl" */ @@ -94,6 +96,8 @@ typedef struct { int32_t loop_start; int32_t loop_end; + uint32_t flag_value; + off_t offsets[EA_MAX_CHANNELS]; off_t coefs[EA_MAX_CHANNELS]; off_t loops[EA_MAX_CHANNELS]; @@ -105,16 +109,16 @@ typedef struct { size_t stream_size; } ea_header; -static VGMSTREAM * parse_schl_block(STREAMFILE *streamFile, off_t offset, int standalone); -static VGMSTREAM * parse_bnk_header(STREAMFILE *streamFile, off_t offset, int target_stream, int is_embedded); -static int parse_variable_header(STREAMFILE* streamFile, ea_header* ea, off_t begin_offset, int max_length, int bnk_version); -static uint32_t read_patch(STREAMFILE* streamFile, off_t* offset); -static off_t get_ea_stream_mpeg_start_offset(STREAMFILE* streamFile, off_t start_offset, const ea_header* ea); -static VGMSTREAM * init_vgmstream_ea_variable_header(STREAMFILE *streamFile, ea_header *ea, off_t start_offset, int is_bnk, int standalone); -static void update_ea_stream_size_and_samples(STREAMFILE* streamFile, off_t start_offset, VGMSTREAM* vgmstream, int standalone); +static VGMSTREAM * parse_schl_block(STREAMFILE* sf, off_t offset, int standalone); +static VGMSTREAM * parse_bnk_header(STREAMFILE* sf, off_t offset, int target_stream, int is_embedded); +static int parse_variable_header(STREAMFILE* sf, ea_header* ea, off_t begin_offset, int max_length, int bnk_version); +static uint32_t read_patch(STREAMFILE* sf, off_t* offset); +static off_t get_ea_stream_mpeg_start_offset(STREAMFILE* sf, off_t start_offset, const ea_header* ea); +static VGMSTREAM * init_vgmstream_ea_variable_header(STREAMFILE* sf, ea_header* ea, off_t start_offset, int is_bnk, int standalone); +static void update_ea_stream_size_and_samples(STREAMFILE* sf, off_t start_offset, VGMSTREAM* vgmstream, int standalone); /* EA SCHl with variable header - from EA games (roughly 1997~2010); generated by EA Canada's sx.exe/Sound eXchange */ -VGMSTREAM * init_vgmstream_ea_schl(STREAMFILE *streamFile) { +VGMSTREAM* init_vgmstream_ea_schl(STREAMFILE* sf) { /* check extension */ /* they don't seem enforced by EA's tools but usually: @@ -134,41 +138,41 @@ VGMSTREAM * init_vgmstream_ea_schl(STREAMFILE *streamFile) { * .gsf: 007 - Everything or Nothing (GC) * .mus: map/mpf+mus only? * (extensionless): SSX (PS2) (inside .big) */ - if (!check_extensions(streamFile,"asf,lasf,str,eam,exa,sng,aud,sx,xa,strm,stm,hab,xsf,gsf,mus,")) + if (!check_extensions(sf,"asf,lasf,str,eam,exa,sng,aud,sx,xa,strm,stm,hab,xsf,gsf,mus,")) goto fail; /* check header */ - if (read_32bitBE(0x00,streamFile) != EA_BLOCKID_HEADER && /* "SCHl" */ - read_32bitBE(0x00,streamFile) != (EA_BLOCKID_LOC_HEADER | EA_BLOCKID_LOC_EN) && /* "SHEN" */ - read_32bitBE(0x00,streamFile) != (EA_BLOCKID_LOC_HEADER | EA_BLOCKID_LOC_FR) && /* "SHFR" */ - read_32bitBE(0x00,streamFile) != (EA_BLOCKID_LOC_HEADER | EA_BLOCKID_LOC_GE) && /* "SHGE" */ - read_32bitBE(0x00,streamFile) != (EA_BLOCKID_LOC_HEADER | EA_BLOCKID_LOC_DE) && /* "SHDE" */ - read_32bitBE(0x00,streamFile) != (EA_BLOCKID_LOC_HEADER | EA_BLOCKID_LOC_IT) && /* "SHIT" */ - read_32bitBE(0x00,streamFile) != (EA_BLOCKID_LOC_HEADER | EA_BLOCKID_LOC_SP) && /* "SHSP" */ - read_32bitBE(0x00,streamFile) != (EA_BLOCKID_LOC_HEADER | EA_BLOCKID_LOC_ES) && /* "SHES" */ - read_32bitBE(0x00,streamFile) != (EA_BLOCKID_LOC_HEADER | EA_BLOCKID_LOC_MX) && /* "SHMX" */ - read_32bitBE(0x00,streamFile) != (EA_BLOCKID_LOC_HEADER | EA_BLOCKID_LOC_RU) && /* "SHRU" */ - read_32bitBE(0x00,streamFile) != (EA_BLOCKID_LOC_HEADER | EA_BLOCKID_LOC_JA) && /* "SHJA" */ - read_32bitBE(0x00,streamFile) != (EA_BLOCKID_LOC_HEADER | EA_BLOCKID_LOC_JP) && /* "SHJP" */ - read_32bitBE(0x00,streamFile) != (EA_BLOCKID_LOC_HEADER | EA_BLOCKID_LOC_PL) && /* "SHPL" */ - read_32bitBE(0x00,streamFile) != (EA_BLOCKID_LOC_HEADER | EA_BLOCKID_LOC_BR)) /* "SHBR" */ + if (read_32bitBE(0x00,sf) != EA_BLOCKID_HEADER && /* "SCHl" */ + read_32bitBE(0x00,sf) != (EA_BLOCKID_LOC_HEADER | EA_BLOCKID_LOC_EN) && /* "SHEN" */ + read_32bitBE(0x00,sf) != (EA_BLOCKID_LOC_HEADER | EA_BLOCKID_LOC_FR) && /* "SHFR" */ + read_32bitBE(0x00,sf) != (EA_BLOCKID_LOC_HEADER | EA_BLOCKID_LOC_GE) && /* "SHGE" */ + read_32bitBE(0x00,sf) != (EA_BLOCKID_LOC_HEADER | EA_BLOCKID_LOC_DE) && /* "SHDE" */ + read_32bitBE(0x00,sf) != (EA_BLOCKID_LOC_HEADER | EA_BLOCKID_LOC_IT) && /* "SHIT" */ + read_32bitBE(0x00,sf) != (EA_BLOCKID_LOC_HEADER | EA_BLOCKID_LOC_SP) && /* "SHSP" */ + read_32bitBE(0x00,sf) != (EA_BLOCKID_LOC_HEADER | EA_BLOCKID_LOC_ES) && /* "SHES" */ + read_32bitBE(0x00,sf) != (EA_BLOCKID_LOC_HEADER | EA_BLOCKID_LOC_MX) && /* "SHMX" */ + read_32bitBE(0x00,sf) != (EA_BLOCKID_LOC_HEADER | EA_BLOCKID_LOC_RU) && /* "SHRU" */ + read_32bitBE(0x00,sf) != (EA_BLOCKID_LOC_HEADER | EA_BLOCKID_LOC_JA) && /* "SHJA" */ + read_32bitBE(0x00,sf) != (EA_BLOCKID_LOC_HEADER | EA_BLOCKID_LOC_JP) && /* "SHJP" */ + read_32bitBE(0x00,sf) != (EA_BLOCKID_LOC_HEADER | EA_BLOCKID_LOC_PL) && /* "SHPL" */ + read_32bitBE(0x00,sf) != (EA_BLOCKID_LOC_HEADER | EA_BLOCKID_LOC_BR)) /* "SHBR" */ goto fail; /* Stream is divided into blocks/chunks: SCHl=audio header, SCCl=count of SCDl, SCDl=data xN, SCLl=loop end, SCEl=end. * Video uses picture blocks (MVhd/MV0K/etc) and sometimes multiaudio blocks (SHxx/SCxx/SDxx/SExx where xx=language). * The number/size is affected by: block rate setting, sample rate, channels, CPU location (SPU/main/DSP/others), etc */ - return parse_schl_block(streamFile, 0x00, 1); + return parse_schl_block(sf, 0x00, 1); fail: return NULL; } /* EA SCHl inside non-demuxed videos, used in current gen games too */ -VGMSTREAM * init_vgmstream_ea_schl_video(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; +VGMSTREAM * init_vgmstream_ea_schl_video(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; off_t offset = 0, start_offset = 0; int blocks_done = 0; - int total_subsongs, target_subsong = streamFile->stream_index; + int total_subsongs, target_subsong = sf->stream_index; int32_t(*read_32bit)(off_t, STREAMFILE*); @@ -177,33 +181,33 @@ VGMSTREAM * init_vgmstream_ea_schl_video(STREAMFILE *streamFile) { /* .dct: early-mid [ex. Need for Speed II SE (PC), FIFA 98 (PC)] */ /* .mad: mid */ /* .vp6: late */ - if (check_extensions(streamFile, "uv,dct")) { + if (check_extensions(sf, "uv,dct")) { /* starts with audio header block */ - if (read_32bitBE(0x00, streamFile) != EA_BLOCKID_HEADER) /* "SCHl" */ + if (read_32bitBE(0x00, sf) != EA_BLOCKID_HEADER) /* "SCHl" */ goto fail; - } else if (check_extensions(streamFile, "mad")) { + } else if (check_extensions(sf, "mad")) { /* check initial movie block id */ - if (read_32bitBE(0x00, streamFile) != 0x4D41446B) /* "MADk" */ + if (read_32bitBE(0x00, sf) != 0x4D41446B) /* "MADk" */ goto fail; - } else if (check_extensions(streamFile, "vp6")) { + } else if (check_extensions(sf, "vp6")) { /* check initial movie block id */ - if (read_32bitBE(0x00, streamFile) != 0x4D566864) /* "MVhd" */ + if (read_32bitBE(0x00, sf) != 0x4D566864) /* "MVhd" */ goto fail; } else { goto fail; } /* use block size to check endianness */ - if (guess_endianness32bit(0x04, streamFile)) { + if (guess_endianness32bit(0x04, sf)) { read_32bit = read_32bitBE; } else { read_32bit = read_32bitLE; } /* find starting valid header for the parser */ - while (offset < get_streamfile_size(streamFile)) { - uint32_t block_id = read_32bitBE(offset+0x00,streamFile); - uint32_t block_size = read_32bit (offset+0x04,streamFile); + while (offset < get_streamfile_size(sf)) { + uint32_t block_id = read_32bitBE(offset+0x00,sf); + uint32_t block_size = read_32bit (offset+0x04,sf); /* find "SCHl" or "SHxx" blocks */ if ((block_id == EA_BLOCKID_HEADER) || ((block_id & 0xFFFF0000) == EA_BLOCKID_LOC_HEADER)) { @@ -220,16 +224,16 @@ VGMSTREAM * init_vgmstream_ea_schl_video(STREAMFILE *streamFile) { offset += block_size; } - if (offset >= get_streamfile_size(streamFile)) + if (offset >= get_streamfile_size(sf)) goto fail; /* find target subsong (one per each SHxx multilang block) */ total_subsongs = 1; if (target_subsong == 0) target_subsong = 1; offset = start_offset; - while (offset < get_streamfile_size(streamFile)) { - uint32_t block_id = read_32bitBE(offset+0x00,streamFile); - uint32_t block_size = read_32bit (offset+0x04,streamFile); + while (offset < get_streamfile_size(sf)) { + uint32_t block_id = read_32bitBE(offset+0x00,sf); + uint32_t block_size = read_32bit (offset+0x04,sf); /* no more subsongs (assumes all SHxx headers go together) */ if (((block_id & 0xFFFF0000) != EA_BLOCKID_LOC_HEADER)) { @@ -247,7 +251,7 @@ VGMSTREAM * init_vgmstream_ea_schl_video(STREAMFILE *streamFile) { if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail; - vgmstream = parse_schl_block(streamFile, start_offset, 1); + vgmstream = parse_schl_block(sf, start_offset, 1); if (!vgmstream) goto fail; vgmstream->num_streams = total_subsongs; @@ -259,9 +263,9 @@ fail: } /* EA BNK with variable header - from EA games SFXs; also created by sx.exe */ -VGMSTREAM * init_vgmstream_ea_bnk(STREAMFILE *streamFile) { +VGMSTREAM * init_vgmstream_ea_bnk(STREAMFILE* sf) { off_t offset; - int target_stream = streamFile->stream_index; + int target_stream = sf->stream_index; /* check extension */ /* .bnk: common @@ -269,18 +273,18 @@ VGMSTREAM * init_vgmstream_ea_bnk(STREAMFILE *streamFile) { * .mus: streams/jingles (rare) * .abk: GoldenEye - Rogue Agent * .ast: FIFA 2004 (inside .big) */ - if (!check_extensions(streamFile,"bnk,sdt,mus,abk,ast")) + if (!check_extensions(sf,"bnk,sdt,mus,abk,ast")) goto fail; /* check header (doesn't use EA blocks, otherwise very similar to SCHl) */ - if (read_32bitBE(0x100,streamFile) == EA_BNK_HEADER_LE) + if (read_32bitBE(0x100,sf) == EA_BNK_HEADER_LE) offset = 0x100; /* Harry Potter and the Goblet of Fire (PS2) .mus have weird extra 0x100 bytes */ else offset = 0x00; if (target_stream == 0) target_stream = 1; - return parse_bnk_header(streamFile, offset, target_stream - 1, 0); + return parse_bnk_header(sf, offset, target_stream - 1, 0); fail: return NULL; @@ -289,8 +293,8 @@ fail: /* EA ABK - common soundbank format in 6th-gen games, can reference RAM and streamed assets */ /* RAM assets are stored in embedded BNK file */ /* streamed assets are stored externally in AST file (mostly seen in earlier 6th-gen games) */ -VGMSTREAM * init_vgmstream_ea_abk(STREAMFILE *streamFile) { - int bnk_target_stream, is_dupe, total_sounds = 0, target_stream = streamFile->stream_index; +VGMSTREAM * init_vgmstream_ea_abk(STREAMFILE* sf) { + int bnk_target_stream, is_dupe, total_sounds = 0, target_stream = sf->stream_index; off_t bnk_offset, header_table_offset, base_offset, value_offset, table_offset, entry_offset, target_entry_offset, schl_offset, schl_loop_offset; uint32_t i, j, k, num_sounds, total_sound_tables; uint16_t num_tables; @@ -303,14 +307,14 @@ VGMSTREAM * init_vgmstream_ea_abk(STREAMFILE *streamFile) { int16_t(*read_16bit)(off_t, STREAMFILE*); /* check extension */ - if (!check_extensions(streamFile, "abk")) + if (!check_extensions(sf, "abk")) goto fail; - if (read_32bitBE(0x00, streamFile) != 0x41424B43) /* "ABKC" */ + if (read_32bitBE(0x00, sf) != 0x41424B43) /* "ABKC" */ goto fail; /* use table offset to check endianness */ - if (guess_endianness32bit(0x1C, streamFile)) { + if (guess_endianness32bit(0x1C, sf)) { read_32bit = read_32bitBE; read_16bit = read_16bitBE; } else { @@ -322,26 +326,26 @@ VGMSTREAM * init_vgmstream_ea_abk(STREAMFILE *streamFile) { if (target_stream < 0) goto fail; - num_tables = read_16bit(0x0A, streamFile); - header_table_offset = read_32bit(0x1C, streamFile); - bnk_offset = read_32bit(0x20, streamFile); + num_tables = read_16bit(0x0A, sf); + header_table_offset = read_32bit(0x1C, sf); + bnk_offset = read_32bit(0x20, sf); target_entry_offset = 0; total_sound_tables = 0; /* check to avoid clashing with the newer ABK format */ if (bnk_offset && - read_32bitBE(bnk_offset, streamFile) != EA_BNK_HEADER_LE && - read_32bitBE(bnk_offset, streamFile) != EA_BNK_HEADER_BE) + read_32bitBE(bnk_offset, sf) != EA_BNK_HEADER_LE && + read_32bitBE(bnk_offset, sf) != EA_BNK_HEADER_BE) goto fail; for (i = 0; i < num_tables; i++) { - num_entries = read_8bit(header_table_offset + 0x24, streamFile); - base_offset = read_32bit(header_table_offset + 0x2C, streamFile); + num_entries = read_8bit(header_table_offset + 0x24, sf); + base_offset = read_32bit(header_table_offset + 0x2C, sf); if (num_entries == 0xff) goto fail; /* EOF read */ for (j = 0; j < num_entries; j++) { - value_offset = read_32bit(header_table_offset + 0x3C + 0x04 * j, streamFile); - table_offset = read_32bit(base_offset + value_offset + 0x04, streamFile); + value_offset = read_32bit(header_table_offset + 0x3C + 0x04 * j, sf); + table_offset = read_32bit(base_offset + value_offset + 0x04, sf); /* For some reason, there are duplicate entries pointing at the same sound tables */ is_dupe = 0; @@ -356,15 +360,15 @@ VGMSTREAM * init_vgmstream_ea_abk(STREAMFILE *streamFile) { continue; sound_table_offsets[total_sound_tables++] = table_offset; - num_sounds = read_32bit(table_offset, streamFile); + num_sounds = read_32bit(table_offset, sf); if (num_sounds == 0xffffffff) goto fail; /* EOF read */ for (k = 0; k < num_sounds; k++) { entry_offset = table_offset + 0x04 + 0x0C * k; - sound_type = read_8bit(entry_offset + 0x00, streamFile); + sound_type = read_8bit(entry_offset + 0x00, sf); /* some of these are dummies pointing at sound 0 in BNK */ - if (sound_type == 0x00 && read_32bit(entry_offset + 0x04, streamFile) == 0) + if (sound_type == 0x00 && read_32bit(entry_offset + 0x04, sf) == 0) continue; total_sounds++; @@ -374,7 +378,7 @@ VGMSTREAM * init_vgmstream_ea_abk(STREAMFILE *streamFile) { } /* there can be another set of values, don't know what they mean */ - num_entries += read_8bit(header_table_offset + 0x27, streamFile); + num_entries += read_8bit(header_table_offset + 0x27, sf); header_table_offset += 0x3C + num_entries * 0x04; } @@ -385,26 +389,26 @@ VGMSTREAM * init_vgmstream_ea_abk(STREAMFILE *streamFile) { /* 0x01: ??? */ /* 0x04: index for normal sounds, offset for streamed sounds */ /* 0x08: loop offset for streamed sounds */ - sound_type = read_8bit(target_entry_offset + 0x00, streamFile); + sound_type = read_8bit(target_entry_offset + 0x00, sf); switch (sound_type) { case 0x00: if (!bnk_offset) goto fail; - bnk_target_stream = read_32bit(target_entry_offset + 0x04, streamFile); - vgmstream = parse_bnk_header(streamFile, bnk_offset, bnk_target_stream, 1); + bnk_target_stream = read_32bit(target_entry_offset + 0x04, sf); + vgmstream = parse_bnk_header(sf, bnk_offset, bnk_target_stream, 1); if (!vgmstream) goto fail; break; case 0x01: - astData = open_streamfile_by_ext(streamFile, "ast"); + astData = open_streamfile_by_ext(sf, "ast"); if (!astData) goto fail; - schl_offset = read_32bit(target_entry_offset + 0x04, streamFile); + schl_offset = read_32bit(target_entry_offset + 0x04, sf); if (read_32bitBE(schl_offset, astData) != EA_BLOCKID_HEADER) goto fail; @@ -415,14 +419,14 @@ VGMSTREAM * init_vgmstream_ea_abk(STREAMFILE *streamFile) { break; case 0x02: - astData = open_streamfile_by_ext(streamFile, "ast"); + astData = open_streamfile_by_ext(sf, "ast"); if (!astData) goto fail; /* looped sounds basically consist of two independent segments * the first one is loop start, the second one is loop body */ - schl_offset = read_32bit(target_entry_offset + 0x04, streamFile); - schl_loop_offset = read_32bit(target_entry_offset + 0x08, streamFile); + schl_offset = read_32bit(target_entry_offset + 0x04, sf); + schl_loop_offset = read_32bit(target_entry_offset + 0x08, sf); if (read_32bitBE(schl_offset, astData) != EA_BLOCKID_HEADER || read_32bitBE(schl_loop_offset, astData) != EA_BLOCKID_HEADER) @@ -473,23 +477,23 @@ fail: } /* EA HDR/DAT v1 (2004-2005) - used for storing speech and other streamed sounds (except for music) */ -VGMSTREAM * init_vgmstream_ea_hdr_dat(STREAMFILE *streamFile) { - int target_stream = streamFile->stream_index; +VGMSTREAM * init_vgmstream_ea_hdr_dat(STREAMFILE* sf) { + VGMSTREAM* vgmstream; + STREAMFILE* sf_dat = NULL; + int target_stream = sf->stream_index; uint32_t offset_mult; uint8_t userdata_size, total_sounds; size_t dat_size; off_t schl_offset; - STREAMFILE *datFile = NULL; - VGMSTREAM *vgmstream; /* checks */ - if (!check_extensions(streamFile, "hdr")) + if (!check_extensions(sf, "hdr")) goto fail; /* main header is machine endian but it's not important here */ /* 0x00: ID */ /* 0x02: sub-ID (used for different police voices in NFS games) */ - /* 0x04: parameters (userdata size, ...) + /* 0x04: parameters (userdata size, ...) */ /* 0x05: number of files */ /* 0x06: alt number of files? */ /* 0x07: offset multiplier flag */ @@ -498,31 +502,31 @@ VGMSTREAM * init_vgmstream_ea_hdr_dat(STREAMFILE *streamFile) { /* 0x0c: table start */ /* no nice way to validate these so we do what we can */ - if (read_u16be(0x0a, streamFile) != 0) + if (read_u16be(0x0a, sf) != 0) goto fail; /* first offset is always zero */ - if (read_u16be(0x0c, streamFile) != 0) + if (read_u16be(0x0c, sf) != 0) goto fail; /* must be accompanied by DAT file with SCHl sounds */ - datFile = open_streamfile_by_ext(streamFile, "dat"); - if (!datFile) + sf_dat = open_streamfile_by_ext(sf, "dat"); + if (!sf_dat) goto fail; - if (read_u32be(0x00, datFile) != EA_BLOCKID_HEADER) + if (read_u32be(0x00, sf_dat) != EA_BLOCKID_HEADER) goto fail; - userdata_size = read_u8(0x04, streamFile) & 0x0F; - total_sounds = read_u8(0x05, streamFile); - offset_mult = read_u8(0x07, streamFile) * 0x0100 + 0x0100; + userdata_size = read_u8(0x04, sf) & 0x0F; + total_sounds = read_u8(0x05, sf); + offset_mult = read_u8(0x07, sf) * 0x0100 + 0x0100; - if (read_u8(0x06, streamFile) > total_sounds) + if (read_u8(0x06, sf) > total_sounds) goto fail; - dat_size = get_streamfile_size(datFile); - if (read_u16le(0x08, streamFile) * offset_mult > dat_size && - read_u16be(0x08, streamFile) * offset_mult > dat_size) + dat_size = get_streamfile_size(sf_dat); + if (read_u16le(0x08, sf) * offset_mult > dat_size && + read_u16be(0x08, sf) * offset_mult > dat_size) goto fail; if (target_stream == 0) target_stream = 1; @@ -530,35 +534,35 @@ VGMSTREAM * init_vgmstream_ea_hdr_dat(STREAMFILE *streamFile) { goto fail; /* offsets are always big endian */ - schl_offset = read_u16be(0x0C + (0x02 + userdata_size) * (target_stream - 1), streamFile) * offset_mult; - if (read_32bitBE(schl_offset, datFile) != EA_BLOCKID_HEADER) + schl_offset = read_u16be(0x0C + (0x02 + userdata_size) * (target_stream - 1), sf) * offset_mult; + if (read_32bitBE(schl_offset, sf_dat) != EA_BLOCKID_HEADER) goto fail; - vgmstream = parse_schl_block(datFile, schl_offset, 0); + vgmstream = parse_schl_block(sf_dat, schl_offset, 0); if (!vgmstream) goto fail; vgmstream->num_streams = total_sounds; - close_streamfile(datFile); + close_streamfile(sf_dat); return vgmstream; fail: - close_streamfile(datFile); + close_streamfile(sf_dat); return NULL; } /* EA HDR/DAT v2 (2006-2014) */ -VGMSTREAM * init_vgmstream_ea_hdr_dat_v2(STREAMFILE *streamFile) { - int target_stream = streamFile->stream_index; +VGMSTREAM * init_vgmstream_ea_hdr_dat_v2(STREAMFILE* sf) { + VGMSTREAM *vgmstream; + STREAMFILE *sf_dat = NULL; + int target_stream = sf->stream_index; uint32_t offset_mult; uint8_t userdata_size, total_sounds; size_t dat_size; off_t schl_offset; - STREAMFILE *datFile = NULL; - VGMSTREAM *vgmstream; /* checks */ - if (!check_extensions(streamFile, "hdr")) + if (!check_extensions(sf, "hdr")) goto fail; /* main header is machine endian but it's not important here */ @@ -573,31 +577,31 @@ VGMSTREAM * init_vgmstream_ea_hdr_dat_v2(STREAMFILE *streamFile) { /* 0x10: table start */ /* no nice way to validate these so we do what we can */ - if (read_u32be(0x0c, streamFile) != 0) + if (read_u32be(0x0c, sf) != 0) goto fail; /* first offset is always zero */ - if (read_u16be(0x10, streamFile) != 0) + if (read_u16be(0x10, sf) != 0) goto fail; /* must be accompanied by DAT file with SCHl sounds */ - datFile = open_streamfile_by_ext(streamFile, "dat"); - if (!datFile) + sf_dat = open_streamfile_by_ext(sf, "dat"); + if (!sf_dat) goto fail; - if (read_u32be(0x00, datFile) != EA_BLOCKID_HEADER) + if (read_u32be(0x00, sf_dat) != EA_BLOCKID_HEADER) goto fail; - userdata_size = read_u8(0x02, streamFile) & 0x0F; - total_sounds = read_u8(0x03, streamFile); - offset_mult = read_u8(0x09, streamFile) * 0x0100 + 0x0100; + userdata_size = read_u8(0x02, sf) & 0x0F; + total_sounds = read_u8(0x03, sf); + offset_mult = read_u8(0x09, sf) * 0x0100 + 0x0100; - if (read_u8(0x08, streamFile) > total_sounds) + if (read_u8(0x08, sf) > total_sounds) goto fail; - dat_size = get_streamfile_size(datFile); - if (read_u16le(0x0a, streamFile) * offset_mult > dat_size && - read_u16be(0x0a, streamFile) * offset_mult > dat_size) + dat_size = get_streamfile_size(sf_dat); + if (read_u16le(0x0a, sf) * offset_mult > dat_size && + read_u16be(0x0a, sf) * offset_mult > dat_size) goto fail; if (target_stream == 0) target_stream = 1; @@ -605,26 +609,26 @@ VGMSTREAM * init_vgmstream_ea_hdr_dat_v2(STREAMFILE *streamFile) { goto fail; /* offsets are always big endian */ - schl_offset = read_u16be(0x10 + (0x02 + userdata_size) * (target_stream - 1), streamFile) * offset_mult; - if (read_32bitBE(schl_offset, datFile) != EA_BLOCKID_HEADER) + schl_offset = read_u16be(0x10 + (0x02 + userdata_size) * (target_stream - 1), sf) * offset_mult; + if (read_32bitBE(schl_offset, sf_dat) != EA_BLOCKID_HEADER) goto fail; - vgmstream = parse_schl_block(datFile, schl_offset, 0); + vgmstream = parse_schl_block(sf_dat, schl_offset, 0); if (!vgmstream) goto fail; vgmstream->num_streams = total_sounds; - close_streamfile(datFile); + close_streamfile(sf_dat); return vgmstream; fail: - close_streamfile(datFile); + close_streamfile(sf_dat); return NULL; } /* open map/mpf+mus pairs that aren't exact pairs, since EA's games can load any combo */ -static STREAMFILE* open_mapfile_pair(STREAMFILE *streamFile, int track, int num_tracks) { +static STREAMFILE* open_mapfile_pair(STREAMFILE* sf, int track, int num_tracks) { static const char *const mapfile_pairs[][2] = { /* standard cases, replace map part with mus part (from the end to preserve prefixes) */ {"MUS_CTRL.MPF", "MUS_STR.MUS"}, /* GoldenEye - Rogue Agent (PS2) */ @@ -654,7 +658,7 @@ static STREAMFILE* open_mapfile_pair(STREAMFILE *streamFile, int track, int num_ * really need to think of something for this */ }; - STREAMFILE *musFile = NULL; + STREAMFILE* sf_mus = NULL; char file_name[PATH_LIMIT]; int pair_count = (sizeof(mapfile_pairs)/sizeof(mapfile_pairs[0])); int i, j; @@ -662,11 +666,11 @@ static STREAMFILE* open_mapfile_pair(STREAMFILE *streamFile, int track, int num_ /* if loading the first track, try opening MUS with the same name first (most common scenario) */ if (track == 0) { - musFile = open_streamfile_by_ext(streamFile, "mus"); - if (musFile) return musFile; + sf_mus = open_streamfile_by_ext(sf, "mus"); + if (sf_mus) return sf_mus; } - get_streamfile_filename(streamFile, file_name, PATH_LIMIT); + get_streamfile_filename(sf, file_name, PATH_LIMIT); file_len = strlen(file_name); for (i = 0; i < pair_count; i++) { @@ -707,10 +711,10 @@ static STREAMFILE* open_mapfile_pair(STREAMFILE *streamFile, int track, int num_ strncpy(file_name, pch, PATH_LIMIT - 1); } - musFile = open_streamfile_by_filename(streamFile, file_name); - if (musFile) return musFile; + sf_mus = open_streamfile_by_filename(sf, file_name); + if (sf_mus) return sf_mus; - get_streamfile_filename(streamFile, file_name, PATH_LIMIT); /* reset for next loop */ + get_streamfile_filename(sf, file_name, PATH_LIMIT); /* reset for next loop */ } /* hack when when multiple maps point to the same mus, uses name before "+" @@ -721,8 +725,8 @@ static STREAMFILE* open_mapfile_pair(STREAMFILE *streamFile, int track, int num_ if (mod_name) { mod_name[0] = '\0'; - musFile = open_streamfile_by_filename(streamFile, file_name); - if (musFile) return musFile; + sf_mus = open_streamfile_by_filename(sf, file_name); + if (sf_mus) return sf_mus; } } @@ -731,26 +735,26 @@ static STREAMFILE* open_mapfile_pair(STREAMFILE *streamFile, int track, int num_ } /* EA MAP/MUS combo - used in older games for interactive music (for EA's PathFinder tool) */ -VGMSTREAM * init_vgmstream_ea_map_mus(STREAMFILE *streamFile) { +VGMSTREAM * init_vgmstream_ea_map_mus(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + STREAMFILE* sf_mus = NULL; uint8_t version, num_sounds, num_events, num_sections; off_t section_offset, schl_offset; - STREAMFILE *musFile = NULL; - VGMSTREAM *vgmstream = NULL; - int target_stream = streamFile->stream_index; + int target_stream = sf->stream_index; /* check extension */ - if (!check_extensions(streamFile, "map,lin,mpf")) + if (!check_extensions(sf, "map,lin,mpf")) goto fail; /* always big endian */ - if (read_32bitBE(0x00, streamFile) != 0x50464478) /* "PFDx" */ + if (read_32bitBE(0x00, sf) != 0x50464478) /* "PFDx" */ goto fail; - version = read_8bit(0x04, streamFile); + version = read_8bit(0x04, sf); if (version > 1) goto fail; - musFile = open_mapfile_pair(streamFile, 0, 1); - if (!musFile) goto fail; + sf_mus = open_mapfile_pair(sf, 0, 1); + if (!sf_mus) goto fail; /* * 0x04: version @@ -761,9 +765,9 @@ VGMSTREAM * init_vgmstream_ea_map_mus(STREAMFILE *streamFile) { * 0x0b: number of events * 0x0c: data start */ - num_sounds = read_8bit(0x06, streamFile); - num_sections = read_8bit(0x07, streamFile); - num_events = read_8bit(0x0b, streamFile); + num_sounds = read_8bit(0x06, sf); + num_sections = read_8bit(0x07, sf); + num_events = read_8bit(0x0b, sf); section_offset = 0x0c; /* section 1: nodes, contains information about segment playback order */ @@ -777,46 +781,46 @@ VGMSTREAM * init_vgmstream_ea_map_mus(STREAMFILE *streamFile) { goto fail; /* section 3: samples */ - schl_offset = read_32bitBE(section_offset + (target_stream - 1) * 0x04, streamFile); - if (read_32bitBE(schl_offset, musFile) != EA_BLOCKID_HEADER) + schl_offset = read_32bitBE(section_offset + (target_stream - 1) * 0x04, sf); + if (read_32bitBE(schl_offset, sf_mus) != EA_BLOCKID_HEADER) goto fail; - vgmstream = parse_schl_block(musFile, schl_offset, 0); + vgmstream = parse_schl_block(sf_mus, schl_offset, 0); if (!vgmstream) goto fail; vgmstream->num_streams = num_sounds; - close_streamfile(musFile); + close_streamfile(sf_mus); return vgmstream; fail: - close_streamfile(musFile); + close_streamfile(sf_mus); return NULL; } /* EA MPF/MUS combo - used in 6th gen games for interactive music (for EA's PathFinder tool) */ -VGMSTREAM * init_vgmstream_ea_mpf_mus(STREAMFILE *streamFile) { +VGMSTREAM * init_vgmstream_ea_mpf_mus(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + STREAMFILE* sf_mus = NULL; off_t tracks_table, samples_table, section_offset, entry_offset, eof_offset, off_mult, sound_offset; uint32_t track_start, track_hash = 0; uint16_t num_nodes; uint8_t version, sub_version, num_tracks, num_sections, num_events, num_routers, num_vars, subentry_num; int32_t(*read_32bit)(off_t, STREAMFILE*); int16_t(*read_16bit)(off_t, STREAMFILE*); - STREAMFILE *musFile = NULL; - VGMSTREAM *vgmstream = NULL; int i; - int target_stream = streamFile->stream_index, total_streams, big_endian, is_bnk = 0; + int target_stream = sf->stream_index, total_streams, big_endian, is_bnk = 0; /* check extension */ - if (!check_extensions(streamFile, "mpf")) + if (!check_extensions(sf, "mpf")) goto fail; /* detect endianness */ - if (read_32bitBE(0x00, streamFile) == 0x50464478) { /* "PFDx" */ + if (read_32bitBE(0x00, sf) == 0x50464478) { /* "PFDx" */ read_32bit = read_32bitBE; read_16bit = read_16bitBE; big_endian = 1; - } else if (read_32bitLE(0x00, streamFile) == 0x50464478) { /* "xDFP" */ + } else if (read_32bitLE(0x00, sf) == 0x50464478) { /* "xDFP" */ read_32bit = read_32bitLE; read_16bit = read_16bitLE; big_endian = 0; @@ -824,18 +828,18 @@ VGMSTREAM * init_vgmstream_ea_mpf_mus(STREAMFILE *streamFile) { goto fail; } - version = read_8bit(0x04, streamFile); - sub_version = read_8bit(0x05, streamFile); + version = read_8bit(0x04, sf); + sub_version = read_8bit(0x05, sf); if (version < 3 || version > 5) goto fail; if (version == 5 && sub_version > 2) goto fail; /* newer version using SNR/SNS */ - num_tracks = read_8bit(0x0d, streamFile); - num_sections = read_8bit(0x0e, streamFile); - num_events = read_8bit(0x0f, streamFile); - num_routers = read_8bit(0x10, streamFile); - num_vars = read_8bit(0x11, streamFile); - num_nodes = read_16bit(0x12, streamFile); + num_tracks = read_8bit(0x0d, sf); + num_sections = read_8bit(0x0e, sf); + num_events = read_8bit(0x0f, sf); + num_routers = read_8bit(0x10, sf); + num_vars = read_8bit(0x11, sf); + num_nodes = read_16bit(0x12, sf); /* HACK: number of sub-entries for nodes and events is stored in bitstreams that are different in LE and BE */ /* I can't figure it out, so let's just use a workaround for now */ @@ -847,14 +851,14 @@ VGMSTREAM * init_vgmstream_ea_mpf_mus(STREAMFILE *streamFile) { /* we need to go through all the sections to get to the samples table */ /* get the last entry offset */ section_offset = 0x24; - entry_offset = (uint16_t)read_16bit(section_offset + (num_nodes - 1) * 0x02, streamFile) * 0x04; + entry_offset = (uint16_t)read_16bit(section_offset + (num_nodes - 1) * 0x02, sf) * 0x04; if (sub_version == 1) { - subentry_num = read_8bit(entry_offset + 0x0b, streamFile); + subentry_num = read_8bit(entry_offset + 0x0b, sf); } else if (sub_version == 4) { if (big_endian) { - subentry_num = (read_32bitBE(entry_offset + 0x04, streamFile) >> 19) & 0xFF; + subentry_num = (read_32bitBE(entry_offset + 0x04, sf) >> 19) & 0xFF; } else { - subentry_num = (read_32bitBE(entry_offset + 0x04, streamFile) >> 16) & 0xFF; + subentry_num = (read_32bitBE(entry_offset + 0x04, sf) >> 16) & 0xFF; } } else { goto fail; @@ -864,17 +868,17 @@ VGMSTREAM * init_vgmstream_ea_mpf_mus(STREAMFILE *streamFile) { section_offset += align_size_to_block(num_events * num_tracks * num_sections, 0x04); section_offset += num_routers * 0x04; section_offset += num_vars * 0x04; - tracks_table = read_32bit(section_offset, streamFile) * 0x04; + tracks_table = read_32bit(section_offset, sf) * 0x04; samples_table = tracks_table + (num_tracks + 1) * 0x04; for (i = num_tracks - 1; i >= 0; i--) { - track_start = read_32bit(tracks_table + i * 0x04, streamFile) * 0x04; + track_start = read_32bit(tracks_table + i * 0x04, sf) * 0x04; track_start = (track_start - samples_table) / 0x08; if (track_start <= target_stream - 1) break; } - eof_offset = read_32bit(tracks_table + num_tracks * 0x04, streamFile) * 0x04; + eof_offset = read_32bit(tracks_table + num_tracks * 0x04, sf) * 0x04; total_streams = (eof_offset - samples_table) / 0x08; off_mult = 0x04; } else if (version == 4) { @@ -882,70 +886,70 @@ VGMSTREAM * init_vgmstream_ea_mpf_mus(STREAMFILE *streamFile) { /* we need to go through all the sections to get to the samples table */ /* get the last entry offset */ section_offset = 0x20; - entry_offset = (uint16_t)read_16bit(section_offset + (num_nodes - 1) * 0x02, streamFile) * 0x04; + entry_offset = (uint16_t)read_16bit(section_offset + (num_nodes - 1) * 0x02, sf) * 0x04; if (big_endian) { - subentry_num = (read_32bitBE(entry_offset + 0x04, streamFile) >> 15) & 0xFF; + subentry_num = (read_32bitBE(entry_offset + 0x04, sf) >> 15) & 0xFF; } else { - subentry_num = (read_32bitBE(entry_offset + 0x04, streamFile) >> 20) & 0xFF; + subentry_num = (read_32bitBE(entry_offset + 0x04, sf) >> 20) & 0xFF; } section_offset = entry_offset + 0x10 + subentry_num * 0x04; /* get the last entry offset */ - entry_offset = (uint16_t)read_16bit(section_offset + (num_events - 1) * 0x02, streamFile) * 0x04; + entry_offset = (uint16_t)read_16bit(section_offset + (num_events - 1) * 0x02, sf) * 0x04; if (big_endian) { - subentry_num = (read_32bitBE(entry_offset + 0x0c, streamFile) >> 10) & 0xFF; + subentry_num = (read_32bitBE(entry_offset + 0x0c, sf) >> 10) & 0xFF; } else { - subentry_num = (read_32bitBE(entry_offset + 0x0c, streamFile) >> 8) & 0xFF; + subentry_num = (read_32bitBE(entry_offset + 0x0c, sf) >> 8) & 0xFF; } section_offset = entry_offset + 0x10 + subentry_num * 0x10; /* TODO: verify this */ - section_offset = read_32bit(section_offset, streamFile) * 0x04; + section_offset = read_32bit(section_offset, sf) * 0x04; section_offset += num_routers * 0x04; section_offset += num_vars * 0x04; tracks_table = section_offset; samples_table = tracks_table + (num_tracks + 1) * 0x04; for (i = num_tracks - 1; i >= 0; i--) { - track_start = read_32bit(tracks_table + i * 0x04, streamFile) * 0x04; + track_start = read_32bit(tracks_table + i * 0x04, sf) * 0x04; track_start = (track_start - samples_table) / 0x08; if (track_start <= target_stream - 1) break; } - eof_offset = read_32bit(tracks_table + num_tracks * 0x04, streamFile) * 0x04; + eof_offset = read_32bit(tracks_table + num_tracks * 0x04, sf) * 0x04; total_streams = (eof_offset - samples_table) / 0x08; off_mult = 0x80; } else if (version == 5) { /* Need for Speed: Most Wanted, Need for Speed: Carbon */ - tracks_table = read_32bit(0x2c, streamFile); - samples_table = read_32bit(0x34, streamFile); + tracks_table = read_32bit(0x2c, sf); + samples_table = read_32bit(0x34, sf); for (i = num_tracks - 1; i >= 0; i--) { - entry_offset = read_32bit(tracks_table + i * 0x04, streamFile) * 0x04; - track_start = read_32bit(entry_offset + 0x00, streamFile); + entry_offset = read_32bit(tracks_table + i * 0x04, sf) * 0x04; + track_start = read_32bit(entry_offset + 0x00, sf); if (track_start <= target_stream - 1) { - track_hash = read_32bitBE(entry_offset + 0x08, streamFile); + track_hash = read_32bitBE(entry_offset + 0x08, sf); is_bnk = (track_hash == 0xF1F1F1F1); /* checks to distinguish it from SNR/SNS version */ if (is_bnk) { - if (read_32bitBE(entry_offset + 0x0c, streamFile) == 0x00) + if (read_32bitBE(entry_offset + 0x0c, sf) == 0x00) goto fail; - track_hash = read_32bitBE(entry_offset + 0x14, streamFile); + track_hash = read_32bitBE(entry_offset + 0x14, sf); if (track_hash == 0xF1F1F1F1) continue; /* empty track */ } else { - if (read_32bitBE(entry_offset + 0x0c, streamFile) != 0x00) + if (read_32bitBE(entry_offset + 0x0c, sf) != 0x00) goto fail; } break; } } - eof_offset = read_32bit(0x38, streamFile); + eof_offset = read_32bit(0x38, sf); total_streams = (eof_offset - samples_table) / 0x08; off_mult = 0x80; } else { @@ -956,46 +960,46 @@ VGMSTREAM * init_vgmstream_ea_mpf_mus(STREAMFILE *streamFile) { goto fail; /* open MUS file that matches this track */ - musFile = open_mapfile_pair(streamFile, i, num_tracks); - if (!musFile) + sf_mus = open_mapfile_pair(sf, i, num_tracks); + if (!sf_mus) goto fail; if (version == 5) { - if (read_32bitBE(0x00, musFile) != track_hash) + if (read_32bitBE(0x00, sf_mus) != track_hash) goto fail; } else { - is_bnk = (read_32bitBE(0x00, musFile) == (big_endian ? EA_BNK_HEADER_BE : EA_BNK_HEADER_LE)); + is_bnk = (read_32bitBE(0x00, sf_mus) == (big_endian ? EA_BNK_HEADER_BE : EA_BNK_HEADER_LE)); } /* 0x00 - offset/BNK index, 0x04 - duration (in milliseconds) */ if (is_bnk) { /* TODO: Harry Potter COS appears to reference only the first segments of multi-segment BNK sounds? */ - sound_offset = read_32bit(samples_table + (target_stream - 1) * 0x08 + 0x00, streamFile); - vgmstream = parse_bnk_header(musFile, version < 5 ? 0x00 : 0x100, sound_offset, 1); + sound_offset = read_32bit(samples_table + (target_stream - 1) * 0x08 + 0x00, sf); + vgmstream = parse_bnk_header(sf_mus, version < 5 ? 0x00 : 0x100, sound_offset, 1); if (!vgmstream) goto fail; } else { - sound_offset = read_32bit(samples_table + (target_stream - 1) * 0x08 + 0x00, streamFile) * off_mult; - if (read_32bitBE(sound_offset, musFile) != EA_BLOCKID_HEADER) + sound_offset = read_32bit(samples_table + (target_stream - 1) * 0x08 + 0x00, sf) * off_mult; + if (read_32bitBE(sound_offset, sf_mus) != EA_BLOCKID_HEADER) goto fail; - vgmstream = parse_schl_block(musFile, sound_offset, 0); + vgmstream = parse_schl_block(sf_mus, sound_offset, 0); if (!vgmstream) goto fail; } vgmstream->num_streams = total_streams; - get_streamfile_filename(musFile, vgmstream->stream_name, STREAM_NAME_SIZE); - close_streamfile(musFile); + get_streamfile_filename(sf_mus, vgmstream->stream_name, STREAM_NAME_SIZE); + close_streamfile(sf_mus); return vgmstream; fail: - close_streamfile(musFile); + close_streamfile(sf_mus); return NULL; } /* EA SCHl with variable header - from EA games (roughly 1997~2010); generated by EA Canada's sx.exe/Sound eXchange */ -static VGMSTREAM * parse_schl_block(STREAMFILE *streamFile, off_t offset, int standalone) { +static VGMSTREAM * parse_schl_block(STREAMFILE* sf, off_t offset, int standalone) { off_t start_offset, header_offset; size_t header_size; uint32_t header_id; @@ -1003,35 +1007,35 @@ static VGMSTREAM * parse_schl_block(STREAMFILE *streamFile, off_t offset, int st /* use higher bits to store target localized block in case of multilang video, * so only header sub-id will be read and other langs skipped */ - header_id = read_32bitBE(offset + 0x00, streamFile); + header_id = read_32bitBE(offset + 0x00, sf); if ((header_id & 0xFFFF0000) == EA_BLOCKID_LOC_HEADER) { ea.codec_config |= (header_id & 0xFFFF) << 16; } - if (guess_endianness32bit(offset + 0x04, streamFile)) { /* size is always LE, except in early SS/MAC */ - header_size = read_32bitBE(offset + 0x04, streamFile); + if (guess_endianness32bit(offset + 0x04, sf)) { /* size is always LE, except in early SS/MAC */ + header_size = read_32bitBE(offset + 0x04, sf); ea.codec_config |= 0x02; } else { - header_size = read_32bitLE(offset + 0x04, streamFile); + header_size = read_32bitLE(offset + 0x04, sf); } header_offset = offset + 0x08; - if (!parse_variable_header(streamFile, &ea, header_offset, header_size - 0x08, 0)) + if (!parse_variable_header(sf, &ea, header_offset, header_size - 0x08, 0)) goto fail; start_offset = offset + header_size; /* starts in "SCCl" (skipped in block layout) or very rarely "SCDl" and maybe movie blocks */ /* rest is common */ - return init_vgmstream_ea_variable_header(streamFile, &ea, start_offset, 0, standalone); + return init_vgmstream_ea_variable_header(sf, &ea, start_offset, 0, standalone); fail: return NULL; } /* EA BNK with variable header - from EA games SFXs; also created by sx.exe */ -static VGMSTREAM * parse_bnk_header(STREAMFILE *streamFile, off_t offset, int target_stream, int is_embedded) { +static VGMSTREAM * parse_bnk_header(STREAMFILE* sf, off_t offset, int target_stream, int is_embedded) { uint32_t i; uint16_t num_sounds; off_t header_offset, start_offset, test_offset, table_offset, entry_offset; @@ -1045,31 +1049,31 @@ static VGMSTREAM * parse_bnk_header(STREAMFILE *streamFile, off_t offset, int ta /* check header */ /* BNK header endianness is platform-native */ - if (read_32bitBE(offset + 0x00, streamFile) == EA_BNK_HEADER_BE) { + if (read_32bitBE(offset + 0x00, sf) == EA_BNK_HEADER_BE) { read_32bit = read_32bitBE; read_16bit = read_16bitBE; - } else if (read_32bitBE(offset + 0x00, streamFile) == EA_BNK_HEADER_LE) { + } else if (read_32bitBE(offset + 0x00, sf) == EA_BNK_HEADER_LE) { read_32bit = read_32bitLE; read_16bit = read_16bitLE; } else { goto fail; } - bnk_version = read_8bit(offset + 0x04, streamFile); - num_sounds = read_16bit(offset + 0x06, streamFile); + bnk_version = read_8bit(offset + 0x04, sf); + num_sounds = read_16bit(offset + 0x06, sf); /* check multi-streams */ switch (bnk_version) { case 0x02: /* early [Need For Speed II (PC/PS1), FIFA 98 (PC/PS1/SAT)] */ table_offset = 0x0c; - header_size = read_32bit(offset + 0x08, streamFile); /* full size */ + header_size = read_32bit(offset + 0x08, sf); /* full size */ break; case 0x04: /* mid (last used in PSX banks) */ case 0x05: /* late (generated by sx.exe ~v2+) */ /* 0x08: header/file size, 0x0C: file size/null, 0x10: always null */ table_offset = 0x14; - header_size = get_streamfile_size(streamFile); /* unknown (header is variable and may have be garbage until data) */ + header_size = get_streamfile_size(sf); /* unknown (header is variable and may have be garbage until data) */ break; default: @@ -1084,12 +1088,12 @@ static VGMSTREAM * parse_bnk_header(STREAMFILE *streamFile, off_t offset, int ta goto fail; entry_offset = offset + table_offset + 0x04 * target_stream; - header_offset = entry_offset + read_32bit(entry_offset, streamFile); + header_offset = entry_offset + read_32bit(entry_offset, sf); } else { /* some of these are dummies with zero offset, skip them when opening standalone BNK */ for (i = 0; i < num_sounds; i++) { entry_offset = offset + table_offset + 0x04 * i; - test_offset = read_32bit(entry_offset, streamFile); + test_offset = read_32bit(entry_offset, sf); if (test_offset != 0) { if (target_stream == real_bnk_sounds) @@ -1102,7 +1106,7 @@ static VGMSTREAM * parse_bnk_header(STREAMFILE *streamFile, off_t offset, int ta if (header_offset == 0) goto fail; - if (!parse_variable_header(streamFile, &ea, header_offset, header_size - header_offset, bnk_version)) + if (!parse_variable_header(sf, &ea, header_offset, header_size - header_offset, bnk_version)) goto fail; /* fix absolute offsets so it works in next funcs */ @@ -1115,7 +1119,7 @@ static VGMSTREAM * parse_bnk_header(STREAMFILE *streamFile, off_t offset, int ta start_offset = ea.offsets[0]; /* first channel, presumably needed for MPEG */ /* rest is common */ - vgmstream = init_vgmstream_ea_variable_header(streamFile, &ea, start_offset, bnk_version, 0); + vgmstream = init_vgmstream_ea_variable_header(sf, &ea, start_offset, bnk_version, 0); if (!vgmstream) goto fail; if (!is_embedded) { vgmstream->num_streams = real_bnk_sounds; @@ -1128,7 +1132,7 @@ fail: } /* inits VGMSTREAM from a EA header */ -static VGMSTREAM * init_vgmstream_ea_variable_header(STREAMFILE *streamFile, ea_header * ea, off_t start_offset, int bnk_version, int standalone) { +static VGMSTREAM * init_vgmstream_ea_variable_header(STREAMFILE* sf, ea_header* ea, off_t start_offset, int bnk_version, int standalone) { VGMSTREAM * vgmstream = NULL; int i, ch; int is_bnk = bnk_version; @@ -1164,12 +1168,18 @@ static VGMSTREAM * init_vgmstream_ea_variable_header(STREAMFILE *streamFile, ea_ } } else if (ea->channels > 1 && ea->codec2 == EA_CODEC2_GCADPCM && ea->offsets[0] == ea->offsets[1]) { - /* pcstream+gcadpcm with sx.exe v2, this is probably a bug (even with this parts of the wave are off) */ + /* pcstream+gcadpcm with sx.exe v2, not in flag_value, probably a bug (even with this parts of the wave are off) */ int interleave = (ea->num_samples / 14 * 8); /* full interleave */ for (i = 0; i < ea->channels; i++) { ea->offsets[i] = ea->offsets[0] + interleave*i; } } + else if (ea->channels > 1 && ea->codec2 == EA_CODEC2_N64 && ea->offsets[1] == 0) { + uint32_t interleave = ea->flag_value; + for (i = 0; i < ea->channels; i++) { + ea->offsets[i] = ea->offsets[0] + interleave * i; + } + } } else { vgmstream->layout_type = layout_blocked_ea_schl; @@ -1225,23 +1235,34 @@ static VGMSTREAM * init_vgmstream_ea_variable_header(STREAMFILE *streamFile, ea_ for (ch=0; ch < ea->channels; ch++) { for (i=0; i < 16; i++) { /* actual size 0x21, last byte unknown */ - vgmstream->ch[ch].adpcm_coef[i] = read_16bit(ea->coefs[ch] + i*2, streamFile); + vgmstream->ch[ch].adpcm_coef[i] = read_16bit(ea->coefs[ch] + i*2, sf); } } } break; + case EA_CODEC2_N64: /* VADPCM */ + vgmstream->coding_type = coding_VADPCM; + vgmstream->layout_type = layout_none; + + for (ch = 0; ch < ea->channels; ch++) { + int order = read_u32be(ea->coefs[ch] + 0x00, sf); + int entries = read_u32be(ea->coefs[ch] + 0x04, sf); + vadpcm_read_coefs_be(vgmstream, sf, ea->coefs[ch] + 0x08, order, entries, ch); + } + break; + #ifdef VGM_USE_MPEG case EA_CODEC2_LAYER2: /* MPEG Layer II, aka MP2 */ case EA_CODEC2_LAYER3: { /* MPEG Layer III, aka MP3 */ mpeg_custom_config cfg = {0}; off_t mpeg_start_offset = is_bnk ? start_offset : - get_ea_stream_mpeg_start_offset(streamFile, start_offset, ea); + get_ea_stream_mpeg_start_offset(sf, start_offset, ea); if (!mpeg_start_offset) goto fail; /* layout is still blocks, but should work fine with the custom mpeg decoder */ - vgmstream->codec_data = init_mpeg_custom(streamFile, mpeg_start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_EA, &cfg); + vgmstream->codec_data = init_mpeg_custom(sf, mpeg_start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_EA, &cfg); if (!vgmstream->codec_data) goto fail; break; } @@ -1250,11 +1271,11 @@ static VGMSTREAM * init_vgmstream_ea_variable_header(STREAMFILE *streamFile, ea_ mpeg_custom_config cfg = {0}; off_t mpeg_start_offset = is_bnk ? start_offset : - get_ea_stream_mpeg_start_offset(streamFile, start_offset, ea); + get_ea_stream_mpeg_start_offset(sf, start_offset, ea); if (!mpeg_start_offset) goto fail; /* layout is still blocks, but should work fine with the custom mpeg decoder */ - vgmstream->codec_data = init_mpeg_custom(streamFile, mpeg_start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_EAL31, &cfg); + vgmstream->codec_data = init_mpeg_custom(sf, mpeg_start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_EAL31, &cfg); if (!vgmstream->codec_data) goto fail; break; } @@ -1286,20 +1307,20 @@ static VGMSTREAM * init_vgmstream_ea_variable_header(STREAMFILE *streamFile, ea_ case EA_CODEC2_ATRAC3PLUS: { /* regular ATRAC3plus chunked in SCxx blocks, including RIFF header [Medal of Honor Heroes 2 (PSP)] */ if (!is_bnk) { - STREAMFILE* temp_streamFile = NULL; + STREAMFILE* temp_sf = NULL; /* remove blocks on reads to feed FFmpeg a clean .at3 */ - temp_streamFile = setup_schl_streamfile(streamFile, ea->codec2, ea->channels, start_offset, 0); - if (!temp_streamFile) goto fail; + temp_sf = setup_schl_streamfile(sf, ea->codec2, ea->channels, start_offset, 0); + if (!temp_sf) goto fail; start_offset = 0x00; /* must point to the custom streamfile's beginning */ - ea->stream_size = get_streamfile_size(temp_streamFile); + ea->stream_size = get_streamfile_size(temp_sf); - vgmstream->codec_data = init_ffmpeg_atrac3_riff(temp_streamFile, start_offset, NULL); - close_streamfile(temp_streamFile); + vgmstream->codec_data = init_ffmpeg_atrac3_riff(temp_sf, start_offset, NULL); + close_streamfile(temp_sf); } else { /* memory file without blocks */ - vgmstream->codec_data = init_ffmpeg_atrac3_riff(streamFile, start_offset, NULL); + vgmstream->codec_data = init_ffmpeg_atrac3_riff(sf, start_offset, NULL); } if (!vgmstream->codec_data) goto fail; @@ -1316,8 +1337,8 @@ static VGMSTREAM * init_vgmstream_ea_variable_header(STREAMFILE *streamFile, ea_ vgmstream->stream_size = ea->stream_size; - /* open files; channel offsets are updated below */ - if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + /* open files; channel offsets are updated below (force multibuffer for bnk) */ + if (!vgmstream_open_stream_bf(vgmstream, sf, start_offset, 1)) goto fail; @@ -1356,7 +1377,7 @@ static VGMSTREAM * init_vgmstream_ea_variable_header(STREAMFILE *streamFile, ea_ /* TODO: Figure out how to get stream size for BNK sounds */ } else { - update_ea_stream_size_and_samples(streamFile, start_offset, vgmstream, standalone); + update_ea_stream_size_and_samples(sf, start_offset, vgmstream, standalone); } return vgmstream; @@ -1367,13 +1388,13 @@ fail: } -static uint32_t read_patch(STREAMFILE* streamFile, off_t* offset) { +static uint32_t read_patch(STREAMFILE* sf, off_t* offset) { uint32_t result = 0; - uint8_t byte_count = read_8bit(*offset, streamFile); + uint8_t byte_count = read_8bit(*offset, sf); (*offset)++; if (byte_count == 0xFF) { /* signals 32b size (ex. custom user data) */ - (*offset) += 4 + read_32bitBE(*offset, streamFile); + (*offset) += 4 + read_32bitBE(*offset, sf); return 0; } @@ -1384,7 +1405,7 @@ static uint32_t read_patch(STREAMFILE* streamFile, off_t* offset) { for ( ; byte_count > 0; byte_count--) { /* count of 0 is also possible, means value 0 */ result <<= 8; - result += (uint8_t)read_8bit(*offset, streamFile); + result += (uint8_t)read_8bit(*offset, sf); (*offset)++; } @@ -1392,7 +1413,7 @@ static uint32_t read_patch(STREAMFILE* streamFile, off_t* offset) { } /* decodes EA's GSTR/PT header (mostly cross-referenced with sx.exe) */ -static int parse_variable_header(STREAMFILE* streamFile, ea_header* ea, off_t begin_offset, int max_length, int bnk_version) { +static int parse_variable_header(STREAMFILE* sf, ea_header* ea, off_t begin_offset, int max_length, int bnk_version) { off_t offset = begin_offset; uint32_t platform_id; int is_header_end = 0; @@ -1404,17 +1425,17 @@ static int parse_variable_header(STREAMFILE* streamFile, ea_header* ea, off_t be ea->codec2 = EA_CODEC2_NONE; /* get platform info */ - platform_id = read_32bitBE(offset, streamFile); + platform_id = read_32bitBE(offset, sf); if (platform_id != 0x47535452 && (platform_id & 0xFFFF0000) != 0x50540000) { offset += 4; /* skip unknown field (related to blocks/size?) in "nbapsstream" (NBA2000 PS, FIFA2001 PS) */ - platform_id = read_32bitBE(offset, streamFile); + platform_id = read_32bitBE(offset, sf); } if (platform_id == 0x47535452) { /* "GSTR" = Generic STReam */ ea->platform = EA_PLATFORM_GENERIC; offset += 4 + 4; /* GSTRs have an extra field (config?): ex. 0x01000000, 0x010000D8 BE */ } else if ((platform_id & 0xFFFF0000) == 0x50540000) { /* "PT" = PlaTform */ - ea->platform = (uint16_t)read_16bitLE(offset + 2,streamFile); + ea->platform = (uint16_t)read_16bitLE(offset + 2,sf); offset += 4; } else { @@ -1424,15 +1445,14 @@ static int parse_variable_header(STREAMFILE* streamFile, ea_header* ea, off_t be /* parse mini-chunks/tags (variable, ommited if default exists; some are removed in later versions of sx.exe) */ while (!is_header_end && offset - begin_offset < max_length) { - uint8_t patch_type = read_8bit(offset,streamFile); + uint8_t patch_type = read_8bit(offset,sf); offset++; - //;off_t test_offset = offset; - //;VGM_LOG("EA SCHl: patch=%02x at %lx, value=%x\n", patch_type, offset-1, read_patch(streamFile, &test_offset)); + //;{ off_t test = offset; VGM_LOG("EA SCHl: patch=%02x at %lx, value=%x\n", patch_type, offset-1, read_patch(sf, &test)); } switch(patch_type) { case 0x00: /* signals non-default block rate and maybe other stuff; or padding after 0xFF */ if (!is_header_end) - read_patch(streamFile, &offset); + read_patch(sf, &offset); break; case 0x05: /* unknown (usually 0x50 except Madden NFL 3DS: 0x3e800) */ @@ -1464,7 +1484,7 @@ static int parse_variable_header(STREAMFILE* streamFile, ea_header* ea, off_t be case 0x23: case 0x24: /* master random detune range (BNK only) */ case 0x25: /* unknown */ - read_patch(streamFile, &offset); + read_patch(sf, &offset); break; case 0xFC: /* padding for alignment between patches */ @@ -1472,104 +1492,108 @@ static int parse_variable_header(STREAMFILE* streamFile, ea_header* ea, off_t be break; case 0x83: /* codec1 defines, used early revisions */ - ea->codec1 = read_patch(streamFile, &offset); + ea->codec1 = read_patch(sf, &offset); break; case 0xA0: /* codec2 defines */ - ea->codec2 = read_patch(streamFile, &offset); + ea->codec2 = read_patch(sf, &offset); break; case 0x80: /* version, affecting some codecs */ - ea->version = read_patch(streamFile, &offset); + ea->version = read_patch(sf, &offset); break; case 0x81: /* bits per sample for codec1 PCM */ - ea->bps = read_patch(streamFile, &offset); + ea->bps = read_patch(sf, &offset); break; case 0x82: /* channel count */ - ea->channels = read_patch(streamFile, &offset); + ea->channels = read_patch(sf, &offset); break; case 0x84: /* sample rate */ - ea->sample_rate = read_patch(streamFile,&offset); + ea->sample_rate = read_patch(sf,&offset); break; case 0x85: /* sample count */ - ea->num_samples = read_patch(streamFile, &offset); + ea->num_samples = read_patch(sf, &offset); break; case 0x86: /* loop start sample */ - ea->loop_start = read_patch(streamFile, &offset); + ea->loop_start = read_patch(sf, &offset); break; case 0x87: /* loop end sample */ - ea->loop_end = read_patch(streamFile, &offset) + 1; /* sx.exe does +1 */ + ea->loop_end = read_patch(sf, &offset) + 1; /* sx.exe does +1 */ break; /* channel offsets (BNK only), can be the equal for all channels or interleaved; not necessarily contiguous */ case 0x88: /* absolute offset of ch1 (or ch1+ch2 for stereo EAXA) */ - ea->offsets[0] = read_patch(streamFile, &offset); + ea->offsets[0] = read_patch(sf, &offset); break; case 0x89: /* absolute offset of ch2 */ - ea->offsets[1] = read_patch(streamFile, &offset); + ea->offsets[1] = read_patch(sf, &offset); break; case 0x94: /* absolute offset of ch3 */ - ea->offsets[2] = read_patch(streamFile, &offset); + ea->offsets[2] = read_patch(sf, &offset); break; case 0x95: /* absolute offset of ch4 */ - ea->offsets[3] = read_patch(streamFile, &offset); + ea->offsets[3] = read_patch(sf, &offset); break; case 0xA2: /* absolute offset of ch5 */ - ea->offsets[4] = read_patch(streamFile, &offset); + ea->offsets[4] = read_patch(sf, &offset); break; case 0xA3: /* absolute offset of ch6 */ - ea->offsets[5] = read_patch(streamFile, &offset); + ea->offsets[5] = read_patch(sf, &offset); break; case 0x8F: /* DSP/N64BLK coefs ch1 */ ea->coefs[0] = offset+1; - read_patch(streamFile, &offset); + read_patch(sf, &offset); break; case 0x90: /* DSP/N64BLK coefs ch2 */ ea->coefs[1] = offset+1; - read_patch(streamFile, &offset); + read_patch(sf, &offset); break; case 0x91: /* DSP coefs ch3, and unknown in older versions */ ea->coefs[2] = offset+1; - read_patch(streamFile, &offset); + read_patch(sf, &offset); break; case 0xAB: /* DSP coefs ch4 */ ea->coefs[3] = offset+1; - read_patch(streamFile, &offset); + read_patch(sf, &offset); break; case 0xAC: /* DSP coefs ch5 */ ea->coefs[4] = offset+1; - read_patch(streamFile, &offset); + read_patch(sf, &offset); break; case 0xAD: /* DSP coefs ch6 */ ea->coefs[5] = offset+1; - read_patch(streamFile, &offset); + read_patch(sf, &offset); break; case 0x1A: /* EA-MT/EA-XA relative loop offset of ch1 */ - ea->loops[0] = read_patch(streamFile, &offset); + ea->loops[0] = read_patch(sf, &offset); break; case 0x26: /* EA-MT/EA-XA relative loop offset of ch2 */ - ea->loops[1] = read_patch(streamFile, &offset); + ea->loops[1] = read_patch(sf, &offset); break; case 0x27: /* EA-MT/EA-XA relative loop offset of ch3 */ - ea->loops[2] = read_patch(streamFile, &offset); + ea->loops[2] = read_patch(sf, &offset); break; case 0x28: /* EA-MT/EA-XA relative loop offset of ch4 */ - ea->loops[3] = read_patch(streamFile, &offset); + ea->loops[3] = read_patch(sf, &offset); break; case 0x29: /* EA-MT/EA-XA relative loop offset of ch5 */ - ea->loops[4] = read_patch(streamFile, &offset); + ea->loops[4] = read_patch(sf, &offset); break; case 0x2a: /* EA-MT/EA-XA relative loop offset of ch6 */ - ea->loops[5] = read_patch(streamFile, &offset); + ea->loops[5] = read_patch(sf, &offset); + break; + + case 0x8C: /* flags (ex. play type = 01=static/02=dynamic | spatialize = 20=pan/etc) */ + /* (ex. PS1 VAG=0, PS2 PCM/LAYER2=4, GC EAXA=4, 3DS DSP=512, Xbox EAXA=36, N64 BLK=05E800, N64 MT10=01588805E800) */ + /* in rare cases value is the interleave, will be ignored if > 32b */ + ea->flag_value = read_patch(sf, &offset); break; case 0x8A: /* long padding (always 0x00000000) */ case 0x8B: /* also padding? [Need for Speed: Hot Pursuit 2 (PC)] */ - case 0x8C: /* flags (ex. play type = 01=static/02=dynamic | spatialize = 20=pan/etc) */ - /* (ex. PS1 VAG=0, PS2 PCM/LAYER2=4, GC EAXA=4, 3DS DSP=512, Xbox EAXA=36, N64 BLK=05E800, N64 MT10=01588805E800) */ case 0x8D: /* unknown, rare [FIFA 07 (GC)] */ case 0x8E: case 0x92: /* bytes per sample? */ @@ -1583,7 +1607,7 @@ static int parse_variable_header(STREAMFILE* streamFile, ea_header* ea, off_t be case 0xA6: /* azimuth ch5 */ case 0xA7: /* azimuth ch6 */ case 0xA1: /* unknown and very rare, always 0x02 [FIFA 2001 (PS2)] */ - read_patch(streamFile, &offset); + read_patch(sf, &offset); break; case 0xFF: /* header end (then 0-padded so it's 32b aligned) */ @@ -1653,7 +1677,7 @@ static int parse_variable_header(STREAMFILE* streamFile, ea_header* ea, off_t be switch(ea->platform) { case EA_PLATFORM_PC: ea->codec1 = EA_CODEC1_PCM; break; case EA_PLATFORM_PSX: ea->codec1 = EA_CODEC1_VAG; break; // assumed - //case EA_PLATFORM_N64: ea->codec1 = EA_CODEC1_N64; break; + case EA_PLATFORM_N64: ea->codec1 = EA_CODEC1_N64; break; case EA_PLATFORM_MAC: ea->codec1 = EA_CODEC1_PCM; break; // assumed case EA_PLATFORM_SAT: ea->codec1 = EA_CODEC1_PCM; break; default: @@ -1671,6 +1695,7 @@ static int parse_variable_header(STREAMFILE* streamFile, ea_header* ea, off_t be case EA_CODEC1_VAG: ea->codec2 = EA_CODEC2_VAG; break; case EA_CODEC1_EAXA: ea->codec2 = EA_CODEC2_EAXA; break; case EA_CODEC1_MT10: ea->codec2 = EA_CODEC2_MT10; break; + case EA_CODEC1_N64: ea->codec2 = EA_CODEC2_N64; break; default: VGM_LOG("EA SCHl: unknown codec1 0x%02x\n", ea->codec1); goto fail; @@ -1746,13 +1771,13 @@ fail: return 0; } -static void update_ea_stream_size_and_samples(STREAMFILE* streamFile, off_t start_offset, VGMSTREAM *vgmstream, int standalone) { +static void update_ea_stream_size_and_samples(STREAMFILE* sf, off_t start_offset, VGMSTREAM *vgmstream, int standalone) { uint32_t block_id; int32_t num_samples = 0; size_t stream_size = 0, file_size; int multiple_schl = 0; - file_size = get_streamfile_size(streamFile); + file_size = get_streamfile_size(sf); /* formats with custom codecs */ if (vgmstream->layout_type != layout_blocked_ea_schl) { @@ -1766,7 +1791,7 @@ static void update_ea_stream_size_and_samples(STREAMFILE* streamFile, off_t star if (vgmstream->current_block_samples < 0) break; - block_id = read_32bitBE(vgmstream->current_block_offset + 0x00, streamFile); + block_id = read_32bitBE(vgmstream->current_block_offset + 0x00, sf); if (block_id == EA_BLOCKID_END) { /* banks should never contain movie "SHxx" */ if (!standalone) break; @@ -1803,8 +1828,8 @@ static void update_ea_stream_size_and_samples(STREAMFILE* streamFile, off_t star } /* find data start offset inside the first SCDl; not very elegant but oh well */ -static off_t get_ea_stream_mpeg_start_offset(STREAMFILE* streamFile, off_t start_offset, const ea_header* ea) { - size_t file_size = get_streamfile_size(streamFile); +static off_t get_ea_stream_mpeg_start_offset(STREAMFILE* sf, off_t start_offset, const ea_header* ea) { + size_t file_size = get_streamfile_size(sf); off_t block_offset = start_offset; int32_t (*read_32bit)(off_t,STREAMFILE*) = ea->big_endian ? read_32bitBE : read_32bitLE; uint32_t header_lang = (ea->codec_config >> 16) & 0xFFFF; @@ -1813,15 +1838,15 @@ static off_t get_ea_stream_mpeg_start_offset(STREAMFILE* streamFile, off_t start uint32_t block_id, block_size; off_t offset; - block_id = read_32bitBE(block_offset+0x00,streamFile); + block_id = read_32bitBE(block_offset+0x00,sf); - block_size = read_32bitLE(block_offset+0x04,streamFile); + block_size = read_32bitLE(block_offset+0x04,sf); if (block_size > 0x00F00000) /* size is always LE, except in early SAT/MAC */ - block_size = read_32bitBE(block_offset+0x04,streamFile); + block_size = read_32bitBE(block_offset+0x04,sf); if (block_id == EA_BLOCKID_DATA || block_id == ((EA_BLOCKID_LOC_DATA | header_lang))) { /* "SCDl" or target "SDxx" multilang blocks */ - offset = read_32bit(block_offset+0x0c,streamFile); /* first value seems ok, second is something else in EALayer3 */ + offset = read_32bit(block_offset+0x0c,sf); /* first value seems ok, second is something else in EALayer3 */ return block_offset + 0x0c + ea->channels*0x04 + offset; } else if (block_id == 0x00000000) { diff --git a/Frameworks/vgmstream/vgmstream/src/meta/fsb5.c b/Frameworks/vgmstream/vgmstream/src/meta/fsb5.c index 58107cd40..bd4a6c252 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/fsb5.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/fsb5.c @@ -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; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/imc.c b/Frameworks/vgmstream/vgmstream/src/meta/imc.c index 6c2b1300f..3ac6d6e42 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/imc.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/imc.c @@ -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; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/imuse.c b/Frameworks/vgmstream/vgmstream/src/meta/imuse.c new file mode 100644 index 000000000..5b6222cc0 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/imuse.c @@ -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; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/meta.h b/Frameworks/vgmstream/vgmstream/src/meta/meta.h index bc3e32cf4..5ba507cdd 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/meta.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/meta.h @@ -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*/ diff --git a/Frameworks/vgmstream/vgmstream/src/vgmstream.c b/Frameworks/vgmstream/vgmstream/src/vgmstream.c index 325ff5910..4960b7803 100644 --- a/Frameworks/vgmstream/vgmstream/src/vgmstream.c +++ b/Frameworks/vgmstream/vgmstream/src/vgmstream.c @@ -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; } } diff --git a/Frameworks/vgmstream/vgmstream/src/vgmstream.h b/Frameworks/vgmstream/vgmstream/src/vgmstream.h index a7a452435..bc2be360a 100644 --- a/Frameworks/vgmstream/vgmstream/src/vgmstream.h +++ b/Frameworks/vgmstream/vgmstream/src/vgmstream.h @@ -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);