diff --git a/README.md b/README.md index 613fab39..e5df35be 100644 --- a/README.md +++ b/README.md @@ -122,11 +122,12 @@ like foobar or Winamp don't react well to that, they may be renamed for vgmstream (mainly to get looping in some cases). - .ac3 to .lac3 - .aac to .laac +- .asf to .sng (EA formats) - .mp4 to .lmp4 - .ogg to .logg - .opus to .lopus - .stm to .lstm -- .wav to .lwav (or .xwav in rare original Xbox cases) +- .wav to .lwav Command line tools don't have this restriction and will accept the original filename. diff --git a/cli/vgmstream_cli.c b/cli/vgmstream_cli.c index 06b17ed4..1a4ee775 100644 --- a/cli/vgmstream_cli.c +++ b/cli/vgmstream_cli.c @@ -26,8 +26,7 @@ extern char * optarg; extern int optind, opterr, optopt; -static void make_wav_header(uint8_t * buf, int32_t sample_count, int32_t sample_rate, int channels); -static void make_smpl_chunk(uint8_t * buf, int32_t loop_start, int32_t loop_end); +static size_t make_wav_header(uint8_t * buf, size_t buf_size, int32_t sample_count, int32_t sample_rate, int channels, int smpl_chunk, int32_t loop_start, int32_t loop_end); static void usage(const char * name) { fprintf(stderr,"vgmstream CLI decoder " VERSION " " __DATE__ "\n" @@ -316,16 +315,23 @@ int main(int argc, char ** argv) { /* slap on a .wav header */ - if (only_stereo != -1) { - make_wav_header((uint8_t*)buf, len_samples, vgmstream->sample_rate, 2); - } else { - make_wav_header((uint8_t*)buf, len_samples, vgmstream->sample_rate, vgmstream->channels); + { + uint8_t wav_buf[0x100]; + int channels = (only_stereo != -1) ? 2 : vgmstream->channels; + int smpl_chunk = (write_lwav && vgmstream->loop_flag); + size_t bytes_done; + + bytes_done = make_wav_header(wav_buf,0x100, + len_samples, vgmstream->sample_rate, channels, + smpl_chunk, vgmstream->loop_start_sample, vgmstream->loop_end_sample); + + /* once "smpl" with loops is written we don't want actual file looping */ + if (smpl_chunk) { + vgmstream_force_loop(vgmstream, 0, 0,0); + } + + fwrite(wav_buf,sizeof(uint8_t),bytes_done,outfile); } - if (write_lwav && vgmstream->loop_flag) { // Adding space for smpl chunk at end - int32_t bytecount = get_32bitLE((uint8_t*)buf + 4); - put_32bitLE((uint8_t*)buf + 4, bytecount + 0x44); - } - fwrite(buf,1,0x2c,outfile); /* decode forever */ while (play_forever) { @@ -374,11 +380,6 @@ int main(int argc, char ** argv) { } } - /* Writing "smpl" chunck at the end */ - if (write_lwav && vgmstream->loop_flag) { - make_smpl_chunk((uint8_t*)buf, vgmstream->loop_start_sample, vgmstream->loop_end_sample); - fwrite(buf,1,0x44,outfile); - } fclose(outfile); outfile = NULL; @@ -389,9 +390,6 @@ int main(int argc, char ** argv) { fprintf(stderr,"failed to open %s for output\n",outfilename_reset); return EXIT_FAILURE; } - /* slap on a .wav header */ - make_wav_header((uint8_t*)buf, len_samples, vgmstream->sample_rate, vgmstream->channels); - fwrite(buf,1,0x2c,outfile); reset_vgmstream(vgmstream); @@ -409,6 +407,29 @@ int main(int argc, char ** argv) { vgmstream_force_loop(vgmstream, 0, 0,0); } + /* slap on a .wav header */ + { + uint8_t wav_buf[0x100]; + int channels = (only_stereo != -1) ? 2 : vgmstream->channels; + int smpl_chunk = (write_lwav && vgmstream->loop_flag); + size_t bytes_done; + + bytes_done = make_wav_header(wav_buf,0x100, + len_samples, vgmstream->sample_rate, channels, + smpl_chunk, vgmstream->loop_start_sample, vgmstream->loop_end_sample); + + /* once "smpl" with looping is written we don't want actual file looping */ + if (smpl_chunk) { + vgmstream_force_loop(vgmstream, 0, 0,0); + } + + if (write_lwav && vgmstream->loop_flag) { // Adding space for smpl chunk at end + int32_t bytecount = get_32bitLE((uint8_t*)buf + 4); + put_32bitLE((uint8_t*)buf + 4, bytecount + 0x44); + } + fwrite(wav_buf,sizeof(uint8_t),bytes_done,outfile); + } + /* decode */ for (i = 0; i < len_samples; i += BUFSIZE) { int toget=BUFSIZE; @@ -452,35 +473,6 @@ int main(int argc, char ** argv) { -/** - * make a header for PCM .wav - * buffer must be 0x2c bytes - */ -static void make_wav_header(uint8_t * buf, int32_t sample_count, int32_t sample_rate, int channels) { - size_t bytecount; - - bytecount = sample_count*channels*sizeof(sample); - - memcpy(buf+0, "RIFF", 4); /* RIFF header */ - put_32bitLE(buf+4, (int32_t)(bytecount+0x2c-8)); /* size of RIFF */ - - memcpy(buf+8, "WAVE", 4); /* WAVE header */ - - memcpy(buf+0xc, "fmt ", 4); /* WAVE fmt chunk */ - put_32bitLE(buf+0x10, 0x10); /* size of WAVE fmt chunk */ - put_16bitLE(buf+0x14, 1); /* compression code 1=PCM */ - put_16bitLE(buf+0x16, channels); /* channel count */ - put_32bitLE(buf+0x18, sample_rate); /* sample rate */ - put_32bitLE(buf+0x1c, sample_rate*channels*sizeof(sample)); /* bytes per second */ - put_16bitLE(buf+0x20, (int16_t)(channels*sizeof(sample))); /* block align */ - put_16bitLE(buf+0x22, sizeof(sample)*8); /* significant bits per sample */ - - /* PCM has no extra format bytes, so we don't even need to specify a count */ - - memcpy(buf+0x24, "data", 4); /* WAVE data chunk */ - put_32bitLE(buf+0x28, (int32_t)bytecount); /* size of WAVE data chunk */ -} - static void make_smpl_chunk(uint8_t * buf, int32_t loop_start, int32_t loop_end) { int i; @@ -500,3 +492,44 @@ static void make_smpl_chunk(uint8_t * buf, int32_t loop_start, int32_t loop_end) put_32bitLE(buf+60, 0); put_32bitLE(buf+64, 0); } + +/* make a RIFF header for .wav */ +static size_t make_wav_header(uint8_t * buf, size_t buf_size, int32_t sample_count, int32_t sample_rate, int channels, int smpl_chunk, int32_t loop_start, int32_t loop_end) { + size_t data_size, header_size; + + data_size = sample_count*channels*sizeof(sample); + header_size = 0x2c; + if (smpl_chunk) + header_size += 0x3c+ 0x08; + + if (header_size > buf_size) + goto fail; + + memcpy(buf+0x00, "RIFF", 4); /* RIFF header */ + put_32bitLE(buf+4, (int32_t)(header_size - 0x08 + data_size)); /* size of RIFF */ + + memcpy(buf+0x08, "WAVE", 4); /* WAVE header */ + + memcpy(buf+0x0c, "fmt ", 4); /* WAVE fmt chunk */ + put_32bitLE(buf+0x10, 0x10); /* size of WAVE fmt chunk */ + put_16bitLE(buf+0x14, 1); /* compression code 1=PCM */ + put_16bitLE(buf+0x16, channels); /* channel count */ + put_32bitLE(buf+0x18, sample_rate); /* sample rate */ + put_32bitLE(buf+0x1c, sample_rate*channels*sizeof(sample)); /* bytes per second */ + put_16bitLE(buf+0x20, (int16_t)(channels*sizeof(sample))); /* block align */ + put_16bitLE(buf+0x22, sizeof(sample)*8); /* significant bits per sample */ + + if (smpl_chunk) { + make_smpl_chunk(buf+0x24, loop_start, loop_end); + memcpy(buf+0x24+0x3c+0x08, "data", 0x04); /* WAVE data chunk */ + put_32bitLE(buf+0x28+0x3c+0x08, (int32_t)data_size); /* size of WAVE data chunk */ + } + else { + memcpy(buf+0x24, "data", 0x04); /* WAVE data chunk */ + put_32bitLE(buf+0x28, (int32_t)data_size); /* size of WAVE data chunk */ + } + + return header_size; +fail: + return 0; +} diff --git a/src/coding/coding.h b/src/coding/coding.h index 4485c515..308abdb9 100644 --- a/src/coding/coding.h +++ b/src/coding/coding.h @@ -59,7 +59,6 @@ void decode_ngc_afc(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspaci /* pcm_decoder */ void decode_pcm16LE(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); -void decode_pcm16LE_XOR_int(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); void decode_pcm16BE(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); void decode_pcm16_int(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int big_endian); void decode_pcm8(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); @@ -76,7 +75,6 @@ size_t pcm_bytes_to_samples(size_t bytes, int channels, int bits_per_sample); /* psx_decoder */ void decode_psx(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); void decode_psx_badflags(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); -void decode_psx_bmdx(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); void decode_psx_configurable(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int frame_size); void decode_hevag(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); size_t ps_bytes_to_samples(size_t bytes, int channels); @@ -118,9 +116,10 @@ void decode_msadpcm_mono(VGMSTREAM * vgmstream, sample * outbuf, int32_t first_s long msadpcm_bytes_to_samples(long bytes, int block_size, int channels); /* yamaha_decoder */ -void decode_aica(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); +void decode_aica(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, int is_stereo); void decode_yamaha(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); void decode_yamaha_nxap(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); +size_t aica_bytes_to_samples(size_t bytes, int channels); size_t yamaha_bytes_to_samples(size_t bytes, int channels); /* nds_procyon_decoder */ diff --git a/src/coding/coding_utils.c b/src/coding/coding_utils.c index 937b608c..466054b4 100644 --- a/src/coding/coding_utils.c +++ b/src/coding/coding_utils.c @@ -297,15 +297,9 @@ fail: int ffmpeg_make_riff_xma2_from_xma2_chunk(uint8_t * buf, size_t buf_size, off_t xma2_offset, size_t xma2_size, size_t data_size, STREAMFILE *streamFile) { uint8_t chunk[0x100]; size_t riff_size; - size_t xma2_final_size = xma2_size; - int xma2_chunk_version = read_8bit(xma2_offset,streamFile); - /* FFmpeg can't parse v3 "XMA2" chunks so we'll have to extend (8 bytes in the middle) */ - if (xma2_chunk_version == 3) - xma2_final_size += 0x8; - riff_size = 4+4+ 4 + 4+4+xma2_final_size + 4+4; - - if (buf_size < riff_size || xma2_final_size > 0x100) + riff_size = 4+4+ 4 + 4+4+xma2_size + 4+4; + if (buf_size < riff_size || xma2_size > 0x100) goto fail; if (read_streamfile(chunk,xma2_offset,xma2_size, streamFile) != xma2_size) goto fail; @@ -316,20 +310,11 @@ int ffmpeg_make_riff_xma2_from_xma2_chunk(uint8_t * buf, size_t buf_size, off_t memcpy(buf+0x08, "WAVE", 4); memcpy(buf+0x0c, "XMA2", 4); - put_32bitLE(buf+0x10, xma2_final_size); - if (xma2_chunk_version == 3) { - /* old XMA2 v3: change to v4 (extra 8 bytes in the middle); always BE */ - put_8bit (buf+0x14 + 0x00, 4); /* v4 */ - memcpy (buf+0x14 + 0x01, chunk+1, 0xF); /* first v3 part (fixed) */ - put_32bitBE(buf+0x14 + 0x10, 0x000010D6); /* extra v4 BE: "EncodeOptions" (not used by FFmpeg) */ - put_32bitBE(buf+0x14 + 0x14, 0); /* extra v4 BE: "PsuedoBytesPerSec" (not used by FFmpeg) */ - memcpy (buf+0x14 + 0x18, chunk+0x10, xma2_size - 0x10); /* second v3 part (variable size) */ - } else { - memcpy(buf+0x14, chunk, xma2_size); - } + put_32bitLE(buf+0x10, xma2_size); + memcpy(buf+0x14, chunk, xma2_size); - memcpy(buf+0x14+xma2_final_size, "data", 4); - put_32bitLE(buf+0x14+xma2_final_size+4, data_size); /* data size */ + memcpy(buf+0x14+xma2_size, "data", 4); + put_32bitLE(buf+0x14+xma2_size+4, data_size); /* data size */ return riff_size; diff --git a/src/coding/ffmpeg_decoder.c b/src/coding/ffmpeg_decoder.c index 9a0f8f40..f9ef42f0 100644 --- a/src/coding/ffmpeg_decoder.c +++ b/src/coding/ffmpeg_decoder.c @@ -364,6 +364,10 @@ ffmpeg_codec_data * init_ffmpeg_config(STREAMFILE *streamFile, uint8_t * header, memcpy(&data->config, config, sizeof(ffmpeg_custom_config)); } + /* ignore bad combos */ + if ((header && !header_size) || (!header && header_size)) + goto fail; + /* fake header to trick FFmpeg into demuxing/decoding the stream */ if (header_size > 0) { data->header_size = header_size; diff --git a/src/coding/pcm_decoder.c b/src/coding/pcm_decoder.c index c2f3732f..406f0dff 100644 --- a/src/coding/pcm_decoder.c +++ b/src/coding/pcm_decoder.c @@ -78,15 +78,6 @@ void decode_pcm16_int(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspa } } -void decode_pcm16LE_XOR_int(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) { - int i; - int32_t sample_count; - - for (i=first_sample,sample_count=0; ioffset+i*2*channelspacing,stream->streamfile)^stream->key_xor; - } -} - static int expand_ulaw(uint8_t ulawbyte) { int sign, segment, quantization, new_sample; const int bias = 0x84; diff --git a/src/coding/psx_decoder.c b/src/coding/psx_decoder.c index a85250ad..4ed91832 100644 --- a/src/coding/psx_decoder.c +++ b/src/coding/psx_decoder.c @@ -110,52 +110,6 @@ void decode_psx(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, stream->adpcm_history2_32=hist2; } -/* encrypted */ -void decode_psx_bmdx(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) { - - int predict_nr, shift_factor, sample; - int32_t hist1=stream->adpcm_history1_32; - int32_t hist2=stream->adpcm_history2_32; - - short scale; - int i; - int32_t sample_count; - uint8_t flag; - - int framesin = first_sample/28; - int head = read_8bit(stream->offset+framesin*16,stream->streamfile) ^ stream->bmdx_xor; - - predict_nr = ((head >> 4) & 0xf); - shift_factor = (head & 0xf); - flag = read_8bit(stream->offset+framesin*16+1,stream->streamfile); - - first_sample = first_sample % 28; - - for (i=first_sample,sample_count=0; ioffset+(framesin*16)+2+i/2,stream->streamfile); - if (i/2 == 0) - sample_byte = (short)(int8_t)(sample_byte+stream->bmdx_add); - - scale = ((i&1 ? - sample_byte >> 4 : - sample_byte & 0x0f)<<12); - - sample=(int)((scale >> shift_factor)+hist1*VAG_f[predict_nr][0]+hist2*VAG_f[predict_nr][1]); - } - - outbuf[sample_count] = clamp16(sample); - hist2=hist1; - hist1=sample; - } - stream->adpcm_history1_32=hist1; - stream->adpcm_history2_32=hist2; -} - /* some games have garbage (?) in their flags, this decoder just ignores that byte */ void decode_psx_badflags(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) { diff --git a/src/coding/yamaha_decoder.c b/src/coding/yamaha_decoder.c index 9ee92b97..15b1e9af 100644 --- a/src/coding/yamaha_decoder.c +++ b/src/coding/yamaha_decoder.c @@ -15,8 +15,8 @@ static const int scale_delta[16] = { }; -/* Yamaha AICA ADPCM, as seen in Dreamcast. Possibly like RIFF codec 0x20 or used in older arcade sound chips. */ -void decode_aica(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) { +/* raw Yamaha ADPCM a.k.a AICA as it's mainly used in Naomi/Dreamcast (also in RIFF and older arcade sound chips). */ +void decode_aica(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, int is_stereo) { int i, sample_count; int32_t hist1 = stream->adpcm_history1_16; @@ -28,8 +28,12 @@ void decode_aica(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, for (i=first_sample,sample_count=0; ioffset) + i/2; - int nibble_shift = (i&1?4:0); /* low nibble first */ + off_t byte_offset = is_stereo ? + stream->offset + i : /* stereo: one nibble per channel */ + stream->offset + i/2; /* mono: consecutive nibbles */ + int nibble_shift = is_stereo ? + (!(channel&1) ? 0:4) : /* even = low/L, odd = high/R */ + (!(i&1) ? 0:4); /* low nibble first */ /* Yamaha/AICA expand, but same result as IMA's (((delta * 2 + 1) * step) >> 3) */ sample_nibble = ((read_8bit(byte_offset,stream->streamfile) >> nibble_shift))&0xf; @@ -141,6 +145,11 @@ void decode_yamaha_nxap(VGMSTREAMCHANNEL * stream, sample * outbuf, int channels stream->adpcm_step_index = step_size; } +size_t aica_bytes_to_samples(size_t bytes, int channels) { + /* 2 samples per byte (2 nibbles) in stereo or mono config */ + return bytes * 2 / channels; +} + size_t yamaha_bytes_to_samples(size_t bytes, int channels) { int block_align = 0x40; diff --git a/src/formats.c b/src/formats.c index bffa52ef..015fdf57 100644 --- a/src/formats.c +++ b/src/formats.c @@ -183,6 +183,7 @@ static const char* extension_list[] = { "matx", "mc3", "mca", + "mcadpcm", "mcg", "mds", "mdsp", @@ -201,6 +202,7 @@ static const char* extension_list[] = { "msd", "msf", "mss", + "msv", //txh/reserved [Fight Club (PS2)] "msvp", "mta2", "mtaf", @@ -349,6 +351,7 @@ static const char* extension_list[] = { "vgs", "vgv", "vig", + "vis", //txth/reserved [AirForce Delta (PS2)] "vms", "voi", "vpk", @@ -376,6 +379,7 @@ static const char* extension_list[] = { "wsd", "wsi", "wv2", //txth/reserved [Slave Zero (PC)] + "wve", "wvs", "xa", @@ -437,7 +441,6 @@ typedef struct { static const coding_info coding_info_list[] = { {coding_PCM16LE, "Little Endian 16-bit PCM"}, - {coding_PCM16LE_XOR_int, "Little Endian 16-bit PCM with 2 byte interleave and XOR obfuscation"}, {coding_PCM16BE, "Big Endian 16-bit PCM"}, {coding_PCM16_int, "16-bit PCM with 2 byte interleave (block)"}, {coding_PCM8, "8-bit PCM"}, @@ -466,7 +469,6 @@ static const coding_info coding_info_list[] = { {coding_XA, "CD-ROM XA 4-bit ADPCM"}, {coding_PSX, "Playstation 4-bit ADPCM"}, {coding_PSX_badflags, "Playstation 4-bit ADPCM (bad flags)"}, - {coding_PSX_bmdx, "Playstation 4-bit ADPCM (BMDX encryption)"}, {coding_PSX_cfg, "Playstation 4-bit ADPCM (configurable)"}, {coding_HEVAG, "Playstation Vita HEVAG 4-bit ADPCM"}, @@ -501,6 +503,7 @@ static const coding_info coding_info_list[] = { {coding_MSADPCM, "Microsoft 4-bit ADPCM"}, {coding_WS, "Westwood Studios VBR ADPCM"}, {coding_AICA, "Yamaha AICA 4-bit ADPCM"}, + {coding_AICA_int, "Yamaha AICA 4-bit ADPCM (mono/interleave)"}, {coding_YAMAHA, "Yamaha 4-bit ADPCM"}, {coding_YAMAHA_NXAP, "Yamaha NXAP 4-bit ADPCM"}, {coding_NDS_PROCYON, "Procyon Studio Digital Sound Elements NDS 4-bit APDCM"}, @@ -557,42 +560,45 @@ static const layout_info layout_info_list[] = { {layout_interleave, "interleave"}, {layout_segmented, "segmented"}, - {layout_aix, "AIX interleave, internally 18-byte interleaved"}, - {layout_scd_int, "SCD multistream interleave"}, + {layout_layered, "layered"}, + {layout_aix, "AIX"}, - {layout_mxch_blocked, "MxCh blocked"}, - {layout_ast_blocked, "AST blocked"}, - {layout_halpst_blocked, "HALPST blocked"}, - {layout_xa_blocked, "CD-ROM XA"}, + {layout_blocked_mxch, "blocked (MxCh)"}, + {layout_blocked_ast, "blocked (AST)"}, + {layout_blocked_halpst, "blocked (HALPST)"}, + {layout_blocked_xa, "blocked (XA)"}, {layout_blocked_ea_schl, "blocked (EA SCHl)"}, {layout_blocked_ea_1snh, "blocked (EA 1SNh)"}, {layout_blocked_caf, "blocked (CAF)"}, {layout_blocked_wsi, "blocked (WSI)"}, - {layout_xvas_blocked, ".xvas blocked"}, - {layout_str_snds_blocked, ".str SNDS blocked"}, - {layout_ws_aud_blocked, "Westwood Studios .aud blocked"}, - {layout_matx_blocked, "Matrix .matx blocked"}, + {layout_blocked_xvas, "blocked (.xvas)"}, + {layout_blocked_str_snds, "blocked (.str SNDS)"}, + {layout_blocked_ws_aud, "blocked (Westwood Studios .aud)"}, + {layout_blocked_matx, "blocked (Matrix .matx)"}, {layout_blocked_dec, "blocked (DEC)"}, - {layout_vs_blocked, "vs blocked"}, - {layout_emff_ps2_blocked, "EMFF (PS2) blocked"}, - {layout_emff_ngc_blocked, "EMFF (NGC/WII) blocked"}, - {layout_gsb_blocked, "GSB blocked"}, - {layout_thp_blocked, "THP Movie Audio blocked"}, - {layout_filp_blocked, "FILp blocked"}, + {layout_blocked_vs, "blocked (vs)"}, + {layout_blocked_emff_ps2, "blocked (EMFF PS2)"}, + {layout_blocked_emff_ngc, "blocked (EMFF NGC)"}, + {layout_blocked_gsb, "blocked (GSB)"}, + {layout_blocked_thp, "blocked (THP Movie Audio)"}, + {layout_blocked_filp, "blocked (FILP)"}, {layout_blocked_ea_swvr, "blocked (EA SWVR)"}, {layout_blocked_adm, "blocked (ADM)"}, - {layout_dsp_bdsp_blocked, "DSP blocked"}, + {layout_blocked_bdsp, "blocked (BDSP)"}, {layout_blocked_ivaud, "blocked (IVAUD)"}, - {layout_ps2_iab_blocked, "IAB blocked"}, - {layout_ps2_strlr_blocked, "The Bouncer STR blocked"}, + {layout_blocked_ps2_iab, "blocked (IAB)"}, + {layout_blocked_ps2_strlr, "blocked (The Bouncer STR)"}, {layout_blocked_rws, "blocked (RWS)"}, {layout_blocked_hwas, "blocked (HWAS)"}, - {layout_tra_blocked, "TRA blocked"}, + {layout_blocked_tra, "blocked (TRA)"}, {layout_blocked_ea_sns, "blocked (EA SNS)"}, {layout_blocked_awc, "blocked (AWC)"}, {layout_blocked_vgs, "blocked (VGS)"}, {layout_blocked_vawx, "blocked (VAWX)"}, {layout_blocked_xvag_subsong, "blocked (XVAG subsong)"}, + {layout_blocked_ea_wve_au00, "blocked (EA WVE au00)"}, + {layout_blocked_ea_wve_ad10, "blocked (EA WVE Ad10)"}, + {layout_blocked_sthd, "blocked (STHD)"}, }; static const meta_info meta_info_list[] = { @@ -753,7 +759,7 @@ static const meta_info meta_info_list[] = { {meta_RSD6AT3P, "Radical RSD6/AT3+ header"}, {meta_RSD6WMA, "Radical RSD6/WMA header"}, {meta_DC_ASD, "ASD Header"}, - {meta_NAOMI_SPSD, "SPSD Header"}, + {meta_NAOMI_SPSD, "Naomi SPSD header"}, {meta_FFXI_BGW, "BGW BGMStream header"}, {meta_FFXI_SPW, "SPW SeWave header"}, {meta_PS2_ASS, "ASS Header"}, @@ -840,7 +846,7 @@ static const meta_info meta_info_list[] = { {meta_PS2_SMPL, "Homura SMPL header"}, {meta_PS2_MSA, "Psyvariar -Complete Edition- MSA header"}, {meta_PC_SMP, "Ghostbusters .smp Header"}, - {meta_NGC_PDT, "PDT DSP header"}, + {meta_NGC_PDT, "Hudson .PDT header"}, {meta_NGC_RKV, "Legacy of Kain - Blood Omen 2 RKV GC header"}, {meta_DSP_DDSP, ".DDSP header"}, {meta_P3D, "Radical P3D header"}, @@ -913,7 +919,7 @@ static const meta_info meta_info_list[] = { {meta_XB3D_ADX, "Xenoblade 3D ADX header"}, {meta_HCA, "CRI MiddleWare HCA Header"}, {meta_PS2_SVAG_SNK, "SNK SVAG header"}, - {meta_PS2_VDS_VDM, "Graffiti Kingdom VDS/VDM header"}, + {meta_PS2_VDS_VDM, "Procyon Studio VDS/VDM header"}, {meta_X360_CXS, "tri-Crescendo CXS header"}, {meta_AKB, "Square-Enix AKB header"}, {meta_NUB_XMA, "Namco NUB XMA header"}, @@ -972,10 +978,13 @@ static const meta_info meta_info_list[] = { {meta_WAVE_segmented, "EngineBlack .WAVE header (segmented)"}, {meta_SMV, "Cho Aniki Zero .SMV header"}, {meta_NXAP, "Nex NXAP header"}, + {meta_EA_WVE_AU00, "Electronic Arts WVE (au00) header"}, + {meta_EA_WVE_AD10, "Electronic Arts WVE (Ad10) header"}, + {meta_STHD, "Dream Factory STHD header"}, + {meta_MP4, "MP4/AAC header"}, + {meta_PCM_SRE, "Capcom .PCM+SRE header"}, + {meta_DSP_MCADPCM, "Bethesda .mcadpcm header"}, -#ifdef VGM_USE_MP4V2 - {meta_MP4, "AAC header"}, -#endif #ifdef VGM_USE_FFMPEG {meta_FFmpeg, "FFmpeg supported file format"}, #endif diff --git a/src/layout/blocked.c b/src/layout/blocked.c index ea369fc9..16b0ce06 100644 --- a/src/layout/blocked.c +++ b/src/layout/blocked.c @@ -65,20 +65,20 @@ void render_vgmstream_blocked(sample * buffer, int32_t sample_count, VGMSTREAM * if (vgmstream->samples_into_block==samples_this_block /*&& vgmstream->current_sample < vgmstream->num_samples*/) { /* don't go past last block */ switch (vgmstream->layout_type) { - case layout_ast_blocked: - ast_block_update(vgmstream->next_block_offset,vgmstream); + case layout_blocked_ast: + block_update_ast(vgmstream->next_block_offset,vgmstream); break; - case layout_mxch_blocked: - mxch_block_update(vgmstream->next_block_offset,vgmstream); + case layout_blocked_mxch: + block_update_mxch(vgmstream->next_block_offset,vgmstream); break; - case layout_halpst_blocked: + case layout_blocked_halpst: if (vgmstream->next_block_offset>=0) - halpst_block_update(vgmstream->next_block_offset,vgmstream); + block_update_halpst(vgmstream->next_block_offset,vgmstream); else vgmstream->current_block_offset = -1; break; - case layout_xa_blocked: - xa_block_update(vgmstream->next_block_offset,vgmstream); + case layout_blocked_xa: + block_update_xa(vgmstream->next_block_offset,vgmstream); break; case layout_blocked_ea_schl: block_update_ea_schl(vgmstream->next_block_offset,vgmstream); @@ -92,38 +92,38 @@ void render_vgmstream_blocked(sample * buffer, int32_t sample_count, VGMSTREAM * case layout_blocked_wsi: block_update_wsi(vgmstream->next_block_offset,vgmstream); break; - case layout_str_snds_blocked: - str_snds_block_update(vgmstream->next_block_offset,vgmstream); + case layout_blocked_str_snds: + block_update_str_snds(vgmstream->next_block_offset,vgmstream); break; - case layout_ws_aud_blocked: - ws_aud_block_update(vgmstream->next_block_offset,vgmstream); + case layout_blocked_ws_aud: + block_update_ws_aud(vgmstream->next_block_offset,vgmstream); break; - case layout_matx_blocked: - matx_block_update(vgmstream->next_block_offset,vgmstream); + case layout_blocked_matx: + block_update_matx(vgmstream->next_block_offset,vgmstream); break; case layout_blocked_dec: block_update_dec(vgmstream->next_block_offset,vgmstream); break; - case layout_emff_ps2_blocked: - emff_ps2_block_update(vgmstream->next_block_offset,vgmstream); + case layout_blocked_emff_ps2: + block_update_emff_ps2(vgmstream->next_block_offset,vgmstream); break; - case layout_emff_ngc_blocked: - emff_ngc_block_update(vgmstream->next_block_offset,vgmstream); + case layout_blocked_emff_ngc: + block_update_emff_ngc(vgmstream->next_block_offset,vgmstream); break; - case layout_gsb_blocked: - gsb_block_update(vgmstream->next_block_offset,vgmstream); + case layout_blocked_gsb: + block_update_gsb(vgmstream->next_block_offset,vgmstream); break; - case layout_vs_blocked: - vs_block_update(vgmstream->next_block_offset,vgmstream); + case layout_blocked_vs: + block_update_vs(vgmstream->next_block_offset,vgmstream); break; - case layout_xvas_blocked: - xvas_block_update(vgmstream->next_block_offset,vgmstream); + case layout_blocked_xvas: + block_update_xvas(vgmstream->next_block_offset,vgmstream); break; - case layout_thp_blocked: - thp_block_update(vgmstream->next_block_offset,vgmstream); + case layout_blocked_thp: + block_update_thp(vgmstream->next_block_offset,vgmstream); break; - case layout_filp_blocked: - filp_block_update(vgmstream->next_block_offset,vgmstream); + case layout_blocked_filp: + block_update_filp(vgmstream->next_block_offset,vgmstream); break; case layout_blocked_ivaud: block_update_ivaud(vgmstream->next_block_offset,vgmstream); @@ -134,17 +134,17 @@ void render_vgmstream_blocked(sample * buffer, int32_t sample_count, VGMSTREAM * case layout_blocked_adm: block_update_adm(vgmstream->next_block_offset,vgmstream); break; - case layout_dsp_bdsp_blocked: - dsp_bdsp_block_update(vgmstream->next_block_offset,vgmstream); + case layout_blocked_bdsp: + block_update_bdsp(vgmstream->next_block_offset,vgmstream); break; - case layout_tra_blocked: - tra_block_update(vgmstream->next_block_offset,vgmstream); + case layout_blocked_tra: + block_update_tra(vgmstream->next_block_offset,vgmstream); break; - case layout_ps2_iab_blocked: - ps2_iab_block_update(vgmstream->next_block_offset,vgmstream); + case layout_blocked_ps2_iab: + block_update_ps2_iab(vgmstream->next_block_offset,vgmstream); break; - case layout_ps2_strlr_blocked: - ps2_strlr_block_update(vgmstream->next_block_offset,vgmstream); + case layout_blocked_ps2_strlr: + block_update_ps2_strlr(vgmstream->next_block_offset,vgmstream); break; case layout_blocked_rws: block_update_rws(vgmstream->next_block_offset,vgmstream); @@ -167,6 +167,15 @@ void render_vgmstream_blocked(sample * buffer, int32_t sample_count, VGMSTREAM * case layout_blocked_xvag_subsong: block_update_xvag_subsong(vgmstream->next_block_offset,vgmstream); break; + case layout_blocked_ea_wve_au00: + block_update_ea_wve_au00(vgmstream->next_block_offset,vgmstream); + break; + case layout_blocked_ea_wve_ad10: + block_update_ea_wve_ad10(vgmstream->next_block_offset,vgmstream); + break; + case layout_blocked_sthd: + block_update_sthd(vgmstream->next_block_offset,vgmstream); + break; default: break; } diff --git a/src/layout/ast_blocked.c b/src/layout/blocked_ast.c similarity index 90% rename from src/layout/ast_blocked.c rename to src/layout/blocked_ast.c index d882a0de..8ff32ba0 100644 --- a/src/layout/ast_blocked.c +++ b/src/layout/blocked_ast.c @@ -2,7 +2,7 @@ #include "../vgmstream.h" /* set up for the block at the given offset */ -void ast_block_update(off_t block_offset, VGMSTREAM * vgmstream) { +void block_update_ast(off_t block_offset, VGMSTREAM * vgmstream) { int i; vgmstream->current_block_offset = block_offset; vgmstream->current_block_size = read_32bitBE( diff --git a/src/layout/bdsp_blocked.c b/src/layout/blocked_bdsp.c similarity index 88% rename from src/layout/bdsp_blocked.c rename to src/layout/blocked_bdsp.c index 88f640b6..f5e763ab 100644 --- a/src/layout/bdsp_blocked.c +++ b/src/layout/blocked_bdsp.c @@ -2,7 +2,7 @@ #include "../vgmstream.h" /* set up for the block at the given offset */ -void dsp_bdsp_block_update(off_t block_offset, VGMSTREAM * vgmstream) { +void block_update_bdsp(off_t block_offset, VGMSTREAM * vgmstream) { int i; vgmstream->current_block_offset = block_offset; diff --git a/src/layout/blocked_ea_schl.c b/src/layout/blocked_ea_schl.c index eea96ffd..68e4ffef 100644 --- a/src/layout/blocked_ea_schl.c +++ b/src/layout/blocked_ea_schl.c @@ -21,7 +21,7 @@ void block_update_ea_schl(off_t block_offset, VGMSTREAM * vgmstream) { block_samples = 0; - if (id == 0x5343446C || id == 0x5344454E) { /* "SCDl" "SDEN" audio data */ + if (id == 0x5343446C || id == 0x5344454E || id == 0x53444652) { /* "SCDl" "SDEN" "SDFR" audio data */ switch(vgmstream->coding_type) { case coding_PSX: block_samples = ps_bytes_to_samples(block_size-0x10, vgmstream->channels); @@ -37,7 +37,7 @@ void block_update_ea_schl(off_t block_offset, VGMSTREAM * vgmstream) { block_size = 0x04; } - if (id == 0x5343486C || id == 0x5348454E) { /* "SCHl" "SHEN" end block */ + if (id == 0x5343486C || id == 0x5348454E || id == 0x53484652) { /* "SCHl" "SHEN" "SHFR" end block */ new_schl = 1; } } @@ -53,8 +53,8 @@ void block_update_ea_schl(off_t block_offset, VGMSTREAM * vgmstream) { break; block_offset += block_size; - /* "SCEl" are aligned to 0x80 usually, but causes problems if not 32b-aligned (ex. Need for Speed 2 PC) */ - if ((id == 0x5343456C || id == 0x5345454E) && block_offset % 0x04) { + /* "SCEl" "SEEN" "SEFR" are aligned to 0x80 usually, but causes problems if not 32b-aligned (ex. Need for Speed 2 PC) */ + if ((id == 0x5343456C || id == 0x5345454E || id == 0x53454652) && block_offset % 0x04) { block_offset += 0x04 - (block_offset % 0x04); } } diff --git a/src/layout/blocked_ea_wve_ad10.c b/src/layout/blocked_ea_wve_ad10.c new file mode 100644 index 00000000..d57615fe --- /dev/null +++ b/src/layout/blocked_ea_wve_ad10.c @@ -0,0 +1,42 @@ +#include "layout.h" +#include "../coding/coding.h" + + +/* EA style blocks, one block per channel when stereo */ +void block_update_ea_wve_ad10(off_t block_offset, VGMSTREAM * vgmstream) { + STREAMFILE* streamFile = vgmstream->ch[0].streamfile; + int i; + size_t block_size = 0; + size_t file_size = get_streamfile_size(streamFile); + + + while (block_offset < file_size) { + uint32_t block_id = read_32bitBE(block_offset+0x00, streamFile); + + block_size = read_32bitBE(block_offset+0x04, streamFile); + + if (block_id == 0x41643130 || block_id == 0x41643131) { /* "Ad10/Ad11" audio block/footer found */ + break; + } + /* rest may be "MDEC" video blocks */ + + block_offset += block_size; + } + + /* EOF reads (unsure if this helps) */ + if (block_offset >= file_size) { + vgmstream->current_block_offset = block_offset; + vgmstream->next_block_offset = block_offset + 0x04; + vgmstream->current_block_size = 0; + return; + } + + /* set offsets */ + for (i = 0; i < vgmstream->channels; i++) { + vgmstream->ch[i].offset = block_offset + block_size*i + 0x08; + } + + vgmstream->current_block_size = block_size - 0x08; /* one block per channel */ + vgmstream->current_block_offset = block_offset; + vgmstream->next_block_offset = block_offset + block_size*vgmstream->channels; +} diff --git a/src/layout/blocked_ea_wve_au00.c b/src/layout/blocked_ea_wve_au00.c new file mode 100644 index 00000000..ccaafa6c --- /dev/null +++ b/src/layout/blocked_ea_wve_au00.c @@ -0,0 +1,37 @@ +#include "layout.h" +#include "../coding/coding.h" + + +/* EA style blocks */ +void block_update_ea_wve_au00(off_t block_offset, VGMSTREAM * vgmstream) { + STREAMFILE* streamFile = vgmstream->ch[0].streamfile; + int i; + size_t block_size = 0, interleave; + size_t file_size = get_streamfile_size(streamFile); + + + while (block_offset < file_size) { + uint32_t block_id = read_32bitBE(block_offset+0x00, streamFile); + + block_size = read_32bitBE(block_offset+0x04, streamFile); + + if (block_id == 0x61753030 || block_id == 0x61753031) { /* "au00/au01" audio block/footer found */ + break; + } + /* rest may be "MDEC" video blocks */ + + block_offset += block_size; + } + + /* size adjusted to frame boundaries as blocks have padding */ + interleave = ((block_size - 0x10) / vgmstream->interleave_block_size * vgmstream->interleave_block_size) / vgmstream->channels; + + /* set offsets */ + for (i = 0; i < vgmstream->channels; i++) { + vgmstream->ch[i].offset = (block_offset + 0x10) + interleave*i; + } + + vgmstream->current_block_size = interleave; + vgmstream->current_block_offset = block_offset; + vgmstream->next_block_offset = block_offset + block_size; +} diff --git a/src/layout/emff_blocked.c b/src/layout/blocked_emff.c similarity index 89% rename from src/layout/emff_blocked.c rename to src/layout/blocked_emff.c index b3ed7f5f..41ad197f 100644 --- a/src/layout/emff_blocked.c +++ b/src/layout/blocked_emff.c @@ -2,7 +2,7 @@ #include "../vgmstream.h" /* set up for the block at the given offset */ -void emff_ps2_block_update(off_t block_offset, VGMSTREAM * vgmstream) { +void block_update_emff_ps2(off_t block_offset, VGMSTREAM * vgmstream) { int i; vgmstream->current_block_offset = block_offset; @@ -17,7 +17,7 @@ void emff_ps2_block_update(off_t block_offset, VGMSTREAM * vgmstream) { } } -void emff_ngc_block_update(off_t block_offset, VGMSTREAM * vgmstream) { +void block_update_emff_ngc(off_t block_offset, VGMSTREAM * vgmstream) { int i; vgmstream->current_block_offset = block_offset; diff --git a/src/layout/filp_blocked.c b/src/layout/blocked_filp.c similarity index 90% rename from src/layout/filp_blocked.c rename to src/layout/blocked_filp.c index 550c6e73..6baea362 100644 --- a/src/layout/filp_blocked.c +++ b/src/layout/blocked_filp.c @@ -2,7 +2,7 @@ #include "../vgmstream.h" /* set up for the block at the given offset */ -void filp_block_update(off_t block_offset, VGMSTREAM * vgmstream) { +void block_update_filp(off_t block_offset, VGMSTREAM * vgmstream) { int i; vgmstream->current_block_offset = block_offset; diff --git a/src/layout/gsb_blocked.c b/src/layout/blocked_gsb.c similarity index 93% rename from src/layout/gsb_blocked.c rename to src/layout/blocked_gsb.c index eccd40d8..29a41771 100644 --- a/src/layout/gsb_blocked.c +++ b/src/layout/blocked_gsb.c @@ -2,7 +2,7 @@ #include "../vgmstream.h" /* set up for the block at the given offset */ -void gsb_block_update(off_t block_offset, VGMSTREAM * vgmstream) { +void block_update_gsb(off_t block_offset, VGMSTREAM * vgmstream) { int i; int block_header_size = 0x20; /*from header*/ int block_channel_size = 0x8000; /*from header, per channel*/ diff --git a/src/layout/halpst_blocked.c b/src/layout/blocked_halpst.c similarity index 92% rename from src/layout/halpst_blocked.c rename to src/layout/blocked_halpst.c index 7c36c1bf..d9ec3763 100644 --- a/src/layout/halpst_blocked.c +++ b/src/layout/blocked_halpst.c @@ -2,7 +2,7 @@ #include "../vgmstream.h" /* set up for the block at the given offset */ -void halpst_block_update(off_t block_offset, VGMSTREAM * vgmstream) { +void block_update_halpst(off_t block_offset, VGMSTREAM * vgmstream) { int i, header_length; /* header length must be a multiple of 0x20 */ header_length = (4+8*vgmstream->channels+0x1f)/0x20*0x20; diff --git a/src/layout/ims_block.c b/src/layout/blocked_matx.c similarity index 90% rename from src/layout/ims_block.c rename to src/layout/blocked_matx.c index c5df5948..e69a0503 100644 --- a/src/layout/ims_block.c +++ b/src/layout/blocked_matx.c @@ -2,7 +2,7 @@ #include "../vgmstream.h" /* set up for the block at the given offset */ -void matx_block_update(off_t block_offset, VGMSTREAM * vgmstream) { +void block_update_matx(off_t block_offset, VGMSTREAM * vgmstream) { int i; vgmstream->current_block_offset = block_offset; diff --git a/src/layout/mxch_blocked.c b/src/layout/blocked_mxch.c similarity index 91% rename from src/layout/mxch_blocked.c rename to src/layout/blocked_mxch.c index 8e49a4a9..f15afbcb 100644 --- a/src/layout/mxch_blocked.c +++ b/src/layout/blocked_mxch.c @@ -2,7 +2,7 @@ #include "../vgmstream.h" //MxCh blocked layout as used by Lego Island -void mxch_block_update(off_t block_offset, VGMSTREAM * vgmstream) { +void block_update_mxch(off_t block_offset, VGMSTREAM * vgmstream) { vgmstream->current_block_offset = block_offset; vgmstream->next_block_offset = block_offset + read_32bitLE(vgmstream->current_block_offset+4,vgmstream->ch[0].streamfile)+8; diff --git a/src/layout/ps2_iab_blocked.c b/src/layout/blocked_ps2_iab.c similarity index 90% rename from src/layout/ps2_iab_blocked.c rename to src/layout/blocked_ps2_iab.c index 0b375b1e..e91c94a4 100644 --- a/src/layout/ps2_iab_blocked.c +++ b/src/layout/blocked_ps2_iab.c @@ -2,7 +2,7 @@ #include "../vgmstream.h" /* set up for the block at the given offset */ -void ps2_iab_block_update(off_t block_offset, VGMSTREAM * vgmstream) { +void block_update_ps2_iab(off_t block_offset, VGMSTREAM * vgmstream) { int i; vgmstream->current_block_offset = block_offset; diff --git a/src/layout/ps2_strlr_blocked.c b/src/layout/blocked_ps2_strlr.c similarity index 89% rename from src/layout/ps2_strlr_blocked.c rename to src/layout/blocked_ps2_strlr.c index 7bf23bda..d2834db5 100644 --- a/src/layout/ps2_strlr_blocked.c +++ b/src/layout/blocked_ps2_strlr.c @@ -2,7 +2,7 @@ #include "../vgmstream.h" /* set up for the block at the given offset */ -void ps2_strlr_block_update(off_t block_offset, VGMSTREAM * vgmstream) { +void block_update_ps2_strlr(off_t block_offset, VGMSTREAM * vgmstream) { int i; vgmstream->current_block_offset = block_offset; diff --git a/src/layout/blocked_sthd.c b/src/layout/blocked_sthd.c new file mode 100644 index 00000000..2d9d9fbc --- /dev/null +++ b/src/layout/blocked_sthd.c @@ -0,0 +1,22 @@ +#include "layout.h" + +/* Dream Factory STHD blocks */ +void block_update_sthd(off_t block_offset, VGMSTREAM * vgmstream) { + STREAMFILE* streamFile = vgmstream->ch[0].streamfile; + size_t block_size, channel_size; + off_t data_offset; + int i; + + block_size = 0x800; + data_offset = read_16bitLE(block_offset + 0x04, streamFile); + channel_size = read_16bitLE(block_offset + 0x16, streamFile); + /* 0x06: num channels, 0x10: total blocks, 0x12: block count, 0x14(2): null, 0x18: block count + 1 */ + + vgmstream->current_block_offset = block_offset; + vgmstream->current_block_size = channel_size; + vgmstream->next_block_offset = block_offset + block_size; + + for (i = 0; i < vgmstream->channels; i++) { + vgmstream->ch[i].offset = block_offset + data_offset + channel_size*i; + } +} diff --git a/src/layout/str_snds_blocked.c b/src/layout/blocked_str_snds.c similarity index 96% rename from src/layout/str_snds_blocked.c rename to src/layout/blocked_str_snds.c index 91b64d60..7a7fca9a 100644 --- a/src/layout/str_snds_blocked.c +++ b/src/layout/blocked_str_snds.c @@ -2,7 +2,7 @@ #include "../vgmstream.h" /* set up for the block at the given offset */ -void str_snds_block_update(off_t block_offset, VGMSTREAM * vgmstream) { +void block_update_str_snds(off_t block_offset, VGMSTREAM * vgmstream) { off_t current_chunk; size_t file_size; int i; diff --git a/src/layout/thp_blocked.c b/src/layout/blocked_thp.c similarity index 94% rename from src/layout/thp_blocked.c rename to src/layout/blocked_thp.c index 828d6c8d..abea3e2c 100644 --- a/src/layout/thp_blocked.c +++ b/src/layout/blocked_thp.c @@ -2,7 +2,7 @@ #include "../vgmstream.h" /* set up for the block at the given offset */ -void thp_block_update(off_t block_offset, VGMSTREAM * vgmstream) { +void block_update_thp(off_t block_offset, VGMSTREAM * vgmstream) { int i,j; STREAMFILE *streamFile=vgmstream->ch[0].streamfile; off_t start_offset; diff --git a/src/layout/tra_blocked.c b/src/layout/blocked_tra.c similarity index 89% rename from src/layout/tra_blocked.c rename to src/layout/blocked_tra.c index cc235f72..db29fe8e 100644 --- a/src/layout/tra_blocked.c +++ b/src/layout/blocked_tra.c @@ -2,7 +2,7 @@ #include "../vgmstream.h" /* set up for the block at the given offset (first 32bytes is useless for decoding) */ -void tra_block_update(off_t block_offset, VGMSTREAM * vgmstream) { +void block_update_tra(off_t block_offset, VGMSTREAM * vgmstream) { int i; vgmstream->current_block_offset = block_offset; diff --git a/src/layout/vs_blocked.c b/src/layout/blocked_vs.c similarity index 89% rename from src/layout/vs_blocked.c rename to src/layout/blocked_vs.c index 76e4cb27..4284642d 100644 --- a/src/layout/vs_blocked.c +++ b/src/layout/blocked_vs.c @@ -2,7 +2,7 @@ #include "../vgmstream.h" /* set up for the block at the given offset */ -void vs_block_update(off_t block_offset, VGMSTREAM * vgmstream) { +void block_update_vs(off_t block_offset, VGMSTREAM * vgmstream) { int i; for (i=0;ichannels;i++) { diff --git a/src/layout/ws_aud_blocked.c b/src/layout/blocked_ws_aud.c similarity index 92% rename from src/layout/ws_aud_blocked.c rename to src/layout/blocked_ws_aud.c index dfc824a5..6912060e 100644 --- a/src/layout/ws_aud_blocked.c +++ b/src/layout/blocked_ws_aud.c @@ -2,7 +2,7 @@ #include "../vgmstream.h" /* set up for the block at the given offset */ -void ws_aud_block_update(off_t block_offset, VGMSTREAM * vgmstream) { +void block_update_ws_aud(off_t block_offset, VGMSTREAM * vgmstream) { int i; vgmstream->current_block_offset = block_offset; vgmstream->current_block_size = read_16bitLE( diff --git a/src/layout/xa_blocked.c b/src/layout/blocked_xa.c similarity index 97% rename from src/layout/xa_blocked.c rename to src/layout/blocked_xa.c index bd208fd2..e7829b68 100644 --- a/src/layout/xa_blocked.c +++ b/src/layout/blocked_xa.c @@ -3,7 +3,7 @@ #include "../vgmstream.h" /* set up for the block at the given offset */ -void xa_block_update(off_t block_offset, VGMSTREAM * vgmstream) { +void block_update_xa(off_t block_offset, VGMSTREAM * vgmstream) { int i; int8_t currentChannel=0; int8_t subAudio=0; diff --git a/src/layout/xvas_block.c b/src/layout/blocked_xvas.c similarity index 91% rename from src/layout/xvas_block.c rename to src/layout/blocked_xvas.c index ee78c3ad..a26ed0da 100644 --- a/src/layout/xvas_block.c +++ b/src/layout/blocked_xvas.c @@ -2,7 +2,7 @@ #include "../vgmstream.h" /* set up for the block at the given offset */ -void xvas_block_update(off_t block_offset, VGMSTREAM * vgmstream) { +void block_update_xvas(off_t block_offset, VGMSTREAM * vgmstream) { int i; vgmstream->current_block_offset = block_offset; diff --git a/src/layout/layered.c b/src/layout/layered.c new file mode 100644 index 00000000..11fc468f --- /dev/null +++ b/src/layout/layered.c @@ -0,0 +1,122 @@ +#include "layout.h" +#include "../vgmstream.h" + +/* TODO: currently only properly handles mono substreams */ +/* TODO: there must be a reasonable way to respect the loop settings, as + the substreams are in their own little world. + Currently the VGMSTREAMs layers loop internally and the external/base VGMSTREAM + doesn't actually loop, and would ignore any altered values/loop_flag. */ + +#define INTERLEAVE_BUF_SIZE 512 + + +void render_vgmstream_layered(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstream) { + sample interleave_buf[INTERLEAVE_BUF_SIZE]; + int32_t samples_done = 0; + layered_layout_data *data = vgmstream->layout_data; + + while (samples_done < sample_count) { + int32_t samples_to_do = INTERLEAVE_BUF_SIZE; + int c; + if (samples_to_do > sample_count - samples_done) + samples_to_do = sample_count - samples_done; + + for (c=0; c < data->layer_count; c++) { + int32_t i; + + render_vgmstream(interleave_buf, samples_to_do, data->layers[c]); + + for (i=0; i < samples_to_do; i++) { + buffer[(samples_done+i)*data->layer_count + c] = interleave_buf[i]; + } + } + + samples_done += samples_to_do; + } +} + + +layered_layout_data* init_layout_layered(int layer_count) { + layered_layout_data *data = NULL; + + if (layer_count <= 0 || layer_count > 255) + goto fail; + + data = calloc(1, sizeof(layered_layout_data)); + if (!data) goto fail; + + data->layer_count = layer_count; + + data->layers = calloc(layer_count, sizeof(VGMSTREAM*)); + if (!data->layers) goto fail; + + return data; +fail: + free_layout_layered(data); + return NULL; +} + +int setup_layout_layered(layered_layout_data* data) { + int i; + + /* setup each VGMSTREAM (roughly equivalent to vgmstream.c's init_vgmstream_internal stuff) */ + for (i = 0; i < data->layer_count; i++) { + if (!data->layers[i]) + goto fail; + + if (data->layers[i]->num_samples <= 0) + goto fail; + + //todo only mono at the moment + if (data->layers[i]->channels != 1) + goto fail; + + if (i > 0) { + /* a bit weird, but no matter */ + if (data->layers[i]->sample_rate != data->layers[i-1]->sample_rate) { + VGM_LOG("layered layout: layer %i has different sample rate\n", i); + } + + /* also weird */ + if (data->layers[i]->coding_type != data->layers[i-1]->coding_type) { + VGM_LOG("layered layout: layer %i has different coding type\n", i); + } + } + + //todo could check if layers'd loop match vs main, etc + + /* save start things so we can restart for seeking/looping */ + memcpy(data->layers[i]->start_ch,data->layers[i]->ch,sizeof(VGMSTREAMCHANNEL)*data->layers[i]->channels); + memcpy(data->layers[i]->start_vgmstream,data->layers[i],sizeof(VGMSTREAM)); + } + + return 1; +fail: + return 0; /* caller is expected to free */ +} + +void free_layout_layered(layered_layout_data *data) { + int i; + + if (!data) + return; + + if (data->layers) { + for (i = 0; i < data->layer_count; i++) { + close_vgmstream(data->layers[i]); + } + free(data->layers); + } + free(data); +} + +void reset_layout_layered(layered_layout_data *data) { + int i; + + if (!data) + return; + + for (i = 0; i < data->layer_count; i++) { + reset_vgmstream(data->layers[i]); + } +} diff --git a/src/layout/layout.h b/src/layout/layout.h index 3a351b4b..a8fc7834 100644 --- a/src/layout/layout.h +++ b/src/layout/layout.h @@ -7,53 +7,32 @@ /* blocked layouts */ void render_vgmstream_blocked(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstream); -void ast_block_update(off_t block_ofset, VGMSTREAM * vgmstream); - -void mxch_block_update(off_t block_ofset, VGMSTREAM * vgmstream); - -void halpst_block_update(off_t block_ofset, VGMSTREAM * vgmstream); - -void xa_block_update(off_t block_offset, VGMSTREAM * vgmstream); - +void block_update_ast(off_t block_ofset, VGMSTREAM * vgmstream); +void block_update_mxch(off_t block_ofset, VGMSTREAM * vgmstream); +void block_update_halpst(off_t block_ofset, VGMSTREAM * vgmstream); +void block_update_xa(off_t block_offset, VGMSTREAM * vgmstream); void block_update_ea_schl(off_t block_offset, VGMSTREAM * vgmstream); void block_update_ea_1snh(off_t block_offset, VGMSTREAM * vgmstream); void block_update_caf(off_t block_offset, VGMSTREAM * vgmstream); void block_update_wsi(off_t block_offset, VGMSTREAM * vgmstream); - -void str_snds_block_update(off_t block_offset, VGMSTREAM * vgmstream); - -void ws_aud_block_update(off_t block_offset, VGMSTREAM * vgmstream); - -void matx_block_update(off_t block_offset, VGMSTREAM * vgmstream); - +void block_update_str_snds(off_t block_offset, VGMSTREAM * vgmstream); +void block_update_ws_aud(off_t block_offset, VGMSTREAM * vgmstream); +void block_update_matx(off_t block_offset, VGMSTREAM * vgmstream); void block_update_dec(off_t block_offset, VGMSTREAM * vgmstream); - -void vs_block_update(off_t block_offset, VGMSTREAM * vgmstream); - -void emff_ps2_block_update(off_t block_offset, VGMSTREAM * vgmstream); - -void emff_ngc_block_update(off_t block_offset, VGMSTREAM * vgmstream); - -void gsb_block_update(off_t block_offset, VGMSTREAM * vgmstream); - -void xvas_block_update(off_t block_offset, VGMSTREAM * vgmstream); - -void thp_block_update(off_t block_offset, VGMSTREAM * vgmstream); - -void filp_block_update(off_t block_offset, VGMSTREAM * vgmstream); - +void block_update_vs(off_t block_offset, VGMSTREAM * vgmstream); +void block_update_emff_ps2(off_t block_offset, VGMSTREAM * vgmstream); +void block_update_emff_ngc(off_t block_offset, VGMSTREAM * vgmstream); +void block_update_gsb(off_t block_offset, VGMSTREAM * vgmstream); +void block_update_xvas(off_t block_offset, VGMSTREAM * vgmstream); +void block_update_thp(off_t block_offset, VGMSTREAM * vgmstream); +void block_update_filp(off_t block_offset, VGMSTREAM * vgmstream); void block_update_ivaud(off_t block_offset, VGMSTREAM * vgmstream); void block_update_ea_swvr(off_t block_offset, VGMSTREAM * vgmstream); void block_update_adm(off_t block_offset, VGMSTREAM * vgmstream); - -void dsp_bdsp_block_update(off_t block_offset, VGMSTREAM * vgmstream); - -void tra_block_update(off_t block_offset, VGMSTREAM * vgmstream); - -void ps2_iab_block_update(off_t block_offset, VGMSTREAM * vgmstream); - -void ps2_strlr_block_update(off_t block_offset, VGMSTREAM * vgmstream); - +void block_update_bdsp(off_t block_offset, VGMSTREAM * vgmstream); +void block_update_tra(off_t block_offset, VGMSTREAM * vgmstream); +void block_update_ps2_iab(off_t block_offset, VGMSTREAM * vgmstream); +void block_update_ps2_strlr(off_t block_offset, VGMSTREAM * vgmstream); void block_update_rws(off_t block_offset, VGMSTREAM * vgmstream); void block_update_hwas(off_t block_offset, VGMSTREAM * vgmstream); void block_update_ea_sns(off_t block_offset, VGMSTREAM * vgmstream); @@ -61,6 +40,9 @@ void block_update_awc(off_t block_offset, VGMSTREAM * vgmstream); void block_update_vgs(off_t block_offset, VGMSTREAM * vgmstream); void block_update_vawx(off_t block_offset, VGMSTREAM * vgmstream); void block_update_xvag_subsong(off_t block_offset, VGMSTREAM * vgmstream); +void block_update_ea_wve_au00(off_t block_offset, VGMSTREAM * vgmstream); +void block_update_ea_wve_ad10(off_t block_offset, VGMSTREAM * vgmstream); +void block_update_sthd(off_t block_offset, VGMSTREAM * vgmstream); /* other layouts */ void render_vgmstream_interleave(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstream); @@ -75,6 +57,10 @@ int setup_layout_segmented(segmented_layout_data* data); void free_layout_segmented(segmented_layout_data *data); void reset_layout_segmented(segmented_layout_data *data); -void render_vgmstream_scd_int(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstream); +void render_vgmstream_layered(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstream); +layered_layout_data* init_layout_layered(int layer_count); +int setup_layout_layered(layered_layout_data* data); +void free_layout_layered(layered_layout_data *data); +void reset_layout_layered(layered_layout_data *data); #endif diff --git a/src/layout/scd_int_layout.c b/src/layout/scd_int_layout.c deleted file mode 100644 index 1a2c5e8a..00000000 --- a/src/layout/scd_int_layout.c +++ /dev/null @@ -1,38 +0,0 @@ -#include "layout.h" -#include "../vgmstream.h" - -/* TODO: currently only properly handles mono substreams */ -/* TODO: there must be a reasonable way to respect the loop settings, as is - the substreams are in their own little world */ - -#define INTERLEAVE_BUF_SIZE 512 -void render_vgmstream_scd_int(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstream) { - sample interleave_buf[INTERLEAVE_BUF_SIZE]; - int32_t samples_done = 0; - scd_int_codec_data *data = vgmstream->codec_data; - - while (samples_done < sample_count) - { - int32_t samples_to_do = INTERLEAVE_BUF_SIZE; - int c; - if (samples_to_do > sample_count - samples_done) - samples_to_do = sample_count - samples_done; - - for (c=0; c < data->substream_count; c++) - { - int32_t i; - - render_vgmstream(interleave_buf, - samples_to_do, data->substreams[c]); - - for (i=0; i < samples_to_do; i++) - { - buffer[(samples_done+i)*data->substream_count + c] = interleave_buf[i]; - } - } - - samples_done += samples_to_do; - - } -} - diff --git a/src/layout/segmented.c b/src/layout/segmented.c index 9b1c4842..4f2b7073 100644 --- a/src/layout/segmented.c +++ b/src/layout/segmented.c @@ -1,70 +1,5 @@ #include "layout.h" #include "../vgmstream.h" -#include "../coding/coding.h" - -segmented_layout_data* init_layout_segmented(int segment_count) { - segmented_layout_data *data = NULL; - - if (segment_count <= 0 || segment_count > 255) - goto fail; - - data = calloc(1, sizeof(segmented_layout_data)); - if (!data) goto fail; - - data->segment_count = segment_count; - data->current_segment = 0; - - data->segments = calloc(segment_count, sizeof(VGMSTREAM*)); - if (!data->segments) goto fail; - - return data; -fail: - free_layout_segmented(data); - return NULL; -} - - -int setup_layout_segmented(segmented_layout_data* data) { - int i; - - /* setup each VGMSTREAM (roughly equivalent to vgmstream.c's init_vgmstream_internal stuff) */ - for (i = 0; i < data->segment_count; i++) { - if (!data->segments[i]) - goto fail; - - if (data->segments[i]->num_samples <= 0) - goto fail; - - /* shouldn't happen */ - if (data->segments[i]->loop_flag != 0) { - VGM_LOG("segmented layout: segment %i is looped\n", i); - data->segments[i]->loop_flag = 0; - } - - if (i > 0) { - if (data->segments[i]->channels != data->segments[i-1]->channels) - goto fail; - - /* a bit weird, but no matter */ - if (data->segments[i]->sample_rate != data->segments[i-1]->sample_rate) { - VGM_LOG("segmented layout: segment %i has different sample rate\n", i); - } - - //if (data->segments[i]->coding_type != data->segments[i-1]->coding_type) - // goto fail; /* perfectly acceptable */ - } - - - /* save start things so we can restart for seeking/looping */ - memcpy(data->segments[i]->start_ch,data->segments[i]->ch,sizeof(VGMSTREAMCHANNEL)*data->segments[i]->channels); - memcpy(data->segments[i]->start_vgmstream,data->segments[i],sizeof(VGMSTREAM)); - } - - - return 1; -fail: - return 0; /* caller is expected to free */ -} void render_vgmstream_segmented(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstream) { @@ -110,6 +45,69 @@ void render_vgmstream_segmented(sample * buffer, int32_t sample_count, VGMSTREAM } +segmented_layout_data* init_layout_segmented(int segment_count) { + segmented_layout_data *data = NULL; + + if (segment_count <= 0 || segment_count > 255) + goto fail; + + data = calloc(1, sizeof(segmented_layout_data)); + if (!data) goto fail; + + data->segment_count = segment_count; + data->current_segment = 0; + + data->segments = calloc(segment_count, sizeof(VGMSTREAM*)); + if (!data->segments) goto fail; + + return data; +fail: + free_layout_segmented(data); + return NULL; +} + +int setup_layout_segmented(segmented_layout_data* data) { + int i; + + /* setup each VGMSTREAM (roughly equivalent to vgmstream.c's init_vgmstream_internal stuff) */ + for (i = 0; i < data->segment_count; i++) { + if (!data->segments[i]) + goto fail; + + if (data->segments[i]->num_samples <= 0) + goto fail; + + /* shouldn't happen */ + if (data->segments[i]->loop_flag != 0) { + VGM_LOG("segmented layout: segment %i is looped\n", i); + data->segments[i]->loop_flag = 0; + } + + if (i > 0) { + if (data->segments[i]->channels != data->segments[i-1]->channels) + goto fail; + + /* a bit weird, but no matter */ + if (data->segments[i]->sample_rate != data->segments[i-1]->sample_rate) { + VGM_LOG("segmented layout: segment %i has different sample rate\n", i); + } + + //if (data->segments[i]->coding_type != data->segments[i-1]->coding_type) + // goto fail; /* perfectly acceptable */ + } + + + /* save start things so we can restart for seeking/looping */ + memcpy(data->segments[i]->start_ch,data->segments[i]->ch,sizeof(VGMSTREAMCHANNEL)*data->segments[i]->channels); + memcpy(data->segments[i]->start_vgmstream,data->segments[i],sizeof(VGMSTREAM)); + } + + + return 1; +fail: + return 0; /* caller is expected to free */ +} + void free_layout_segmented(segmented_layout_data *data) { int i; @@ -118,8 +116,6 @@ void free_layout_segmented(segmented_layout_data *data) { if (data->segments) { for (i = 0; i < data->segment_count; i++) { - /* note that the close_streamfile won't do anything but deallocate itself, - * there is only one open file in vgmstream->ch[0].streamfile */ close_vgmstream(data->segments[i]); } free(data->segments); diff --git a/src/libvgmstream.vcproj b/src/libvgmstream.vcproj index 5b2e12e2..eeac4134 100644 --- a/src/libvgmstream.vcproj +++ b/src/libvgmstream.vcproj @@ -216,10 +216,18 @@ RelativePath=".\meta\aix_streamfile.h" > + + + + @@ -387,6 +395,14 @@ + + + + + + + + @@ -1691,11 +1715,11 @@ > + + + + @@ -1739,23 +1771,23 @@ > + + diff --git a/src/libvgmstream.vcxproj b/src/libvgmstream.vcxproj index 4d811105..4be03ad0 100644 --- a/src/libvgmstream.vcxproj +++ b/src/libvgmstream.vcxproj @@ -96,7 +96,9 @@ + + @@ -121,10 +123,10 @@ - - - - + + + + @@ -165,6 +167,7 @@ + @@ -212,6 +215,8 @@ + + @@ -288,6 +293,7 @@ + @@ -471,8 +477,8 @@ - - + + @@ -483,26 +489,29 @@ - - - - - + + + + + + + - + + - - - - + + + + - - + + diff --git a/src/libvgmstream.vcxproj.filters b/src/libvgmstream.vcxproj.filters index 2ca53481..03b28dfb 100644 --- a/src/libvgmstream.vcxproj.filters +++ b/src/libvgmstream.vcxproj.filters @@ -68,9 +68,15 @@ meta\Header Files + + meta\Header Files + meta\Header Files + + meta\Header Files + meta\Header Files @@ -235,6 +241,12 @@ meta\Source Files + + meta\Source Files + + + meta\Source Files + meta\Source Files @@ -448,6 +460,9 @@ meta\Source Files + + meta\Source Files + meta\Source Files @@ -988,10 +1003,10 @@ layout\Source Files - + layout\Source Files - + layout\Source Files @@ -1003,6 +1018,12 @@ layout\Source Files + + layout\Source Files + + + layout\Source Files + layout\Source Files @@ -1024,22 +1045,22 @@ layout\Source Files - + layout\Source Files - + layout\Source Files - + layout\Source Files - + layout\Source Files layout\Source Files - + layout\Source Files @@ -1048,7 +1069,7 @@ layout\Source Files - + layout\Source Files @@ -1063,25 +1084,28 @@ layout\Source Files - + layout\Source Files - + layout\Source Files - + layout\Source Files - + + layout\Source Files + + layout\Source Files layout\Source Files - + layout\Source Files - + layout\Source Files @@ -1117,13 +1141,13 @@ meta\Source Files - + layout\Source Files - + layout\Source Files - + layout\Source Files @@ -1144,6 +1168,9 @@ meta\Source Files + + meta\Source Files + meta\Source Files @@ -1192,7 +1219,7 @@ meta\Source Files - + layout\Source Files diff --git a/src/meta/adx.c b/src/meta/adx.c index 5e6a875c..0dc09918 100644 --- a/src/meta/adx.c +++ b/src/meta/adx.c @@ -28,8 +28,10 @@ VGMSTREAM * init_vgmstream_adx(STREAMFILE *streamFile) { uint16_t xor_start=0,xor_mult=0,xor_add=0; - /* check extension, case insensitive */ - if (!check_extensions(streamFile,"adx")) goto fail; + /* checks*/ + /* .adx: standard, .adp: Headhunter (DC) */ + if (!check_extensions(streamFile,"adx,adp")) + goto fail; /* check first 2 bytes */ if ((uint16_t)read_16bitBE(0x00,streamFile)!=0x8000) goto fail; diff --git a/src/meta/ast.c b/src/meta/ast.c index fde88172..4ed5162c 100644 --- a/src/meta/ast.c +++ b/src/meta/ast.c @@ -59,7 +59,7 @@ VGMSTREAM * init_vgmstream_ast(STREAMFILE *streamFile) { vgmstream->loop_end_sample = read_32bitBE(0x1c,streamFile); vgmstream->coding_type = coding_type; - vgmstream->layout_type = layout_ast_blocked; + vgmstream->layout_type = layout_blocked_ast; vgmstream->meta_type = meta_AST; /* open the file for reading by each channel */ @@ -80,7 +80,7 @@ VGMSTREAM * init_vgmstream_ast(STREAMFILE *streamFile) { } /* start me up */ - ast_block_update(0x40,vgmstream); + block_update_ast(0x40,vgmstream); return vgmstream; diff --git a/src/meta/atsl.c b/src/meta/atsl.c index 6cc02e49..bb19f62f 100644 --- a/src/meta/atsl.c +++ b/src/meta/atsl.c @@ -2,7 +2,7 @@ #include "../coding/coding.h" static STREAMFILE* setup_atsl_streamfile(STREAMFILE *streamFile, off_t subfile_offset, size_t subfile_size, const char* fake_ext); -typedef enum { ATRAC3, ATRAC9, KOVS } atsl_codec; +typedef enum { ATRAC3, ATRAC9, KOVS, KTSS } atsl_codec; /* .ATSL - Koei Tecmo audio container [One Piece Pirate Warriors (PS3), Warriors All-Stars (PC)] */ VGMSTREAM * init_vgmstream_atsl(STREAMFILE *streamFile) { @@ -39,6 +39,7 @@ VGMSTREAM * init_vgmstream_atsl(STREAMFILE *streamFile) { * - 00060301 00040301 atsl in G1L from One Piece Pirate Warriors 3 (Vita)[ATRAC9] * - 00060301 00010301 atsl in G1L from One Piece Pirate Warriors 3 (PC)[KOVS] * - 000A0301 00010501 atsl in G1L from Warriors All-Stars (PC)[KOVS] + * - 000B0301 00080601 atsl in G1l from Sengoku Musou Sanada Maru (Switch)[KTSS] */ type = read_8bit(0x0d, streamFile); switch(type) { @@ -56,7 +57,12 @@ VGMSTREAM * init_vgmstream_atsl(STREAMFILE *streamFile) { codec = ATRAC9; fake_ext = "at9"; break; + case 0x08: + codec = KTSS; + fake_ext = "ktss"; + break; default: + VGM_LOG("ATSL: unknown type %x\n", type); goto fail; } read_32bit = big_endian ? read_32bitBE : read_32bitLE; @@ -118,6 +124,10 @@ VGMSTREAM * init_vgmstream_atsl(STREAMFILE *streamFile) { vgmstream = init_vgmstream_ogg_vorbis(temp_streamFile); if (!vgmstream) goto fail; break; + case KTSS: + vgmstream = init_vgmstream_ktss(temp_streamFile); + if (!vgmstream) goto fail; + break; default: goto fail; } diff --git a/src/meta/atx.c b/src/meta/atx.c index c969f361..bace1a29 100644 --- a/src/meta/atx.c +++ b/src/meta/atx.c @@ -59,7 +59,7 @@ static STREAMFILE* setup_atx_streamfile(STREAMFILE *streamFile) { size_t subfile_size; filename[filename_len - 5] = ('0'+i+1); /* ghetto digit conversion */ - new_streamFile = open_stream_name(streamFile, filename); + new_streamFile = open_streamfile_by_filename(streamFile, filename); if (!new_streamFile) goto fail; segment_streamFiles[i] = new_streamFile; diff --git a/src/meta/awc.c b/src/meta/awc.c index 6423e947..1994613a 100644 --- a/src/meta/awc.c +++ b/src/meta/awc.c @@ -1,6 +1,7 @@ #include "meta.h" #include "../coding/coding.h" #include "../layout/layout.h" +#include "awc_xma_streamfile.h" typedef struct { int big_endian; @@ -64,6 +65,76 @@ VGMSTREAM * init_vgmstream_awc(STREAMFILE *streamFile) { vgmstream->codec_endian = awc.big_endian; break; +#ifdef VGM_USE_FFMPEG + case 0x05: { /* XMA2 (X360) */ + uint8_t buf[0x100]; + size_t bytes, block_size, block_count, substream_size; + off_t substream_offset; + + if (awc.is_music) { + /* 1ch XMAs in blocks, we'll use layered layout + custom IO to get multi-FFmpegs working */ + int i; + layered_layout_data * data = NULL; + + /* init layout */ + data = init_layout_layered(awc.channel_count); + if (!data) goto fail; + vgmstream->layout_data = data; + vgmstream->layout_type = layout_layered; + vgmstream->coding_type = coding_FFmpeg; + + /* open each layer subfile */ + for (i = 0; i < awc.channel_count; i++) { + STREAMFILE* temp_streamFile; + int layer_channels = 1; + + /* build the layer VGMSTREAM */ + data->layers[i] = allocate_vgmstream(layer_channels, 0); + if (!data->layers[i]) goto fail; + + data->layers[i]->sample_rate = awc.sample_rate; + data->layers[i]->meta_type = meta_AWC; + data->layers[i]->coding_type = coding_FFmpeg; + data->layers[i]->layout_type = layout_none; + data->layers[i]->num_samples = awc.num_samples; + + /* setup custom IO streamfile, pass to FFmpeg and hope it's fooled */ + temp_streamFile = setup_awc_xma_streamfile(streamFile, awc.stream_offset, awc.stream_size, awc.block_chunk, awc.channel_count, i); + if (!temp_streamFile) goto fail; + + substream_offset = 0; /* where FFmpeg thinks data starts, which our custom streamFile will clamp */ + substream_size = get_streamfile_size(temp_streamFile); /* data of one XMA substream without blocks */ + block_size = 0x8000; /* no idea */ + block_count = substream_size / block_size; /* not accurate but not needed */ + + bytes = ffmpeg_make_riff_xma2(buf, 0x100, awc.num_samples, substream_size, layer_channels, awc.sample_rate, block_count, block_size); + data->layers[i]->codec_data = init_ffmpeg_header_offset(temp_streamFile, buf,bytes, substream_offset,substream_size); + + close_streamfile(temp_streamFile); + if (!data->layers[i]->codec_data) goto fail; + } + + /* setup layered VGMSTREAMs */ + if (!setup_layout_layered(data)) + goto fail; + } + else { + /* regular XMA for sfx */ + block_size = 0x8000; /* no idea */ + block_count = awc.stream_size / block_size; /* not accurate but not needed */ + + bytes = ffmpeg_make_riff_xma2(buf, 0x100, awc.num_samples, awc.stream_size, awc.channel_count, awc.sample_rate, block_count, block_size); + vgmstream->codec_data = init_ffmpeg_header_offset(streamFile, buf,bytes, awc.stream_offset,awc.stream_size); + if (!vgmstream->codec_data) goto fail; + vgmstream->coding_type = coding_FFmpeg; + vgmstream->layout_type = layout_none; + } + + break; + } + + +#endif #ifdef VGM_USE_MPEG case 0x07: { /* MPEG (PS3) */ mpeg_custom_config cfg; @@ -80,7 +151,6 @@ VGMSTREAM * init_vgmstream_awc(STREAMFILE *streamFile) { } #endif - case 0x05: /* XMA2 (X360) */ default: VGM_LOG("AWC: unknown codec 0x%02x\n", awc.codec); goto fail; diff --git a/src/meta/awc_xma_streamfile.h b/src/meta/awc_xma_streamfile.h new file mode 100644 index 00000000..f4c231c7 --- /dev/null +++ b/src/meta/awc_xma_streamfile.h @@ -0,0 +1,257 @@ +#ifndef _AWC_XMA_STREAMFILE_H_ +#define _AWC_XMA_STREAMFILE_H_ +#include "../streamfile.h" + + +typedef struct { + /* config */ + off_t stream_offset; + size_t stream_size; + int channel_count; + int channel; + size_t chunk_size; + + /* state */ + off_t logical_offset; /* offset that corresponds to physical_offset */ + off_t physical_offset; /* actual file offset */ + off_t next_block_offset; /* physical offset of the next block start */ + off_t last_offset; /* physical offset of where the last block ended */ + size_t current_data_size; + size_t current_consumed_size; + + size_t total_size; /* size of the resulting XMA data */ +} awc_xma_io_data; + + +static size_t get_block_header_size(STREAMFILE *streamFile, off_t offset, awc_xma_io_data *data); +static size_t get_repeated_data_size(STREAMFILE *streamFile, off_t new_offset, off_t last_offset); +static size_t get_block_skip_count(STREAMFILE *streamFile, off_t offset, int channel); + +/* Reads plain XMA data of a single stream. Each block has a header and channels have different num_samples/frames. + * Channel data is separate within the block (first all frames of ch0, then ch1, etc), padded, and sometimes + * the last few frames of a channel are repeated in the new block (marked with the "discard samples" field). */ +static size_t awc_xma_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length, awc_xma_io_data* data) { + size_t total_read = 0; + size_t frame_size = 0x800; + + /* ignore bad reads */ + if (offset < 0 || offset > data->total_size) { + return 0; + } + + /* previous offset: re-start as we can't map logical<>physical offsets + * (kinda slow as it trashes buffers, but shouldn't happen often) */ + if (offset < data->logical_offset) { + data->logical_offset = 0x00; + data->physical_offset = data->stream_offset; + data->next_block_offset = 0; + data->last_offset = 0; + data->current_data_size = 0; + data->current_consumed_size = 0; + } + + /* read from block, moving to next when all data is consumed */ + while (length > 0) { + size_t to_read, bytes_read; + + /* new block */ + if (data->current_data_size == 0) { + size_t header_size = get_block_header_size(streamfile, data->physical_offset, data); + /* header table entries = frames... I hope */ + size_t skip_size = get_block_skip_count(streamfile, data->physical_offset, data->channel) * frame_size; + //size_t skip_size = read_32bitBE(data->physical_offset + 0x10*data->channel + 0x00, streamfile) * frame_size; + size_t data_size = read_32bitBE(data->physical_offset + 0x10*data->channel + 0x04, streamfile) * frame_size; + size_t repeat_samples = read_32bitBE(data->physical_offset + 0x10*data->channel + 0x08, streamfile); + size_t repeat_size = 0; + + /* if there are repeat samples current block repeats some frames from last block, find out size */ + if (repeat_samples && data->last_offset) { + off_t data_offset = data->physical_offset + header_size + skip_size; + repeat_size = get_repeated_data_size(streamfile, data_offset, data->last_offset); + } + + data->next_block_offset = data->physical_offset + data->chunk_size; + data->physical_offset += header_size + skip_size + repeat_size; /* data start */ + data->current_data_size = data_size - repeat_size; /* readable max in this block */ + data->current_consumed_size = 0; + continue; + } + + /* block end, go next */ + if (data->current_consumed_size == data->current_data_size) { + data->last_offset = data->physical_offset; /* where last block ended */ + data->physical_offset = data->next_block_offset; + data->current_data_size = 0; + continue; + } + + /* requested offset is further along, pretend we consumed data and try again */ + if (offset > data->logical_offset) { + size_t to_consume = offset - data->logical_offset; + if (to_consume > data->current_data_size - data->current_consumed_size) + to_consume = data->current_data_size - data->current_consumed_size; + + data->physical_offset += to_consume; + data->logical_offset += to_consume; + data->current_consumed_size += to_consume; + continue; + } + + /* clamp reads up to this block's end */ + to_read = (data->current_data_size - data->current_consumed_size); + if (to_read > length) + to_read = length; + if (to_read == 0) + return total_read; /* should never happen... */ + + /* finally read and move buffer/offsets */ + bytes_read = read_streamfile(dest, data->physical_offset, to_read, streamfile); + total_read += bytes_read; + if (bytes_read != to_read) + return total_read; /* couldn't read fully */ + + dest += bytes_read; + offset += bytes_read; + length -= bytes_read; + + data->physical_offset += bytes_read; + data->logical_offset += bytes_read; + data->current_consumed_size += bytes_read; + } + + return total_read; +} + +static size_t awc_xma_io_size(STREAMFILE *streamfile, awc_xma_io_data* data) { + off_t physical_offset, max_physical_offset, last_offset; + size_t frame_size = 0x800; + size_t total_size = 0; + + if (data->total_size) + return data->total_size; + + physical_offset = data->stream_offset; + max_physical_offset = data->stream_offset + data->stream_size; + last_offset = 0; + + /* read blocks and sum final size */ + while (physical_offset < max_physical_offset) { + size_t header_size = get_block_header_size(streamfile, physical_offset, data); + /* header table entries = frames... I hope */ + size_t skip_size = get_block_skip_count(streamfile, physical_offset, data->channel) * frame_size; + //size_t skip_size = read_32bitBE(physical_offset + 0x10*data->channel + 0x00, streamfile) * frame_size; + size_t data_size = read_32bitBE(physical_offset + 0x10*data->channel + 0x04, streamfile) * frame_size; + size_t repeat_samples = read_32bitBE(physical_offset + 0x10*data->channel + 0x08, streamfile); + size_t repeat_size = 0; + + /* if there are repeat samples current block repeats some frames from last block, find out size */ + if (repeat_samples && last_offset) { + off_t data_offset = physical_offset + header_size + skip_size; + repeat_size = get_repeated_data_size(streamfile, data_offset, last_offset); + } + + last_offset = physical_offset + header_size + skip_size + data_size; + total_size += data_size - repeat_size; + physical_offset += data->chunk_size; + } + + data->total_size = total_size; + return data->total_size; +} + + +/* Prepares custom IO for AWC XMA, which is interleaved XMA in AWC blocks */ +static STREAMFILE* setup_awc_xma_streamfile(STREAMFILE *streamFile, off_t stream_offset, size_t stream_size, size_t chunk_size, int channel_count, int channel) { + STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL; + awc_xma_io_data io_data = {0}; + size_t io_data_size = sizeof(awc_xma_io_data); + + io_data.stream_offset = stream_offset; + io_data.stream_size = stream_size; + io_data.chunk_size = chunk_size; + io_data.channel_count = channel_count; + io_data.channel = channel; + io_data.physical_offset = stream_offset; + + /* setup subfile */ + new_streamFile = open_wrap_streamfile(streamFile); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + new_streamFile = open_io_streamfile(temp_streamFile, &io_data,io_data_size, awc_xma_io_read,awc_xma_io_size); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + //todo maybe should force to read filesize once + return temp_streamFile; + +fail: + close_streamfile(temp_streamFile); + return NULL; +} + +/* block header size, aligned/padded to 0x800 */ +static size_t get_block_header_size(STREAMFILE *streamFile, off_t offset, awc_xma_io_data *data) { + size_t header_size = 0; + int i; + int entries = data->channel_count; + + for (i = 0; i < entries; i++) { + header_size += 0x10; + header_size += read_32bitBE(offset + 0x10*i + 0x04, streamFile) * 0x04; /* entries in the table */ + } + + if (header_size % 0x800) /* padded */ + header_size += 0x800 - (header_size % 0x800); + + return header_size; +} + + +/* find data that repeats in the beginning of a new block at the end of last block */ +static size_t get_repeated_data_size(STREAMFILE *streamFile, off_t new_offset, off_t last_offset) { + uint8_t new_frame[0x800];/* buffer to avoid fseek back and forth */ + size_t frame_size = 0x800; + off_t offset; + int i; + + /* read block first frame */ + if (read_streamfile(new_frame,new_offset, frame_size,streamFile) != frame_size) + goto fail; + + /* find the frame in last bytes of prev block */ + offset = last_offset - 0x4000; /* typical max is 1 frame of ~0x800, no way to know exact size */ + while (offset < last_offset) { + /* compare frame vs prev block data */ + for (i = 0; i < frame_size; i++) { + if ((uint8_t)read_8bit(offset+i,streamFile) != new_frame[i]) + break; + } + + /* frame fully compared? */ + if (i == frame_size) + return last_offset - offset; + else + offset += i+1; + } + +fail: + VGM_LOG("AWC: can't find repeat size, new=0x%08lx, last=0x%08lx\n", new_offset, last_offset); + return 0; /* keep on truckin' */ +} + +/* header has a skip value, but somehow it's sometimes bigger than expected (WHY!?!?) so just sum all */ +static size_t get_block_skip_count(STREAMFILE *streamFile, off_t offset, int channel) { + size_t skip_count = 0; + int i; + + //skip_size = read_32bitBE(offset + 0x10*channel + 0x00, streamFile); /* wrong! */ + for (i = 0; i < channel; i++) { + skip_count += read_32bitBE(offset + 0x10*i + 0x04, streamFile); /* number of frames of this channel */ + } + + return skip_count; +} + + +#endif /* _AWC_XMA_STREAMFILE_H_ */ diff --git a/src/meta/bgw.c b/src/meta/bgw.c index 0be6ebab..601d7cbd 100644 --- a/src/meta/bgw.c +++ b/src/meta/bgw.c @@ -244,7 +244,7 @@ static STREAMFILE* setup_bgw_atrac3_streamfile(STREAMFILE *streamFile, off_t sub if (!new_streamFile) goto fail; temp_streamFile = new_streamFile; - new_streamFile = open_io_streamfile(temp_streamFile, &io_data,io_data_size, bgw_decryption_read); + new_streamFile = open_io_streamfile(temp_streamFile, &io_data,io_data_size, bgw_decryption_read,NULL); if (!new_streamFile) goto fail; temp_streamFile = new_streamFile; diff --git a/src/meta/dc_dcsw_dcs.c b/src/meta/dc_dcsw_dcs.c index 23105fd0..d1710560 100644 --- a/src/meta/dc_dcsw_dcs.c +++ b/src/meta/dc_dcsw_dcs.c @@ -92,7 +92,7 @@ VGMSTREAM * init_vgmstream_dc_dcsw_dcs(STREAMFILE *streamFile) { vgmstream->interleave_block_size = 0x4000; } - vgmstream->coding_type = coding_AICA; + vgmstream->coding_type = coding_AICA_int; vgmstream->meta_type = meta_DC_DCSW_DCS; /* open the file for reading by each channel */ diff --git a/src/meta/dc_str.c b/src/meta/dc_str.c index 9bac3c36..d9be078d 100644 --- a/src/meta/dc_str.c +++ b/src/meta/dc_str.c @@ -39,7 +39,7 @@ VGMSTREAM * init_vgmstream_dc_str(STREAMFILE *streamFile) { /* fill in the vital statistics */ switch (samples) { case 4: - vgmstream->coding_type = coding_AICA; + vgmstream->coding_type = coding_AICA_int; vgmstream->num_samples = read_32bitLE(0x14,streamFile); if (loop_flag) { vgmstream->loop_start_sample = 0; diff --git a/src/meta/dec.c b/src/meta/dec.c index 0d562b7a..43a3fe18 100644 --- a/src/meta/dec.c +++ b/src/meta/dec.c @@ -103,23 +103,23 @@ static int get_falcom_looping(STREAMFILE *streamFile, int *out_loop_start, int * /* try one of the many loop files */ - if ((streamText = open_stream_name(streamFile,"bgm.tbl")) != NULL) { + if ((streamText = open_streamfile_by_filename(streamFile,"bgm.tbl")) != NULL) { type = XANADU_NEXT; } - else if ((streamText = open_stream_name(streamFile,"bgm.scr")) != NULL) { + else if ((streamText = open_streamfile_by_filename(streamFile,"bgm.scr")) != NULL) { type = ZWEI; } - else if ((streamText = open_stream_name(streamFile,"loop.txt")) != NULL) { /* actual name in Shift JIS, 0x838B815B8376 */ + else if ((streamText = open_streamfile_by_filename(streamFile,"loop.txt")) != NULL) { /* actual name in Shift JIS, 0x838B815B8376 */ type = DINOSAUR_RESURRECTION; } - else if ((streamText = open_stream_name(streamFile,"map.itm")) != NULL) { + else if ((streamText = open_streamfile_by_filename(streamFile,"map.itm")) != NULL) { type = GURUMIN; } else { goto end; } - get_streamfile_name(streamFile,filename,TXT_LINE_MAX); + get_streamfile_filename(streamFile,filename,TXT_LINE_MAX); /* read line by line */ while (txt_offset < get_streamfile_size(streamText)) { diff --git a/src/meta/dsp_bdsp.c b/src/meta/dsp_bdsp.c index d71b7141..cd39bf62 100644 --- a/src/meta/dsp_bdsp.c +++ b/src/meta/dsp_bdsp.c @@ -35,7 +35,7 @@ VGMSTREAM * init_vgmstream_dsp_bdsp(STREAMFILE *streamFile) { #endif - vgmstream->layout_type = layout_dsp_bdsp_blocked; + vgmstream->layout_type = layout_blocked_bdsp; vgmstream->interleave_block_size = 0x8; vgmstream->meta_type = meta_DSP_BDSP; @@ -64,17 +64,17 @@ VGMSTREAM * init_vgmstream_dsp_bdsp(STREAMFILE *streamFile) { /* Calc num_samples */ start_offset = 0x0; - dsp_bdsp_block_update(start_offset,vgmstream); + block_update_bdsp(start_offset,vgmstream); vgmstream->num_samples=0; do { vgmstream->num_samples += vgmstream->current_block_size*14/8; - dsp_bdsp_block_update(vgmstream->next_block_offset,vgmstream); + block_update_bdsp(vgmstream->next_block_offset,vgmstream); } while (vgmstream->next_block_offsetchannels; cfg.config_data = read_32bitBE(header_offset + 0x08,streamHead); - /* 0x0c: data size without blocks?, 0x10: frame size? (same as config data?) */ + /* 0x10: frame size? (same as config data?) */ + total_size = read_32bitLE(header_offset + 0x0c,streamHead); /* actual data size without blocks, LE b/c why make sense */ vgmstream->codec_data = init_atrac9(&cfg); if (!vgmstream->codec_data) goto fail; vgmstream->coding_type = coding_ATRAC9; - vgmstream->layout_type = layout_blocked_ea_sns; + vgmstream->layout_type = layout_none; + + /* EATrax is "buffered" ATRAC9, uses custom IO since it's kind of complex to add to the decoder */ + start_offset = 0x00; /* must point to header start */ + temp_streamFile = setup_eatrax_streamfile(streamData, total_size); + if (!temp_streamFile) goto fail; + break; } -#endif #endif case 0x00: /* "NONE" (internal 'codec not set' flag) */ @@ -266,16 +274,18 @@ static VGMSTREAM * init_vgmstream_eaaudiocore_header(STREAMFILE * streamHead, ST } - /* open the file for reading by each channel */ - if (!vgmstream_open_stream(vgmstream,streamData,start_offset)) + if (!vgmstream_open_stream(vgmstream,temp_streamFile ? temp_streamFile : streamData,start_offset)) goto fail; + close_streamfile(temp_streamFile); + if (vgmstream->layout_type == layout_blocked_ea_sns) block_update_ea_sns(start_offset, vgmstream); return vgmstream; fail: + close_streamfile(temp_streamFile); close_vgmstream(vgmstream); return NULL; } diff --git a/src/meta/ea_eaac_eatrax_streamfile.h b/src/meta/ea_eaac_eatrax_streamfile.h new file mode 100644 index 00000000..4a03661b --- /dev/null +++ b/src/meta/ea_eaac_eatrax_streamfile.h @@ -0,0 +1,127 @@ +#ifndef _EA_EAAC_EATRAX_STREAMFILE_H_ +#define _EA_EAAC_EATRAX_STREAMFILE_H_ +#include "../streamfile.h" + + +typedef struct { + /* state */ + off_t logical_offset; /* offset that corresponds to physical_offset */ + off_t physical_offset; /* actual file offset */ + + size_t total_size; /* size of the resulting substream */ +} eatrax_io_data; + + +/* Reads skipping EA's block headers, so the resulting data is smaller than physical data, + * and physical_offset is bigger than offset (ex. reads at offset = 0x00 could be at physical_offset = 0x10). + * physical/logical_offset should always be at the start of a block and only advance when a block is fully done */ +static size_t eatrax_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length, eatrax_io_data* data) { + size_t total_read = 0; + + /* ignore bad reads */ + if (offset < 0 || offset > data->total_size) { + return 0; + } + + /* previous offset: re-start as we can't map logical<>physical offsets + * (kinda slow as it trashes buffers, but shouldn't happen often) */ + if (offset < data->logical_offset) { + data->physical_offset = 0x00; + data->logical_offset = 0x00; + } + + /* read doing one EA block at a time */ + while (length > 0) { + size_t to_read, bytes_read; + off_t intrablock_offset, intradata_offset; + uint32_t block_size, block_flag, data_size; + + block_flag = read_8bit(data->physical_offset+0x00,streamfile); + block_size = read_32bitBE(data->physical_offset+0x00,streamfile) & 0x00FFFFFF; + data_size = read_32bitBE(data->physical_offset+0x04,streamfile); /* typically block_size - 0x08 */ + + + /* skip header block */ + if (block_flag == 0x48) { + data->physical_offset += block_size; + continue; + } + /* stop on footer block */ + if (block_flag == 0x45) { + data->physical_offset += block_size; + return total_read; + } + /* data block expected */ + if (block_flag != 0x44) { + return total_read; + } + + /* requested offset is outside current block, try next */ + if (offset >= data->logical_offset + data_size) { + data->physical_offset += block_size; + data->logical_offset += data_size; + continue; + } + + /* reads could fall in the middle of the block */ + intradata_offset = offset - data->logical_offset; + intrablock_offset = 0x08 + intradata_offset; + + /* clamp reads up to this block's end */ + to_read = (data_size - intradata_offset); + if (to_read > length) + to_read = length; + if (to_read == 0) + return total_read; /* should never happen... */ + + /* finally read and move buffer/offsets */ + bytes_read = read_streamfile(dest, data->physical_offset + intrablock_offset, to_read, streamfile); + total_read += bytes_read; + if (bytes_read != to_read) + return total_read; /* couldn't read fully */ + + dest += bytes_read; + offset += bytes_read; + length -= bytes_read; + + /* block fully read, go next */ + if (intradata_offset + bytes_read == data_size) { + data->physical_offset += block_size; + data->logical_offset += data_size; + } + } + + return total_read; +} + +static size_t eatrax_io_size(STREAMFILE *streamfile, eatrax_io_data* data) { + return data->total_size; +} + + +/* Prepares custom IO for EATrax, which is simply blocked ATRAC9 data, but blocks + * may end in the middle of an ATRAC9 frame, so reads remove their headers */ +static STREAMFILE* setup_eatrax_streamfile(STREAMFILE *streamFile, size_t total_size) { + STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL; + eatrax_io_data io_data = {0}; + size_t io_data_size = sizeof(eatrax_io_data); + + io_data.total_size = total_size; + + /* setup subfile */ + new_streamFile = open_wrap_streamfile(streamFile); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + new_streamFile = open_io_streamfile(temp_streamFile, &io_data,io_data_size, eatrax_io_read,eatrax_io_size); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + return temp_streamFile; + +fail: + close_streamfile(temp_streamFile); + return NULL; +} + +#endif /* _EA_EAAC_STREAMFILE_H_ */ diff --git a/src/meta/ea_schl.c b/src/meta/ea_schl.c index c6986f66..85adbf67 100644 --- a/src/meta/ea_schl.c +++ b/src/meta/ea_schl.c @@ -92,11 +92,12 @@ VGMSTREAM * init_vgmstream_ea_schl(STREAMFILE *streamFile) { /* check header */ if (read_32bitBE(0x00,streamFile) != 0x5343486C && /* "SCHl" */ - read_32bitBE(0x00,streamFile) != 0x5348454E) /* "SHEN" */ + read_32bitBE(0x00,streamFile) != 0x5348454E && /* "SHEN" */ + read_32bitBE(0x00,streamFile) != 0x53484652) /* "SHFR" */ 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 various blocks (MVhd/MV0K/etc) and sometimes alt audio blocks (SHEN/SDEN/SEEN). + * Video uses various blocks (MVhd/MV0K/etc) and sometimes alt audio blocks (SHxx/SCxx/SDxx/SExx where XX=language, EN/FR). * The number/size is affected by: block rate setting, sample rate, channels, CPU location (SPU/main/DSP/others), etc */ header_size = read_32bitLE(0x04,streamFile); @@ -769,7 +770,7 @@ static int get_ea_stream_total_samples(STREAMFILE* streamFile, off_t start_offse block_samples = 0; - if (id == 0x5343446C || id == 0x5344454E) { /* "SCDl" "SDEN" audio data */ + if (id == 0x5343446C || id == 0x5344454E || id == 0x53444652) { /* "SCDl" "SDEN" "SDFR" audio data */ switch (ea->codec2) { case EA_CODEC2_VAG: block_samples = ps_bytes_to_samples(block_size-0x10, ea->channels); @@ -785,7 +786,7 @@ static int get_ea_stream_total_samples(STREAMFILE* streamFile, off_t start_offse block_size = 0x04; } - if (id == 0x5343486C || id == 0x5348454E) { /* "SCHl" "SHEN" end block */ + if (id == 0x5343486C || id == 0x5348454E || id == 0x53484652) { /* "SCHl" "SHEN" "SHFR" end block */ new_schl = 1; } } @@ -800,8 +801,8 @@ static int get_ea_stream_total_samples(STREAMFILE* streamFile, off_t start_offse num_samples += block_samples; block_offset += block_size; - /* "SCEl" are aligned to 0x80 usually, but causes problems if not 32b-aligned (ex. Need for Speed 2 PC) */ - if ((id == 0x5343456C || id == 0x5345454E) && block_offset % 0x04) { + /* "SCEl" "SEEN" "SEFR" are aligned to 0x80 usually, but causes problems if not 32b-aligned (ex. Need for Speed 2 PC) */ + if ((id == 0x5343456C || id == 0x5345454E || id == 0x53454652) && block_offset % 0x04) { VGM_LOG_ONCE("EA SCHl: mis-aligned end offset found\n"); block_offset += 0x04 - (block_offset % 0x04); } @@ -832,10 +833,10 @@ static off_t get_ea_stream_mpeg_start_offset(STREAMFILE* streamFile, off_t start if (block_size > 0x00F00000) /* size is always LE, except in early SAT/MAC */ block_size = read_32bitBE(block_offset+0x04,streamFile); - if (id == 0x5343446C) { /* "SCDl" data block found */ + if (id == 0x5343446C || id == 0x5344454E || id == 0x53444652) { /* "SCDl/SDEN/SDFR" data block found */ off_t offset = read_32bit(block_offset+0x0c,streamFile); /* first value seems ok, second is something else in EALayer3 */ return block_offset + 0x0c + ea->channels*0x04 + offset; - } else if (id == 0x5343436C) { /* "SCCl" data count found */ + } else if (id == 0x5343436C || id == 0x5343454E || id == 0x53434652) { /* "SCCl/SCEN/SCFR" data count found */ block_offset += block_size; /* size includes header */ continue; } else { diff --git a/src/meta/ea_wve_ad10.c b/src/meta/ea_wve_ad10.c new file mode 100644 index 00000000..2a197e24 --- /dev/null +++ b/src/meta/ea_wve_ad10.c @@ -0,0 +1,56 @@ +#include "meta.h" +#include "../coding/coding.h" +#include "../layout/layout.h" + +/* EA WVE (Ad10) - from Electronic Arts PS movies [Wing Commander 3/4 (PS)] */ +VGMSTREAM * init_vgmstream_ea_wve_ad10(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + off_t start_offset; + int loop_flag, channel_count; + + + /* checks */ + if (!check_extensions(streamFile, "wve")) + goto fail; + + start_offset = 0x00; + if (read_32bitBE(start_offset, streamFile) != 0x41643130 && /* "Ad10" */ + read_32bitBE(start_offset, streamFile) != 0x41643131) /* "Ad11" (last block, but could be first) */ + goto fail; + loop_flag = 0; + /* no header = no channels, but seems if the first PS-ADPCM header is 00 then it's mono, somehow + * (ex. Wing Commander 3 intro / Wing Commander 4 = stereo, rest of Wing Commander 3 = mono) */ + channel_count = read_8bit(start_offset+0x08,streamFile) != 0 ? 2 : 1; + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count, loop_flag); + if (!vgmstream) goto fail; + + vgmstream->sample_rate = 22050; + vgmstream->meta_type = meta_EA_WVE_AD10; + vgmstream->coding_type = coding_PSX; + vgmstream->layout_type = layout_blocked_ea_wve_ad10; + + if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + goto fail; + + /* calc num_samples manually */ + { + vgmstream->num_samples = 0; + block_update_ea_wve_ad10(start_offset,vgmstream); + do { + vgmstream->num_samples += ps_bytes_to_samples(vgmstream->current_block_size, 1); + block_update_ea_wve_ad10(vgmstream->next_block_offset,vgmstream); + } + while (vgmstream->next_block_offset < get_streamfile_size(streamFile)); + } + + block_update_ea_wve_ad10(start_offset, vgmstream); + + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} diff --git a/src/meta/ea_wve_au00.c b/src/meta/ea_wve_au00.c new file mode 100644 index 00000000..0f20029a --- /dev/null +++ b/src/meta/ea_wve_au00.c @@ -0,0 +1,61 @@ +#include "meta.h" +#include "../coding/coding.h" +#include "../layout/layout.h" + +/* EA WVE (VLC0/au00) - from Electronic Arts PS movies [Future Cop - L.A.P.D. (PS), Supercross 2000 (PS)] */ +VGMSTREAM * init_vgmstream_ea_wve_au00(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + off_t start_offset; + int loop_flag, channel_count; + + + /* checks */ + if (!check_extensions(streamFile, "wve")) + goto fail; + if (read_32bitBE(0x00,streamFile) != 0x564C4330) /* "VLC0" */ + goto fail; + + start_offset = read_32bitBE(0x04,streamFile); + if (read_32bitBE(start_offset, streamFile) != 0x61753030 && /* "au00" */ + read_32bitBE(start_offset, streamFile) != 0x61753031) /* "au01" (last block, but could be first) */ + goto fail; + loop_flag = 0; + channel_count = 2; + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count, loop_flag); + if (!vgmstream) goto fail; + + vgmstream->sample_rate = 22050; + vgmstream->meta_type = meta_EA_WVE_AU00; + + /* You'd think they'd use coding_EA_XA_int but instead it's PS-ADPCM without flags and 0x0f frame size + * (equivalent to configurable PS-ADPCM), surely to shoehorn EA-XA sizes into the PS1 hardware decoder */ + vgmstream->coding_type = coding_PSX_cfg; + vgmstream->interleave_block_size = 0x0f; + vgmstream->layout_type = layout_blocked_ea_wve_au00; + + if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + goto fail; + + /* calc num_samples manually */ + { + vgmstream->num_samples = 0; + block_update_ea_wve_au00(start_offset,vgmstream); + do { + /* ps_cfg_bytes_to_samples */ + vgmstream->num_samples += vgmstream->current_block_size / vgmstream->interleave_block_size*channel_count * 28 / channel_count; + block_update_ea_wve_au00(vgmstream->next_block_offset,vgmstream); + } + while (vgmstream->next_block_offset < get_streamfile_size(streamFile)); + } + + block_update_ea_wve_au00(start_offset, vgmstream); + + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} diff --git a/src/meta/emff.c b/src/meta/emff.c index 10cb7645..79928e66 100644 --- a/src/meta/emff.c +++ b/src/meta/emff.c @@ -44,7 +44,7 @@ VGMSTREAM * init_vgmstream_emff_ps2(STREAMFILE *streamFile) { vgmstream->channels = channel_count; vgmstream->coding_type = coding_PSX; - vgmstream->layout_type = layout_emff_ps2_blocked; + vgmstream->layout_type = layout_blocked_emff_ps2; vgmstream->interleave_block_size = 0x10; vgmstream->meta_type = meta_EMFF_PS2; @@ -59,7 +59,7 @@ VGMSTREAM * init_vgmstream_emff_ps2(STREAMFILE *streamFile) { } /* Calc num_samples */ - emff_ps2_block_update(start_offset,vgmstream); + block_update_emff_ps2(start_offset,vgmstream); vgmstream->num_samples = read_32bitLE(0x8,streamFile); if (loop_flag) { vgmstream->loop_start_sample = (read_32bitLE(0x28,streamFile)-start_offset)*28/16/channel_count; @@ -148,7 +148,7 @@ VGMSTREAM * init_vgmstream_emff_ngc(STREAMFILE *streamFile) { goto fail; } - vgmstream->layout_type = layout_emff_ngc_blocked; + vgmstream->layout_type = layout_blocked_emff_ngc; vgmstream->interleave_block_size = 0x10; vgmstream->meta_type = meta_EMFF_NGC; @@ -163,7 +163,7 @@ VGMSTREAM * init_vgmstream_emff_ngc(STREAMFILE *streamFile) { } /* Calc num_samples */ - emff_ngc_block_update(start_offset,vgmstream); + block_update_emff_ngc(start_offset,vgmstream); vgmstream->num_samples = read_32bitBE(0x8,streamFile);; if (loop_flag) { vgmstream->loop_start_sample = (read_32bitBE(0x28,streamFile))*14/8/channel_count; diff --git a/src/meta/ffmpeg.c b/src/meta/ffmpeg.c index 87c8662e..dc063974 100644 --- a/src/meta/ffmpeg.c +++ b/src/meta/ffmpeg.c @@ -3,6 +3,8 @@ #ifdef VGM_USE_FFMPEG +static int read_pos_file(uint8_t * buf, size_t bufsize, STREAMFILE *streamFile); + /** * Generic init FFmpeg and vgmstream for any file supported by FFmpeg. * Called by vgmstream when trying to identify the file type (if the player allows it). @@ -75,4 +77,47 @@ fail: return NULL; } + +/** + * open file containing looping data and copy to buffer + * + * returns true if found and copied + */ +int read_pos_file(uint8_t * buf, size_t bufsize, STREAMFILE *streamFile) { + char posname[PATH_LIMIT]; + char filename[PATH_LIMIT]; + /*size_t bytes_read;*/ + STREAMFILE * streamFilePos= NULL; + + streamFile->get_name(streamFile,filename,sizeof(filename)); + + if (strlen(filename)+4 > sizeof(posname)) goto fail; + + /* try to open a posfile using variations: "(name.ext).pos" */ + { + strcpy(posname, filename); + strcat(posname, ".pos"); + streamFilePos = streamFile->open(streamFile,posname,STREAMFILE_DEFAULT_BUFFER_SIZE); + if (streamFilePos) goto found; + + goto fail; + } + +found: + //if (get_streamfile_size(streamFilePos) != bufsize) goto fail; + + /* allow pos files to be of different sizes in case of new features, just fill all we can */ + memset(buf, 0, bufsize); + read_streamfile(buf, 0, bufsize, streamFilePos); + + close_streamfile(streamFilePos); + + return 1; + +fail: + if (streamFilePos) close_streamfile(streamFilePos); + + return 0; +} + #endif diff --git a/src/meta/fsb_encrypted.c b/src/meta/fsb_encrypted.c index 98af7df9..1dd17d5f 100644 --- a/src/meta/fsb_encrypted.c +++ b/src/meta/fsb_encrypted.c @@ -146,7 +146,7 @@ static STREAMFILE* setup_fsb_streamfile(STREAMFILE *streamFile, const uint8_t * if (!new_streamFile) goto fail; temp_streamFile = new_streamFile; - new_streamFile = open_io_streamfile(temp_streamFile, &io_data,io_data_size, fsb_decryption_read); + new_streamFile = open_io_streamfile(temp_streamFile, &io_data,io_data_size, fsb_decryption_read,NULL); if (!new_streamFile) goto fail; temp_streamFile = new_streamFile; diff --git a/src/meta/genh.c b/src/meta/genh.c index 3b2b6ddd..70cac106 100644 --- a/src/meta/genh.c +++ b/src/meta/genh.c @@ -157,6 +157,8 @@ VGMSTREAM * init_vgmstream_genh(STREAMFILE *streamFile) { coding = coding_DVI_IMA_int; if (coding == coding_IMA) coding = coding_IMA_int; + if (coding == coding_AICA) + coding = coding_AICA_int; } /* to avoid endless loops */ @@ -173,7 +175,7 @@ VGMSTREAM * init_vgmstream_genh(STREAMFILE *streamFile) { } /* setup adpcm */ - if (coding == coding_AICA) { + if (coding == coding_AICA || coding == coding_AICA_int) { int i; for (i=0;ichannels;i++) { vgmstream->ch[i].adpcm_step_index = 0x7f; diff --git a/src/meta/gsp_gsb.c b/src/meta/gsp_gsb.c index 4e1c3021..d51ec240 100644 --- a/src/meta/gsp_gsb.c +++ b/src/meta/gsp_gsb.c @@ -16,7 +16,7 @@ VGMSTREAM * init_vgmstream_gsp_gsb(STREAMFILE *streamFile) { if (!check_extensions(streamFile,"gsb")) goto fail; - streamFileGSP = open_stream_ext(streamFile, "gsp"); + streamFileGSP = open_streamfile_by_ext(streamFile, "gsp"); if (!streamFileGSP) goto fail; /* check header */ @@ -63,7 +63,7 @@ VGMSTREAM * init_vgmstream_gsp_gsb(STREAMFILE *streamFile) { size_t num_blocks; vgmstream->coding_type = coding_NGC_DSP; - vgmstream->layout_type = layout_gsb_blocked; + vgmstream->layout_type = layout_blocked_gsb; if (!find_chunk_be(streamFileGSP, 0x47434558,first_offset,1, &chunk_offset,NULL)) goto fail; /*"GCEX"*/ diff --git a/src/meta/halpst.c b/src/meta/halpst.c index caec16ad..24e6e23c 100644 --- a/src/meta/halpst.c +++ b/src/meta/halpst.c @@ -91,7 +91,7 @@ VGMSTREAM * init_vgmstream_halpst(STREAMFILE *streamFile) { } vgmstream->coding_type = coding_NGC_DSP; - vgmstream->layout_type = layout_halpst_blocked; + vgmstream->layout_type = layout_blocked_halpst; vgmstream->meta_type = meta_HALPST; /* load decode coefs */ @@ -120,7 +120,7 @@ VGMSTREAM * init_vgmstream_halpst(STREAMFILE *streamFile) { } /* start me up */ - halpst_block_update(header_length,vgmstream); + block_update_halpst(header_length,vgmstream); return vgmstream; diff --git a/src/meta/meta.h b/src/meta/meta.h index 9149b4de..d22ca4b0 100644 --- a/src/meta/meta.h +++ b/src/meta/meta.h @@ -285,6 +285,7 @@ VGMSTREAM * init_vgmstream_psx_fag(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_ps2_mihb(STREAMFILE * streamFile); +VGMSTREAM * init_vgmstream_ngc_pdt_split(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_ngc_pdt(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_wii_mus(STREAMFILE * streamFile); @@ -717,4 +718,16 @@ VGMSTREAM * init_vgmstream_smv(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_nxap(STREAMFILE * streamFile); +VGMSTREAM * init_vgmstream_ea_wve_au00(STREAMFILE * streamFile); + +VGMSTREAM * init_vgmstream_ea_wve_ad10(STREAMFILE * streamFile); + +VGMSTREAM * init_vgmstream_sthd(STREAMFILE * streamFile); + +VGMSTREAM * init_vgmstream_ngc_dsp_std_le(STREAMFILE *streamFile); + +VGMSTREAM * init_vgmstream_pcm_sre(STREAMFILE *streamFile); + +VGMSTREAM * init_vgmstream_dsp_mcadpcm(STREAMFILE *streamFile); + #endif /*_META_H*/ diff --git a/src/meta/mp4.c b/src/meta/mp4.c index b232319a..ac53aa53 100644 --- a/src/meta/mp4.c +++ b/src/meta/mp4.c @@ -240,7 +240,7 @@ VGMSTREAM * init_vgmstream_mp4_aac_ffmpeg(STREAMFILE *streamFile) { vgmstream->codec_data = ffmpeg_data; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; - vgmstream->meta_type = meta_FFmpeg; + vgmstream->meta_type = meta_MP4; vgmstream->num_samples = ffmpeg_data->totalSamples; vgmstream->sample_rate = ffmpeg_data->sampleRate; diff --git a/src/meta/naomi_adpcm.c b/src/meta/naomi_adpcm.c index 7ca6ec96..7f176fa7 100644 --- a/src/meta/naomi_adpcm.c +++ b/src/meta/naomi_adpcm.c @@ -31,7 +31,7 @@ VGMSTREAM * init_vgmstream_naomi_adpcm(STREAMFILE *streamFile) { start_offset = 0x40; vgmstream->channels = channel_count; vgmstream->sample_rate = 44100; - vgmstream->coding_type = coding_AICA; + vgmstream->coding_type = coding_AICA_int; vgmstream->num_samples = (get_streamfile_size(streamFile)-start_offset); if (loop_flag) { vgmstream->loop_start_sample = 0; diff --git a/src/meta/naomi_spsd.c b/src/meta/naomi_spsd.c index 8d329b6b..5780774b 100644 --- a/src/meta/naomi_spsd.c +++ b/src/meta/naomi_spsd.c @@ -1,48 +1,105 @@ #include "meta.h" -#include "../util.h" +#include "../coding/coding.h" -/* SPSD - Naomi GD-ROM streams [Guilty Gear X (Naomi), Crazy Taxi (Naomi), Virtua Tennis 2 (Naomi)] */ +/* SPSD - Naomi (arcade) streams [Guilty Gear X (Naomi), Crazy Taxi (Naomi), Virtua Tennis 2 (Naomi)] */ VGMSTREAM * init_vgmstream_naomi_spsd(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; off_t start_offset; size_t data_size; - int loop_flag, channel_count; + int loop_flag, channel_count, codec, flags, index; /* checks */ + /* .spsd: header id */ if (!check_extensions(streamFile, "spsd")) goto fail; if (read_32bitBE(0x00,streamFile) != 0x53505344) /* "SPSD" */ goto fail; - loop_flag = 0; - channel_count = 2; + if (read_32bitBE(0x04,streamFile) != 0x01010004 && /* standard version */ + read_32bitBE(0x04,streamFile) != 0x00010004) /* uncommon version [Crazy Taxi (Naomi)] */ + goto fail; + + codec = read_8bit(0x08,streamFile); + flags = read_8bit(0x09,streamFile); + index = read_16bitLE(0x0a,streamFile); + data_size = read_32bitLE(0x0c,streamFile); + //if (data_size + start_offset != get_streamfile_size(streamFile)) + // goto fail; /* some rips out there have incorrect padding */ + + //todo with 0x80 seems 0x2c is a loop_start_sample but must be adjusted to +1 block? (uncommon flag though) + loop_flag = (flags & 0x80); + channel_count = ((flags & 0x01) || (flags & 0x02)) ? 2 : 1; /* 0x02 is rare but looks normal (Virtua Tennis 2) */ start_offset = 0x40; - data_size = get_streamfile_size(streamFile) - start_offset; + /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; vgmstream->sample_rate = (uint16_t)read_16bitLE(0x2A,streamFile); - vgmstream->num_samples = read_32bitLE(0x0C,streamFile); vgmstream->meta_type = meta_NAOMI_SPSD; - switch (read_8bit(0x08,streamFile)) { + switch (codec) { + case 0x00: /* [Virtua Tennis 2 (Naomi), Club Kart - European Session (Naomi)] */ + vgmstream->coding_type = coding_PCM16LE; + vgmstream->num_samples = pcm_bytes_to_samples(data_size,channel_count,16); + vgmstream->loop_start_sample = read_32bitLE(0x2c,streamFile) + pcm_bytes_to_samples(0x2000*channel_count,channel_count,16); + vgmstream->loop_end_sample = vgmstream->num_samples; + break; + case 0x01: /* [Virtua Tennis 2 (Naomi)] */ vgmstream->coding_type = coding_PCM8; + vgmstream->num_samples = pcm_bytes_to_samples(data_size,channel_count,8); + vgmstream->loop_start_sample = read_32bitLE(0x2c,streamFile) + pcm_bytes_to_samples(0x2000*channel_count,channel_count,8); + vgmstream->loop_end_sample = vgmstream->num_samples; + break; - case 0x03: - vgmstream->coding_type = coding_AICA; + + case 0x03: /* standard */ + vgmstream->coding_type = coding_AICA_int; + vgmstream->num_samples = aica_bytes_to_samples(data_size,channel_count); + vgmstream->loop_start_sample = /*read_32bitLE(0x2c,streamFile) +*/ aica_bytes_to_samples(0x2000*channel_count,channel_count); + vgmstream->loop_end_sample = vgmstream->num_samples; break; + default: goto fail; } - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = 0x2000; - if (vgmstream->interleave_block_size) - vgmstream->interleave_last_block_size = (data_size % (vgmstream->interleave_block_size*vgmstream->channels)) / vgmstream->channels; + + /* interleave index, maybe */ + switch(index) { + case 0x0000: + if (channel_count != 1) goto fail; + vgmstream->layout_type = layout_none; + break; + + case 0x000d: + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x2000; + if (vgmstream->interleave_block_size) + vgmstream->interleave_last_block_size = (data_size % (vgmstream->interleave_block_size*vgmstream->channels)) / vgmstream->channels; + break; + + case 0x00ff: + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = data_size / channel_count; + break; + + default: + goto fail; + } + + /* todo seems to decode slightly incorrectly in after certain data (loop section start?) + * may depend on values in 0x20 or 0x2c [ex. Marvel vs Capcom 2 (Naomi)] + * at 0x30(4*ch) is some config per channel but doesn't seem to affect ADPCM (found with PCM too) */ + { + int i; + for (i = 0; i < channel_count; i++) { + vgmstream->ch[i].adpcm_step_index = 0x7f; + } + } if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) diff --git a/src/meta/ngc_dsp_std.c b/src/meta/ngc_dsp_std.c index 24c8f65f..0eeed7c5 100644 --- a/src/meta/ngc_dsp_std.c +++ b/src/meta/ngc_dsp_std.c @@ -30,32 +30,41 @@ struct dsp_header { }; /* read the above struct; returns nonzero on failure */ -static int read_dsp_header(struct dsp_header *header, off_t offset, STREAMFILE *file) { +static int read_dsp_header_endian(struct dsp_header *header, off_t offset, STREAMFILE *streamFile, int big_endian) { + int32_t (*get_32bit)(uint8_t *) = big_endian ? get_32bitBE : get_32bitLE; + int16_t (*get_16bit)(uint8_t *) = big_endian ? get_16bitBE : get_16bitLE; int i; uint8_t buf[0x4e]; /* usually padded out to 0x60 */ - if (read_streamfile(buf, offset, 0x4e, file) != 0x4e) + + if (read_streamfile(buf, offset, 0x4e, streamFile) != 0x4e) return 1; - header->sample_count = get_32bitBE(buf+0x00); - header->nibble_count = get_32bitBE(buf+0x04); - header->sample_rate = get_32bitBE(buf+0x08); - header->loop_flag = get_16bitBE(buf+0x0c); - header->format = get_16bitBE(buf+0x0e); - header->loop_start_offset = get_32bitBE(buf+0x10); - header->loop_end_offset = get_32bitBE(buf+0x14); - header->ca = get_32bitBE(buf+0x18); + header->sample_count = get_32bit(buf+0x00); + header->nibble_count = get_32bit(buf+0x04); + header->sample_rate = get_32bit(buf+0x08); + header->loop_flag = get_16bit(buf+0x0c); + header->format = get_16bit(buf+0x0e); + header->loop_start_offset = get_32bit(buf+0x10); + header->loop_end_offset = get_32bit(buf+0x14); + header->ca = get_32bit(buf+0x18); for (i=0; i < 16; i++) - header->coef[i] = get_16bitBE(buf+0x1c+i*0x2); - header->gain = get_16bitBE(buf+0x3c); - header->initial_ps = get_16bitBE(buf+0x3e); - header->initial_hist1 = get_16bitBE(buf+0x40); - header->initial_hist2 = get_16bitBE(buf+0x42); - header->loop_ps = get_16bitBE(buf+0x44); - header->loop_hist1 = get_16bitBE(buf+0x46); - header->loop_hist2 = get_16bitBE(buf+0x48); - header->channel_count = get_16bitBE(buf+0x4a); - header->block_size = get_16bitBE(buf+0x4c); + header->coef[i] = get_16bit(buf+0x1c+i*0x02); + header->gain = get_16bit(buf+0x3c); + header->initial_ps = get_16bit(buf+0x3e); + header->initial_hist1 = get_16bit(buf+0x40); + header->initial_hist2 = get_16bit(buf+0x42); + header->loop_ps = get_16bit(buf+0x44); + header->loop_hist1 = get_16bit(buf+0x46); + header->loop_hist2 = get_16bit(buf+0x48); + header->channel_count = get_16bit(buf+0x4a); + header->block_size = get_16bit(buf+0x4c); return 0; } +static int read_dsp_header(struct dsp_header *header, off_t offset, STREAMFILE *file) { + return read_dsp_header_endian(header, offset, file, 1); +} +static int read_dsp_header_le(struct dsp_header *header, off_t offset, STREAMFILE *file) { + return read_dsp_header_endian(header, offset, file, 0); +} static void setup_vgmstream_dsp(VGMSTREAM* vgmstream, struct dsp_header* ch_header) { @@ -71,12 +80,12 @@ static void setup_vgmstream_dsp(VGMSTREAM* vgmstream, struct dsp_header* ch_head } } -static int dsp_load_header(struct dsp_header* ch_header, int channels, STREAMFILE *streamFile, off_t offset, size_t spacing) { +static int dsp_load_header_endian(struct dsp_header* ch_header, int channels, STREAMFILE *streamFile, off_t offset, size_t spacing, int big_endian) { int i; /* load standard dsp header per channel */ for (i = 0; i < channels; i++) { - if (read_dsp_header(&ch_header[i], offset + i*spacing, streamFile)) + if (read_dsp_header_endian(&ch_header[i], offset + i*spacing, streamFile, big_endian)) goto fail; } @@ -84,6 +93,12 @@ static int dsp_load_header(struct dsp_header* ch_header, int channels, STREAMFIL fail: return 0; } +static int dsp_load_header(struct dsp_header* ch_header, int channels, STREAMFILE *streamFile, off_t offset, size_t spacing) { + return dsp_load_header_endian(ch_header, channels, streamFile, offset, spacing, 1); +} +static int dsp_load_header_le(struct dsp_header* ch_header, int channels, STREAMFILE *streamFile, off_t offset, size_t spacing) { + return dsp_load_header_endian(ch_header, channels, streamFile, offset, spacing, 0); +} static int check_dsp_format(struct dsp_header* ch_header, int channels) { int i; @@ -149,30 +164,29 @@ fail: /* ********************************* */ -/* the standard .dsp, as generated by DSPADPCM.exe */ +/* .dsp - standard dsp as generated by DSPADPCM.exe */ VGMSTREAM * init_vgmstream_ngc_dsp_std(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; - struct dsp_header header; - const off_t start_offset = 0x60; - int i; + const size_t header_size = 0x60; + off_t start_offset; + int i, channel_count; - /* check extension, case insensitive */ + /* checks */ /* .dsp: standard, .adp: Dr. Muto (GC) mono files */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("dsp",filename_extension(filename)) && - strcasecmp("adp", filename_extension(filename))) goto fail; + if (!check_extensions(streamFile, "dsp,adp")) + goto fail; - if (read_dsp_header(&header, 0, streamFile)) goto fail; + if (read_dsp_header(&header, 0x00, streamFile)) + goto fail; + + channel_count = 1; + start_offset = header_size; - /* check initial predictor/scale */ if (header.initial_ps != (uint8_t)read_8bit(start_offset,streamFile)) - goto fail; - - /* check type==0 and gain==0 */ + goto fail; /* check initial predictor/scale */ if (header.format || header.gain) - goto fail; + goto fail; /* check type==0 and gain==0 */ /* Check for a matching second header. If we find one and it checks * out thoroughly, we're probably not dealing with a genuine mono DSP. @@ -180,13 +194,14 @@ VGMSTREAM * init_vgmstream_ngc_dsp_std(STREAMFILE *streamFile) { * predictor/scale check if the first byte is 0 */ { struct dsp_header header2; - - read_dsp_header(&header2, 0x60, streamFile); + read_dsp_header(&header2, header_size, streamFile); if (header.sample_count == header2.sample_count && header.nibble_count == header2.nibble_count && header.sample_rate == header2.sample_rate && - header.loop_flag == header2.loop_flag) goto fail; + header.loop_flag == header2.loop_flag) { + goto fail; + } } if (header.loop_flag) { @@ -194,7 +209,7 @@ VGMSTREAM * init_vgmstream_ngc_dsp_std(STREAMFILE *streamFile) { /* check loop predictor/scale */ loop_off = header.loop_start_offset/16*8; if (header.loop_ps != (uint8_t)read_8bit(start_offset+loop_off,streamFile)) { - /* rarely won't match (ex ESPN 2002), not sure if header or calc problem, but doesn't seem to matter + /* rarely won't match (ex ESPN 2002), not sure if header or calc problem, but doesn't seem to matter * (there may be a "click" when looping, or loop values may be too big and loop disabled anyway) */ VGM_LOG("DSP (std): bad loop_predictor\n"); //header.loop_flag = 0; @@ -202,110 +217,162 @@ VGMSTREAM * init_vgmstream_ngc_dsp_std(STREAMFILE *streamFile) { } } - /* compare num_samples with nibble count */ - /* - fprintf(stderr,"num samples (literal): %d\n",read_32bitBE(0,streamFile)); - fprintf(stderr,"num samples (nibbles): %d\n",dsp_nibbles_to_samples(read_32bitBE(4,streamFile))); - */ /* build the VGMSTREAM */ - - - vgmstream = allocate_vgmstream(1,header.loop_flag); + vgmstream = allocate_vgmstream(channel_count,header.loop_flag); if (!vgmstream) goto fail; - /* fill in the vital statistics */ - vgmstream->num_samples = header.sample_count; vgmstream->sample_rate = header.sample_rate; - - vgmstream->loop_start_sample = dsp_nibbles_to_samples( - header.loop_start_offset); - vgmstream->loop_end_sample = dsp_nibbles_to_samples( - header.loop_end_offset)+1; - - /* don't know why, but it does happen*/ - if (vgmstream->loop_end_sample > vgmstream->num_samples) + vgmstream->num_samples = header.sample_count; + vgmstream->loop_start_sample = dsp_nibbles_to_samples(header.loop_start_offset); + vgmstream->loop_end_sample = dsp_nibbles_to_samples(header.loop_end_offset)+1; + if (vgmstream->loop_end_sample > vgmstream->num_samples) /* don't know why, but it does happen */ vgmstream->loop_end_sample = vgmstream->num_samples; + vgmstream->meta_type = meta_DSP_STD; vgmstream->coding_type = coding_NGC_DSP; vgmstream->layout_type = layout_none; - vgmstream->meta_type = meta_DSP_STD; - /* coeffs */ - for (i=0;i<16;i++) - vgmstream->ch[0].adpcm_coef[i] = header.coef[i]; - - /* initial history */ - /* always 0 that I've ever seen, but for completeness... */ - vgmstream->ch[0].adpcm_history1_16 = header.initial_hist1; - vgmstream->ch[0].adpcm_history2_16 = header.initial_hist2; - - /* open the file for reading */ - vgmstream->ch[0].streamfile = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); - - if (!vgmstream->ch[0].streamfile) goto fail; - - vgmstream->ch[0].channel_start_offset= - vgmstream->ch[0].offset=start_offset; + { + /* adpcm coeffs/history */ + for (i = 0; i < 16; i++) + vgmstream->ch[0].adpcm_coef[i] = header.coef[i]; + vgmstream->ch[0].adpcm_history1_16 = header.initial_hist1; + vgmstream->ch[0].adpcm_history2_16 = header.initial_hist2; + } + if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + goto fail; return vgmstream; fail: - /* clean up anything we may have opened */ - if (vgmstream) close_vgmstream(vgmstream); + close_vgmstream(vgmstream); return NULL; } -/* the standard multi-channel .dsp, as generated by DSPADPCM.exe */ +/* .dsp - little endian dsp, possibly main Switch .dsp [LEGO Worlds (Switch)] */ +VGMSTREAM * init_vgmstream_ngc_dsp_std_le(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + struct dsp_header header; + const size_t header_size = 0x60; + off_t start_offset; + int i, channel_count; + /* checks */ + /* .adpcm: LEGO Worlds */ + if (!check_extensions(streamFile, "adpcm")) + goto fail; + + if (read_dsp_header_le(&header, 0x00, streamFile)) + goto fail; + + channel_count = 1; + start_offset = header_size; + + if (header.initial_ps != (uint8_t)read_8bit(start_offset,streamFile)) + goto fail; /* check initial predictor/scale */ + if (header.format || header.gain) + goto fail; /* check type==0 and gain==0 */ + + /* Check for a matching second header. If we find one and it checks + * out thoroughly, we're probably not dealing with a genuine mono DSP. + * In many cases these will pass all the other checks, including the + * predictor/scale check if the first byte is 0 */ + { + struct dsp_header header2; + read_dsp_header_le(&header2, header_size, streamFile); + + if (header.sample_count == header2.sample_count && + header.nibble_count == header2.nibble_count && + header.sample_rate == header2.sample_rate && + header.loop_flag == header2.loop_flag) { + goto fail; + } + } + + if (header.loop_flag) { + off_t loop_off; + /* check loop predictor/scale */ + loop_off = header.loop_start_offset/16*8; + if (header.loop_ps != (uint8_t)read_8bit(start_offset+loop_off,streamFile)) { + /* rarely won't match (ex ESPN 2002), not sure if header or calc problem, but doesn't seem to matter + * (there may be a "click" when looping, or loop values may be too big and loop disabled anyway) */ + VGM_LOG("DSP (std): bad loop_predictor\n"); + //header.loop_flag = 0; + //goto fail; + } + } + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count,header.loop_flag); + if (!vgmstream) goto fail; + + vgmstream->sample_rate = header.sample_rate; + vgmstream->num_samples = header.sample_count; + vgmstream->loop_start_sample = dsp_nibbles_to_samples(header.loop_start_offset); + vgmstream->loop_end_sample = dsp_nibbles_to_samples(header.loop_end_offset)+1; + if (vgmstream->loop_end_sample > vgmstream->num_samples) /* don't know why, but it does happen */ + vgmstream->loop_end_sample = vgmstream->num_samples; + + vgmstream->meta_type = meta_DSP_STD; + vgmstream->coding_type = coding_NGC_DSP; + vgmstream->layout_type = layout_none; + + { + /* adpcm coeffs/history */ + for (i = 0; i < 16; i++) + vgmstream->ch[0].adpcm_coef[i] = header.coef[i]; + vgmstream->ch[0].adpcm_history1_16 = header.initial_hist1; + vgmstream->ch[0].adpcm_history2_16 = header.initial_hist2; + } + + if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + goto fail; + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} + +/* .dsp - standard multi-channel dsp as generated by DSPADPCM.exe (later revisions) */ VGMSTREAM * init_vgmstream_ngc_mdsp_std(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; - struct dsp_header header; - const off_t header_size = 0x60; + const size_t header_size = 0x60; off_t start_offset; int i, c, channel_count; - /* check extension, case insensitive */ - streamFile->get_name(streamFile, filename, sizeof(filename)); - if (strcasecmp("dsp", filename_extension(filename)) && - strcasecmp("mdsp", filename_extension(filename))) goto fail; + /* checks */ + if (!check_extensions(streamFile, "dsp,mdsp")) + goto fail; - if (read_dsp_header(&header, 0, streamFile)) goto fail; + if (read_dsp_header(&header, 0x00, streamFile)) + goto fail; channel_count = header.channel_count==0 ? 1 : header.channel_count; start_offset = header_size * channel_count; - /* check initial predictor/scale */ if (header.initial_ps != (uint8_t)read_8bit(start_offset, streamFile)) - goto fail; - - /* check type==0 and gain==0 */ + goto fail; /* check initial predictor/scale */ if (header.format || header.gain) - goto fail; + goto fail; /* check type==0 and gain==0 */ /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count, header.loop_flag); if (!vgmstream) goto fail; - /* fill in the vital statistics */ - vgmstream->num_samples = header.sample_count; vgmstream->sample_rate = header.sample_rate; - - vgmstream->loop_start_sample = dsp_nibbles_to_samples( - header.loop_start_offset); - vgmstream->loop_end_sample = dsp_nibbles_to_samples( - header.loop_end_offset) + 1; - - /* don't know why, but it does happen*/ - if (vgmstream->loop_end_sample > vgmstream->num_samples) + vgmstream->num_samples = header.sample_count; + vgmstream->loop_start_sample = dsp_nibbles_to_samples(header.loop_start_offset); + vgmstream->loop_end_sample = dsp_nibbles_to_samples(header.loop_end_offset) + 1; + if (vgmstream->loop_end_sample > vgmstream->num_samples) /* don't know why, but it does happen*/ vgmstream->loop_end_sample = vgmstream->num_samples; + vgmstream->meta_type = meta_DSP_STD; vgmstream->coding_type = coding_NGC_DSP; vgmstream->layout_type = channel_count == 1 ? layout_none : layout_interleave; - vgmstream->meta_type = meta_DSP_STD; vgmstream->interleave_block_size = header.block_size * 8; if (vgmstream->interleave_block_size) vgmstream->interleave_last_block_size = (header.nibble_count / 2 % vgmstream->interleave_block_size + 7) / 8 * 8; @@ -313,12 +380,9 @@ VGMSTREAM * init_vgmstream_ngc_mdsp_std(STREAMFILE *streamFile) { for (i = 0; i < channel_count; i++) { if (read_dsp_header(&header, header_size * i, streamFile)) goto fail; - /* coeffs */ + /* adpcm coeffs/history */ for (c = 0; c < 16; c++) vgmstream->ch[i].adpcm_coef[c] = header.coef[c]; - - /* initial history */ - /* always 0 that I've ever seen, but for completeness... */ vgmstream->ch[i].adpcm_history1_16 = header.initial_hist1; vgmstream->ch[i].adpcm_history2_16 = header.initial_hist2; } @@ -328,8 +392,7 @@ VGMSTREAM * init_vgmstream_ngc_mdsp_std(STREAMFILE *streamFile) { return vgmstream; fail: - /* clean up anything we may have opened */ - if (vgmstream) close_vgmstream(vgmstream); + close_vgmstream(vgmstream); return NULL; } @@ -1226,7 +1289,7 @@ fail: } #define WSD_MAX_CHANNELS 2 -/* .wsd - Custom heade + full interleaved dsp [Phantom Brave (Wii)] */ +/* .wsd - Custom header + full interleaved dsp [Phantom Brave (Wii)] */ VGMSTREAM * init_vgmstream_wii_wsd(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; off_t start_offset, header_offset; @@ -1999,3 +2062,63 @@ fail: close_vgmstream(vgmstream); return NULL; } + +#define MCADPCM_MAX_CHANNELS 2 +/* .mcadpcm - Custom header + full interleaved dsp [Skyrim (Switch)] */ +VGMSTREAM * init_vgmstream_dsp_mcadpcm(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + off_t start_offset, header_offset; + size_t header_spacing, interleave; + int channel_count; + struct dsp_header ch_header[MCADPCM_MAX_CHANNELS]; + + /* checks */ + if (!check_extensions(streamFile, "mcadpcm")) + goto fail; +VGM_LOG("1\n"); + if (read_32bitLE(0x08,streamFile) != read_32bitLE(0x10,streamFile)) /* dsp sizes */ + goto fail; +VGM_LOG("2\n"); + channel_count = read_32bitLE(0x00,streamFile); + if (channel_count > MCADPCM_MAX_CHANNELS) goto fail; + + header_offset = read_32bitLE(0x04,streamFile); + header_spacing = read_32bitLE(0x08,streamFile); /* technically dsp size but eh */ + start_offset = header_offset + 0x60; + interleave = header_spacing; +VGM_LOG("3\n"); + /* read dsp */ + if (!dsp_load_header_le(ch_header, channel_count, streamFile,header_offset,header_spacing)) goto fail; +VGM_LOG("4\n"); + if (!check_dsp_format(ch_header, channel_count)) goto fail; +VGM_LOG("5\n"); + if (!check_dsp_samples(ch_header, channel_count)) goto fail; +VGM_LOG("6\n"); + if (!check_dsp_initial_ps(ch_header, channel_count, streamFile,start_offset,interleave)) goto fail; +VGM_LOG("7\n"); + if (!check_dsp_loop_ps(ch_header, channel_count, streamFile,start_offset,interleave)) goto fail; +VGM_LOG("8\n"); + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count,ch_header[0].loop_flag); + if (!vgmstream) goto fail; + + vgmstream->sample_rate = ch_header[0].sample_rate; + vgmstream->num_samples = ch_header[0].sample_count; + vgmstream->loop_start_sample = dsp_nibbles_to_samples(ch_header[0].loop_start_offset); + vgmstream->loop_end_sample = dsp_nibbles_to_samples(ch_header[0].loop_end_offset)+1; + + vgmstream->meta_type = meta_DSP_MCADPCM; + vgmstream->coding_type = coding_NGC_DSP; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = interleave; + setup_vgmstream_dsp(vgmstream, ch_header); + + if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + goto fail; + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} diff --git a/src/meta/ngc_pdt.c b/src/meta/ngc_pdt.c index 8417c74d..0ca228d2 100644 --- a/src/meta/ngc_pdt.c +++ b/src/meta/ngc_pdt.c @@ -1,101 +1,215 @@ #include "meta.h" -#include "../util.h" +#include "../coding/coding.h" -/* PDT - Custom Generated File (Mario Party) */ +/* PDT - Hudson's stream container [Adventure Island (GC), Muscle Champion (GC), Mario Party series (GC)] */ VGMSTREAM * init_vgmstream_ngc_pdt(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; - int loop_flag; - int channel_count; + int loop_flag, channel_count, sample_rate; + size_t entries, nibble_size, loop_start; + off_t entries_offset, coefs_offset, header_offset; + off_t channel1_offset = 0, channel2_offset = 0, coef_offset = 0; + int total_subsongs, target_subsong = streamFile->stream_index; + + + /* checks */ + if (!check_extensions(streamFile, "pdt")) + goto fail; + + if (read_16bitBE(0x00,streamFile) != 0x01) /* version? */ + goto fail; + if (read_32bitBE(0x04,streamFile) != 0x04) /* entry size? */ + goto fail; + if (read_32bitBE(0x08,streamFile) != 0x7d00) /* not-sample rate? */ + goto fail; + if (read_32bitBE(0x0c,streamFile) != 0x02 && /* not-channels? */ + read_32bitBE(0x0c,streamFile) != 0x04) + goto fail; + +//VGM_LOG("1\n"); + entries = read_16bitBE(0x02,streamFile); + entries_offset = read_32bitBE(0x10,streamFile); + coefs_offset = read_32bitBE(0x14,streamFile); + //headers_offset = read_32bitBE(0x18,streamFile); /* we'll have pointers to those two */ + //streams_offset = read_32bitBE(0x1c,streamFile); + + /* find subsongs and target header, as entries can be empty/repeated */ + { + /* tables to cache reads as it can be kinda slow with so many loops */ + uint32_t data_offsets[0x2000]; + uint32_t entry_offset, data_offset; + int i,j; + + if (entries > 0x2000) + goto fail; + + total_subsongs = 0; + if (target_subsong == 0) target_subsong = 1; + + header_offset = 0; + for (i = 0; i < entries; i++) { + int is_unique = 1; + + entry_offset = read_32bitBE(entries_offset + i*0x04,streamFile); + if (entry_offset == 0x00) + continue; + data_offset = read_32bitBE(entry_offset+0x10,streamFile); + + /* check if current entry header was repeated (same file offset, difference in flags only) */ + for (j = 0; j < total_subsongs; j++) { + if (data_offsets[j] == data_offset) { + is_unique = 0; + break; + } + } + if (!is_unique) + continue; + + data_offsets[total_subsongs] = data_offset; + total_subsongs++; + + /* target GET, but keep going to count subsongs */ + if (!header_offset && target_subsong == total_subsongs) { + header_offset = entry_offset; + } + } + } + +//VGM_LOG("header: %lx\n", header_offset);//todo + /* parse header */ + { + uint8_t flags; + size_t coef1_entry; + off_t coef1_offset; + + flags = read_8bit(header_offset+0x00,streamFile); + sample_rate = read_32bitBE(header_offset+0x04,streamFile); + /* 0x01: unknown + 0x4000 */ + sample_rate = read_32bitBE(header_offset+0x04,streamFile); + nibble_size = read_32bitBE(header_offset+0x08,streamFile); + loop_start = read_32bitBE(header_offset+0x0c,streamFile); + + channel1_offset = read_32bitBE(header_offset+0x10,streamFile); + coef1_entry = read_16bitBE(header_offset+0x14,streamFile); + coef1_offset = coefs_offset + coef1_entry*0x20; + + if (flags & 0x01) { + //size_t coef2_entry; + //off_t coef2_offset; + + channel2_offset = read_32bitBE(header_offset+0x18,streamFile); + /* always after coef1 in practice */ + //coef2_entry = read_16bitBE(header_offset+0x1c,streamFile); + //coef2_offset = coefs_offset + coef2_entry*0x20; + //if (coef1_offset + 0x20 != coef2_offset) + // goto fail; + } + + coef_offset = coef1_offset; + loop_flag = (flags & 0x02); + channel_count = (flags & 0x01) ? 2 : 1; + } + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count, loop_flag); + if (!vgmstream) goto fail; + + vgmstream->sample_rate = sample_rate; + //vgmstream->num_samples = dsp_bytes_to_samples(data_size, channel_count);//todo remove + vgmstream->num_samples = dsp_nibbles_to_samples(nibble_size); + //vgmstream->loop_start_sample = dsp_bytes_to_samples(loop_start, channel_count);//todo remove + vgmstream->loop_start_sample = dsp_nibbles_to_samples(loop_start); + vgmstream->loop_end_sample = vgmstream->num_samples; + + vgmstream->meta_type = meta_NGC_PDT; + vgmstream->coding_type = coding_NGC_DSP; + vgmstream->layout_type = layout_none; + dsp_read_coefs_be(vgmstream, streamFile, coef_offset, 0x20); + + vgmstream->num_streams = total_subsongs; + vgmstream->stream_size = nibble_size / 2 * channel_count; + + if (!vgmstream_open_stream(vgmstream,streamFile,channel1_offset)) + goto fail; + + /* channels may start at slightly separated offsets */ + if (channel_count == 2) { + vgmstream->ch[1].channel_start_offset = + vgmstream->ch[1].offset = channel2_offset; + } + + + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} + + +/* PDT - custom fake header for split (PDTExt) .ptd [Mario Party (GC)] */ +VGMSTREAM * init_vgmstream_ngc_pdt_split(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; off_t start_offset; - int second_channel_start = -1; - - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("pdt",filename_extension(filename))) goto fail; + int loop_flag, channel_count; - /* check header */ - if (read_32bitBE(0x00,streamFile) != 0x50445420) /* "PDT " */ - goto fail; - if (read_32bitBE(0x04,streamFile) != 0x44535020) /* "DSP " */ - goto fail; - if (read_32bitBE(0x08,streamFile) != 0x48454144) /* "HEAD " */ - goto fail; - if (read_16bitBE(0x0C,streamFile) != 0x4552) /* "ER " */ + + /* checks */ + if (!check_extensions(streamFile, "pdt")) goto fail; - loop_flag = (read_32bitBE(0x1C,streamFile)!=2); + /* 0x10 fake header + chunks of the original header / data pasted together */ + if (read_32bitBE(0x00,streamFile) != 0x50445420 && /* "PDT " */ + read_32bitBE(0x04,streamFile) != 0x44535020 && /* "DSP " */ + read_32bitBE(0x08,streamFile) != 0x48454144 && /* "HEAD " */ + read_16bitBE(0x0C,streamFile) != 0x4552) /* "ER " */ + goto fail; + + start_offset = 0x800; channel_count = (uint16_t)(read_16bitLE(0x0E,streamFile)); + loop_flag = (read_32bitBE(0x1C,streamFile) != 2); - /* build the VGMSTREAM */ + + /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; - /* fill in the vital statistics */ - start_offset = 0x800; - vgmstream->channels = channel_count; vgmstream->sample_rate = read_32bitBE(0x14,streamFile); - vgmstream->coding_type = coding_NGC_DSP; - if (channel_count == 1) - { - vgmstream->num_samples = read_32bitBE(0x18,streamFile)*14/8/channel_count/2; + if (channel_count == 1) { + vgmstream->num_samples = read_32bitBE(0x18,streamFile)*14/8/channel_count/2; if (loop_flag) { vgmstream->loop_start_sample = read_32bitBE(0x1C,streamFile)*14/8/channel_count/2; vgmstream->loop_end_sample = read_32bitBE(0x18,streamFile)*14/8/channel_count/2; } } - else if (channel_count == 2) - { - vgmstream->num_samples = read_32bitBE(0x18,streamFile)*14/8/channel_count; + else if (channel_count == 2) { + vgmstream->num_samples = read_32bitBE(0x18,streamFile)*14/8/channel_count; if (loop_flag) { vgmstream->loop_start_sample = read_32bitBE(0x1C,streamFile)*14/8/channel_count; vgmstream->loop_end_sample = read_32bitBE(0x18,streamFile)*14/8/channel_count; } - second_channel_start = (get_streamfile_size(streamFile)+start_offset)/2; } - else - { - goto fail; + else { + goto fail; } - vgmstream->layout_type = layout_none; vgmstream->meta_type = meta_NGC_PDT; + vgmstream->coding_type = coding_NGC_DSP; + vgmstream->layout_type = layout_none; + dsp_read_coefs_be(vgmstream, streamFile, 0x50, 0x20); - if (vgmstream->coding_type == coding_NGC_DSP) { - int i; - for (i=0;i<16;i++) { - vgmstream->ch[0].adpcm_coef[i] = read_16bitBE(0x50+i*2,streamFile); - } - if (vgmstream->channels == 2) { - for (i=0;i<16;i++) { - vgmstream->ch[1].adpcm_coef[i] = read_16bitBE(0x70+i*2,streamFile); - } - } - } + if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + goto fail; - /* open the file for reading */ - { - int i; - STREAMFILE * file; - file = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); - if (!file) goto fail; - for (i=0;ich[i].streamfile = file; - - vgmstream->ch[0].channel_start_offset=start_offset; - if (channel_count == 2) { - if (second_channel_start == -1) goto fail; - vgmstream->ch[1].channel_start_offset=second_channel_start; - } - vgmstream->ch[i].offset = vgmstream->ch[i].channel_start_offset; - } + if (channel_count == 2) { + vgmstream->ch[1].channel_start_offset = + vgmstream->ch[1].offset = ((get_streamfile_size(streamFile)+start_offset) / channel_count); } return vgmstream; - /* clean up anything we may have opened */ fail: - if (vgmstream) close_vgmstream(vgmstream); + close_vgmstream(vgmstream); return NULL; } diff --git a/src/meta/pc_mxst.c b/src/meta/pc_mxst.c index 2e79f0f1..c33cfc26 100644 --- a/src/meta/pc_mxst.c +++ b/src/meta/pc_mxst.c @@ -179,7 +179,7 @@ VGMSTREAM * init_vgmstream_pc_mxst(STREAMFILE *streamFile) { /* fill in the vital statistics */ vgmstream->channels = channel_count; vgmstream->sample_rate = sample_rate; - vgmstream->layout_type = layout_mxch_blocked; + vgmstream->layout_type = layout_blocked_mxch; vgmstream->meta_type = meta_PC_MXST; if(bits_per_sample == 8) @@ -207,7 +207,7 @@ VGMSTREAM * init_vgmstream_pc_mxst(STREAMFILE *streamFile) { } } - mxch_block_update(start_offset, vgmstream); + block_update_mxch(start_offset, vgmstream); return vgmstream; diff --git a/src/meta/pcm_sre.c b/src/meta/pcm_sre.c new file mode 100644 index 00000000..3a51c1fc --- /dev/null +++ b/src/meta/pcm_sre.c @@ -0,0 +1,76 @@ +#include "meta.h" +#include "../coding/coding.h" + +/* .PCM+SRE. - Capcom's header+data container thing [Viewtiful Joe (PS2)] */ +VGMSTREAM * init_vgmstream_pcm_sre(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + STREAMFILE * streamHeader = NULL; + off_t start_offset; + int loop_flag, channel_count; + size_t table1_entries, table2_entries; + off_t table1_offset, table2_offset, header_offset; + int total_subsongs, target_subsong = streamFile->stream_index; + + + /* checks */ + /* .pcm=data, .sre=header */ + if (!check_extensions(streamFile, "pcm")) + goto fail; + + /* first PS-ADPCM frame should be is null */ + if (read_32bitBE(0x00,streamFile) != 0x00020000 || + read_32bitBE(0x04,streamFile) != 0x00000000 || + read_32bitBE(0x08,streamFile) != 0x00000000 || + read_32bitBE(0x0c,streamFile) != 0x00000000) + goto fail; + + streamHeader = open_streamfile_by_ext(streamFile, "sre"); + if (!streamHeader) goto fail; + + table1_entries = read_32bitLE(0x00, streamHeader); + table1_offset = read_32bitLE(0x04, streamHeader); + table2_entries = read_32bitLE(0x08, streamHeader); + table2_offset = read_32bitLE(0x0c, streamHeader); + if (table1_entries*0x60 + table1_offset != table2_offset) + goto fail; /* just in case */ + + total_subsongs = table2_entries; + if (target_subsong == 0) target_subsong = 1; + if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail; + + header_offset = table2_offset + (target_subsong-1)*0x20; + + channel_count = read_32bitLE(header_offset+0x00,streamHeader); + loop_flag = read_32bitLE(header_offset+0x18,streamHeader); + start_offset = read_32bitLE(header_offset+0x08,streamHeader); + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count, loop_flag); + if (!vgmstream) goto fail; + + vgmstream->sample_rate = read_16bitLE(header_offset+0x04,streamHeader); + vgmstream->meta_type = meta_PCM_SRE; + vgmstream->coding_type = coding_PSX; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x1000; + + vgmstream->num_samples = ps_bytes_to_samples(read_32bitLE(header_offset+0x0c,streamHeader), channel_count); + vgmstream->loop_start_sample = ps_bytes_to_samples(read_32bitLE(header_offset+0x10,streamHeader)*channel_count, channel_count); + vgmstream->loop_end_sample = ps_bytes_to_samples(read_32bitLE(header_offset+0x14,streamHeader)*channel_count, channel_count); + + vgmstream->num_streams = total_subsongs; + vgmstream->stream_size = read_32bitLE(header_offset+0x0c,streamHeader); + + + if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + goto fail; + + close_streamfile(streamHeader); + return vgmstream; + +fail: + close_streamfile(streamHeader); + close_vgmstream(vgmstream); + return NULL; +} diff --git a/src/meta/ps2_adm.c b/src/meta/ps2_adm.c index cd23625f..3e2cad5f 100644 --- a/src/meta/ps2_adm.c +++ b/src/meta/ps2_adm.c @@ -74,7 +74,7 @@ static int get_adm_loop_info(STREAMFILE *streamFile, off_t *loop_start_offset) { int i, name_index = -1, loop_flag; off_t offset; - streamExe = open_stream_name(streamFile, "SLPM_655.55"); + streamExe = open_streamfile_by_filename(streamFile, "SLPM_655.55"); if (!streamExe) goto fail; get_streamfile_filename(streamFile, file_name, PATH_LIMIT); diff --git a/src/meta/ps2_bmdx.c b/src/meta/ps2_bmdx.c index a1e735af..229b7332 100644 --- a/src/meta/ps2_bmdx.c +++ b/src/meta/ps2_bmdx.c @@ -1,92 +1,116 @@ #include "meta.h" -#include "../util.h" +#include "../coding/coding.h" +static STREAMFILE* setup_bmdx_streamfile(STREAMFILE *streamFile, off_t start_offset, size_t data_size); + + +/* .bmdx - from Beatmania IIDX (PS2) games */ VGMSTREAM * init_vgmstream_ps2_bmdx(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; - int loop_flag=0; - int channel_count; + STREAMFILE *temp_streamFile = NULL; off_t start_offset; - int i; + size_t data_size; + int loop_flag, channel_count, encryption; - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("bmdx",filename_extension(filename))) goto fail; - /* check NPSF Header */ + /* checks */ + if (!check_extensions(streamFile, "bmdx")) + goto fail; if (read_32bitBE(0x00,streamFile) != 0x01006408 || - read_32bitBE(0x04,streamFile) != 0) + read_32bitBE(0x04,streamFile) != 0x00) goto fail; - /* check loop */ - loop_flag = (read_32bitLE(0x10,streamFile)!=0); - channel_count=read_32bitLE(0x1C,streamFile); - + start_offset = read_32bitLE(0x08,streamFile); + data_size = read_32bitLE(0x0c,streamFile); + loop_flag = (read_32bitLE(0x10,streamFile) != 0x00); + channel_count = read_32bitLE(0x1C,streamFile); + encryption = (read_32bitLE(0x20,streamFile) == 1); + /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; - - /* fill in the vital statistics */ - vgmstream->channels = channel_count; vgmstream->sample_rate = read_32bitLE(0x18,streamFile); - /* Check for Compression Scheme */ - if (read_32bitLE(0x20,streamFile) == 1) - vgmstream->coding_type = coding_PSX_bmdx; - else - vgmstream->coding_type = coding_PSX; - vgmstream->num_samples = read_32bitLE(0x0c,streamFile)*28/16/channel_count; - - /* Get loop point values */ - if(vgmstream->loop_flag) { - vgmstream->loop_start_sample = read_32bitLE(0x10,streamFile)*28/16/channel_count; - vgmstream->loop_end_sample = vgmstream->num_samples; - } - - if (channel_count == 1) { - vgmstream->layout_type = layout_none; - } else if (channel_count > 1) { - vgmstream->interleave_block_size = read_32bitLE(0x24,streamFile); - vgmstream->layout_type = layout_interleave; - } + vgmstream->num_samples = ps_bytes_to_samples(data_size, channel_count); + vgmstream->loop_start_sample = ps_bytes_to_samples(read_32bitLE(0x10,streamFile), channel_count); + vgmstream->loop_end_sample = vgmstream->num_samples; vgmstream->meta_type = meta_PS2_BMDX; + vgmstream->coding_type = coding_PSX; + vgmstream->layout_type = (channel_count == 1) ? layout_none : layout_interleave; + vgmstream->interleave_block_size = read_32bitLE(0x24,streamFile); - start_offset = read_32bitLE(0x08,streamFile); - - if (vgmstream->coding_type == coding_PSX_bmdx) - { - uint8_t xor = read_8bit(start_offset,streamFile); - uint8_t add = (~(uint8_t)read_8bit(start_offset+2,streamFile))+1; - int c; - for (c=0;cch[c].bmdx_xor = xor; - vgmstream->ch[c].bmdx_add = add; - } + /* later games are encrypted [beatmaniaIIDX 14 GOLD (PS2)] */ + if (encryption) { + temp_streamFile = setup_bmdx_streamfile(streamFile, start_offset, data_size); + if (!temp_streamFile) goto fail; } - /* open the file for reading by each channel */ - { - for (i=0;ich[0].streamfile) { - vgmstream->ch[0].streamfile = streamFile->open(streamFile,filename,0x8000); - } - vgmstream->ch[i].streamfile = vgmstream->ch[0].streamfile; + if (!vgmstream_open_stream(vgmstream,encryption ? temp_streamFile : streamFile,start_offset)) + goto fail; - if (!vgmstream->ch[i].streamfile) goto fail; - - vgmstream->ch[i].channel_start_offset= - vgmstream->ch[i].offset= - (off_t)(start_offset+vgmstream->interleave_block_size*i); - } - } - + close_streamfile(temp_streamFile); return vgmstream; - /* clean up anything we may have opened */ fail: - if (vgmstream) close_vgmstream(vgmstream); + close_streamfile(temp_streamFile); + close_vgmstream(vgmstream); + return NULL; +} + + +typedef struct { + uint8_t xor; + uint8_t add; + off_t start_offset; + size_t data_size; +} bmdx_decryption_data; + +static size_t bmdx_decryption_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length, bmdx_decryption_data* data) { + size_t bytes_read; + int i; + + bytes_read = streamfile->read(streamfile, dest, offset, length); + + /* decrypt data (xor) */ + for (i = 0; i < bytes_read; i++) { + if (offset+i >= data->start_offset && offset+i < data->start_offset + data->data_size) { + if (((offset+i) % 0x10) == 0) /* XOR header byte per frame */ + dest[i] = dest[i] ^ data->xor; + else if (((offset+i) % 0x10) == 2) /* ADD first data byte per frame */ + dest[i] = (uint8_t)(dest[i] + data->add); + } + } + + return bytes_read; +} + +static STREAMFILE* setup_bmdx_streamfile(STREAMFILE *streamFile, off_t start_offset, size_t data_size) { + STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL; + bmdx_decryption_data io_data = {0}; + size_t io_data_size = sizeof(bmdx_decryption_data); + + /* setup decryption (usually xor=0xFF and add=0x02) */ + io_data.xor = read_8bit(start_offset,streamFile); + io_data.add = (~(uint8_t)read_8bit(start_offset+2,streamFile)) + 0x01; + io_data.start_offset = start_offset; + io_data.data_size = data_size; + + + /* setup custom streamfile */ + new_streamFile = open_wrap_streamfile(streamFile); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + new_streamFile = open_io_streamfile(temp_streamFile, &io_data,io_data_size, bmdx_decryption_read,NULL); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + return temp_streamFile; + +fail: + close_streamfile(temp_streamFile); return NULL; } diff --git a/src/meta/ps2_filp.c b/src/meta/ps2_filp.c index ec5d6757..967cf2cc 100644 --- a/src/meta/ps2_filp.c +++ b/src/meta/ps2_filp.c @@ -37,7 +37,7 @@ VGMSTREAM * init_vgmstream_filp(STREAMFILE *streamFile) { vgmstream->channels = channel_count; vgmstream->sample_rate = read_32bitLE(0x110,streamFile); vgmstream->coding_type = coding_PSX; - vgmstream->layout_type = layout_filp_blocked; + vgmstream->layout_type = layout_blocked_filp; vgmstream->meta_type = meta_FILP; /* open the file for reading */ @@ -50,7 +50,7 @@ VGMSTREAM * init_vgmstream_filp(STREAMFILE *streamFile) { } } - filp_block_update(start_offset,vgmstream); + block_update_filp(start_offset,vgmstream); vgmstream->num_samples = read_32bitLE(0x10C,streamFile)/16*28; if (loop_flag) { vgmstream->loop_start_sample = 0; diff --git a/src/meta/ps2_iab.c b/src/meta/ps2_iab.c index 0d9fdb78..0d010ae5 100644 --- a/src/meta/ps2_iab.c +++ b/src/meta/ps2_iab.c @@ -36,7 +36,7 @@ VGMSTREAM * init_vgmstream_ps2_iab(STREAMFILE *streamFile) { vgmstream->sample_rate = read_32bitLE(0x4,streamFile); vgmstream->coding_type = coding_PSX; - vgmstream->layout_type = layout_ps2_iab_blocked; + vgmstream->layout_type = layout_blocked_ps2_iab; vgmstream->interleave_block_size = read_32bitLE(0xC, streamFile); vgmstream->meta_type = meta_PS2_IAB; @@ -50,16 +50,16 @@ VGMSTREAM * init_vgmstream_ps2_iab(STREAMFILE *streamFile) { } /* Calc num_samples */ - ps2_iab_block_update(start_offset, vgmstream); + block_update_ps2_iab(start_offset, vgmstream); vgmstream->num_samples=0; do { vgmstream->num_samples += 0x4000 * 14 / 16; - ps2_iab_block_update(vgmstream->next_block_offset, vgmstream); + block_update_ps2_iab(vgmstream->next_block_offset, vgmstream); } while (vgmstream->next_block_offset < get_streamfile_size(streamFile)); - ps2_iab_block_update(start_offset, vgmstream); + block_update_ps2_iab(start_offset, vgmstream); return vgmstream; diff --git a/src/meta/ps2_jstm.c b/src/meta/ps2_jstm.c index bb739995..bad93736 100644 --- a/src/meta/ps2_jstm.c +++ b/src/meta/ps2_jstm.c @@ -1,63 +1,104 @@ #include "meta.h" -#include "../util.h" +#include "../coding/coding.h" -/* JSTM (.STM (renamed .JSTM) from Tantei Jinguji Saburo - Kind of Blue) */ +static STREAMFILE* setup_jstm_streamfile(STREAMFILE *streamFile, off_t start_offset); + +/* JSTM - from Tantei Jinguji Saburo - Kind of Blue (PS2) */ VGMSTREAM * init_vgmstream_ps2_jstm(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; - off_t start_offset = 0x20; - int loop_flag; - int channel_count; - char filename[PATH_LIMIT]; + STREAMFILE *temp_streamFile = NULL; + off_t start_offset; + int loop_flag, channel_count; - /* check extension */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("stm",filename_extension(filename)) && - strcasecmp("jstm",filename_extension(filename))) goto fail; - /* check header (JSTM) */ - if (read_32bitBE(0x0,streamFile) != 0x4A53544D) goto fail; + /* checks */ + /* .stm: original, .jstm: header id */ + if (!check_extensions(streamFile, "stm,jstm")) + goto fail; + if (read_32bitBE(0x00,streamFile) != 0x4A53544D) /* "JSTM" */ + goto fail; - loop_flag = (read_32bitLE(0x14,streamFile) != 0); - channel_count = read_16bitLE(0x4,streamFile); - - // hmm, don't know what 6 is, one is probably bytes per sample and the - // other is channels, but who can say? - if (channel_count != read_16bitLE(0x6,streamFile)) goto fail; + start_offset = 0x20; + channel_count = read_16bitLE(0x04,streamFile); + if (channel_count != read_16bitLE(0x06,streamFile)) /* 0x02, interleave? */ + goto fail; + loop_flag = (read_32bitLE(0x14,streamFile) != -1); + /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; - /* fill in the statistics vitale */ - vgmstream->sample_rate = read_32bitLE(0x8,streamFile); - vgmstream->coding_type = coding_PCM16LE_XOR_int; - vgmstream->num_samples = read_32bitLE(0xC,streamFile)/2/channel_count; - vgmstream->layout_type = layout_none; + vgmstream->sample_rate = read_32bitLE(0x08,streamFile); + vgmstream->num_samples = pcm_bytes_to_samples(read_32bitLE(0x0C,streamFile),channel_count,16); + if (loop_flag) { + vgmstream->loop_start_sample = pcm_bytes_to_samples(read_32bitLE(0x14,streamFile),channel_count,16); + vgmstream->loop_end_sample = vgmstream->num_samples; + } vgmstream->meta_type = meta_PS2_JSTM; + vgmstream->coding_type = coding_PCM16LE; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x02; - if (loop_flag) { - vgmstream->loop_start_sample=read_32bitLE(0x14,streamFile)/2/channel_count; - vgmstream->loop_end_sample=vgmstream->num_samples; - } - - /* open the file for reading */ - { - int i; - STREAMFILE * file; - file = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); - if (!file) goto fail; - - for (i=0; i < channel_count; i++) { - vgmstream->ch[i].streamfile = file; - vgmstream->ch[i].channel_start_offset = - vgmstream->ch[i].offset = start_offset + 2*i; - vgmstream->ch[i].key_xor = 0x5A5A; - } - } + temp_streamFile = setup_jstm_streamfile(streamFile, start_offset); + if (!temp_streamFile) goto fail; + if (!vgmstream_open_stream(vgmstream,temp_streamFile,start_offset)) + goto fail; + + close_streamfile(temp_streamFile); + return vgmstream; fail: - if (vgmstream) close_vgmstream(vgmstream); + close_streamfile(temp_streamFile); + close_vgmstream(vgmstream); + return NULL; +} + + +typedef struct { + off_t start_offset; +} jstm_decryption_data; + +static size_t jstm_decryption_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length, jstm_decryption_data* data) { + size_t bytes_read; + int i; + + bytes_read = streamfile->read(streamfile, dest, offset, length); + + /* decrypt data (xor) */ + for (i = 0; i < bytes_read; i++) { + if (offset+i >= data->start_offset) { + //VGM_LOG("xor %x to %x\n", dest[i], dest[i] ^ 0x5A);getchar(); + dest[i] = dest[i] ^ 0x5A; + } + } + + return bytes_read; +} + +static STREAMFILE* setup_jstm_streamfile(STREAMFILE *streamFile, off_t start_offset) { + STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL; + jstm_decryption_data io_data = {0}; + size_t io_data_size = sizeof(jstm_decryption_data); + + /* setup decryption */ + io_data.start_offset = start_offset; + + + /* setup custom streamfile */ + new_streamFile = open_wrap_streamfile(streamFile); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + new_streamFile = open_io_streamfile(temp_streamFile, &io_data,io_data_size, jstm_decryption_read,NULL); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + return temp_streamFile; + +fail: + close_streamfile(temp_streamFile); return NULL; } diff --git a/src/meta/ps2_rxws.c b/src/meta/ps2_rxws.c index d32b69e0..2cffb994 100644 --- a/src/meta/ps2_rxws.c +++ b/src/meta/ps2_rxws.c @@ -24,7 +24,7 @@ VGMSTREAM * init_vgmstream_ps2_rxws(STREAMFILE *streamFile) { (read_32bitBE(0x00,streamFile) == 0x444E4257)) /* "DNBW" (BE) */ goto fail; - streamHeader = open_stream_ext(streamFile, "xwh"); + streamHeader = open_streamfile_by_ext(streamFile, "xwh"); if (!streamHeader) goto fail; } else { streamHeader = streamFile; diff --git a/src/meta/ps2_strlr.c b/src/meta/ps2_strlr.c index 0ef63ea5..2003f379 100644 --- a/src/meta/ps2_strlr.c +++ b/src/meta/ps2_strlr.c @@ -46,7 +46,7 @@ VGMSTREAM * init_vgmstream_ps2_strlr(STREAMFILE *streamFile) { vgmstream->sample_rate = 48000; vgmstream->coding_type = coding_PSX; - vgmstream->layout_type = layout_ps2_strlr_blocked; + vgmstream->layout_type = layout_blocked_ps2_strlr; //vgmstream->interleave_block_size = read_32bitLE(0xC, streamFile); vgmstream->meta_type = meta_PS2_STRLR; @@ -60,16 +60,16 @@ VGMSTREAM * init_vgmstream_ps2_strlr(STREAMFILE *streamFile) { } /* Calc num_samples */ - ps2_strlr_block_update(start_offset, vgmstream); + block_update_ps2_strlr(start_offset, vgmstream); vgmstream->num_samples=0; do { vgmstream->num_samples += vgmstream->current_block_size * 14 / 16; - ps2_strlr_block_update(vgmstream->next_block_offset, vgmstream); + block_update_ps2_strlr(vgmstream->next_block_offset, vgmstream); } while (vgmstream->next_block_offset < get_streamfile_size(streamFile)); - ps2_strlr_block_update(start_offset, vgmstream); + block_update_ps2_strlr(start_offset, vgmstream); return vgmstream; diff --git a/src/meta/ps2_vds_vdm.c b/src/meta/ps2_vds_vdm.c index dd29f9c8..1b4006a4 100644 --- a/src/meta/ps2_vds_vdm.c +++ b/src/meta/ps2_vds_vdm.c @@ -1,43 +1,46 @@ #include "meta.h" +#include "../coding/coding.h" -/* VDS/VDM - from Grafitti Kingdom / Rakugaki Oukoku 2 */ +/* VDS/VDM - from Procyon Studio games [Grafitti Kingdom / Rakugaki Oukoku 2 (PS2), Tsukiyo ni Saraba (PS2)] */ VGMSTREAM * init_vgmstream_ps2_vds_vdm(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; off_t start_offset; int loop_flag, channel_count; - /* check extension, case insensitive */ + + /* checks */ if ( !check_extensions(streamFile,"vds,vdm")) goto fail; - if (read_32bitBE(0x00,streamFile) != 0x56445320 && /* "VDS " (music)*/ read_32bitBE(0x00,streamFile) != 0x56444D20) /* "VDM " (voices) */ goto fail; loop_flag = read_8bit(0x20,streamFile); channel_count = read_32bitLE(0x10,streamFile); + start_offset = 0x800; - /* build the VGMSTREAM */ + /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; - vgmstream->coding_type = coding_PSX; - vgmstream->layout_type = channel_count > 1 ? layout_interleave : layout_none; - vgmstream->meta_type = meta_PS2_VDS_VDM; - start_offset = 0x800; - vgmstream->num_samples = read_32bitLE(0x04,streamFile) * 28 / 16 / channel_count; - /* 0x08: unknown, always 10 */ + /* 0x08: unknown, always 0x10 */ vgmstream->sample_rate = read_32bitLE(0x0c,streamFile); - vgmstream->channels = channel_count; /*0x10*/ - vgmstream->interleave_block_size = read_32bitLE(0x14,streamFile); - vgmstream->loop_start_sample = (read_32bitLE(0x18,streamFile) - start_offset) * 28 / 16 / channel_count; - vgmstream->loop_end_sample = (read_32bitLE(0x1c,streamFile) - start_offset) * 28 / 16 / channel_count; - vgmstream->loop_flag = loop_flag; /*0x20*/ - /*0x21: volume? */ - /*0x22: pan? */ - /*0x23: 02=VDS 04=VDM? */ - /* open the file for reading */ + /* when looping (or maybe when stereo) data_size at 0x04 is actually smaller than file_size, + * sometimes cutting outros with loop disabled; doesn't affect looping though */ + if (!loop_flag) + vgmstream->num_samples = ps_bytes_to_samples(read_32bitLE(0x04,streamFile), channel_count); + else + vgmstream->num_samples = ps_bytes_to_samples(get_streamfile_size(streamFile) - start_offset, channel_count); + vgmstream->loop_start_sample = ps_bytes_to_samples(read_32bitLE(0x18,streamFile) - start_offset, channel_count); + vgmstream->loop_end_sample = ps_bytes_to_samples(read_32bitLE(0x1c,streamFile) - start_offset, channel_count); + /* 0x21: volume?, 0x22: pan?, 0x23: 02=VDS 04=VDM? 02/05=VDM in Tsukiyo ni Saraba? */ + + vgmstream->meta_type = meta_PS2_VDS_VDM; + vgmstream->coding_type = coding_PSX; + vgmstream->layout_type = (channel_count == 1) ? layout_none : layout_interleave; + vgmstream->interleave_block_size = read_32bitLE(0x14,streamFile); + if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) ) goto fail; return vgmstream; diff --git a/src/meta/ps3_msf.c b/src/meta/ps3_msf.c index fc4e3cdb..d76850ef 100644 --- a/src/meta/ps3_msf.c +++ b/src/meta/ps3_msf.c @@ -1,6 +1,5 @@ #include "meta.h" #include "../coding/coding.h" -#include "../util.h" /* MSF - Sony's PS3 SDK format (MultiStream File) */ VGMSTREAM * init_vgmstream_ps3_msf(STREAMFILE *streamFile) { @@ -11,10 +10,12 @@ VGMSTREAM * init_vgmstream_ps3_msf(STREAMFILE *streamFile) { int loop_flag = 0, channel_count; - /* check extension, case insensitive (.at3: Silent Hill HD Collection, .mp3: Darkstalkers Resurrection) */ - if (!check_extensions(streamFile,"msf,at3,mp3")) goto fail; + /* checks */ + /* .msf: standard, .at3: Silent Hill HD Collection, .mp3: Darkstalkers Resurrection */ + if (!check_extensions(streamFile,"msf,at3,mp3")) + goto fail; - /* "WMSF" variation with a mini header over the MSFC header, same extension */ + /* "WMSF" variation with a mini header over the MSFC header [Dai-2-Ji Super Robot Generations (PS3)] */ if (read_32bitBE(0x00,streamFile) == 0x574D5346) { header_offset = 0x10; } @@ -35,7 +36,7 @@ VGMSTREAM * init_vgmstream_ps3_msf(STREAMFILE *streamFile) { /* byte flags, not in MSFv1 or v2 * 0x01/02/04/08: loop marker 0/1/2/3 * 0x10: resample options (force 44/48khz) - * 0x20: VBR MP3 + * 0x20: VBR MP3 source (changed into simplified 0x1a1 CBR) * 0x40: joint stereo MP3 (apparently interleaved stereo for other formats) * 0x80+: (none/reserved) */ flags = read_32bitBE(header_offset+0x14,streamFile); @@ -57,7 +58,7 @@ VGMSTREAM * init_vgmstream_ps3_msf(STREAMFILE *streamFile) { vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; - /* Sample rate hack for strange MSFv1 files that don't have a specified frequency */ + /* sample rate hack for strange MSFv1 files that don't have it */ vgmstream->sample_rate = read_32bitBE(header_offset+0x10,streamFile); if (vgmstream->sample_rate == 0x00000000) /* PS ADPCM only? */ vgmstream->sample_rate = 48000; @@ -65,43 +66,43 @@ VGMSTREAM * init_vgmstream_ps3_msf(STREAMFILE *streamFile) { vgmstream->meta_type = meta_PS3_MSF; switch (codec_id) { - case 0x0: /* PCM (Big Endian) */ - case 0x1: { /* PCM (Little Endian) */ + case 0x00: /* PCM (Big Endian) */ + case 0x01: { /* PCM (Little Endian) [Smash Cars (PS3)] */ vgmstream->coding_type = codec_id==0 ? coding_PCM16BE : coding_PCM16LE; vgmstream->layout_type = channel_count == 1 ? layout_none : layout_interleave; vgmstream->interleave_block_size = 2; - vgmstream->num_samples = data_size/2/channel_count; + vgmstream->num_samples = pcm_bytes_to_samples(data_size, channel_count,16); if (loop_flag){ - vgmstream->loop_start_sample = loop_start/2/channel_count; - vgmstream->loop_end_sample = loop_end/2/channel_count; + vgmstream->loop_start_sample = pcm_bytes_to_samples(loop_start, channel_count,16); + vgmstream->loop_end_sample = pcm_bytes_to_samples(loop_end, channel_count,16); } break; } - case 0x2: { /* PCM 32 (Float) */ - goto fail; //probably unused/spec only + case 0x02: { /* PCM 32 (Float) */ + goto fail; /* probably unused/spec only */ } - case 0x3: { /* PS ADPCM */ + case 0x03: { /* PS ADPCM [Smash Cars (PS3)] */ vgmstream->coding_type = coding_PSX; vgmstream->layout_type = channel_count == 1 ? layout_none : layout_interleave; vgmstream->interleave_block_size = 0x10; - vgmstream->num_samples = data_size*28/16/channel_count; + vgmstream->num_samples = ps_bytes_to_samples(data_size, channel_count); if (loop_flag) { - vgmstream->loop_start_sample = loop_start*28/16/channel_count; - vgmstream->loop_end_sample = loop_end*28/16/channel_count; + vgmstream->loop_start_sample = ps_bytes_to_samples(loop_start,channel_count); + vgmstream->loop_end_sample = ps_bytes_to_samples(loop_end,channel_count); } break; } #ifdef VGM_USE_FFMPEG - case 0x4: /* ATRAC3 low (66 kbps, frame size 96, Joint Stereo) */ - case 0x5: /* ATRAC3 mid (105 kbps, frame size 152) */ - case 0x6: { /* ATRAC3 high (132 kbps, frame size 192) */ + case 0x04: /* ATRAC3 low (66 kbps, frame size 96, Joint Stereo) [Silent Hill HD (PS3)] */ + case 0x05: /* ATRAC3 mid (105 kbps, frame size 152) [Atelier Rorona (PS3)] */ + case 0x06: { /* ATRAC3 high (132 kbps, frame size 192) [Tekken Tag Tournament HD (PS3)] */ ffmpeg_codec_data *ffmpeg_data = NULL; uint8_t buf[100]; int32_t bytes, block_size, encoder_delay, joint_stereo; @@ -141,67 +142,55 @@ VGMSTREAM * init_vgmstream_ps3_msf(STREAMFILE *streamFile) { break; } #endif -#ifdef VGM_USE_FFMPEG - case 0x7: { /* MPEG (LAME MP3 of any quality) */ - /* delegate to FFMpeg, it can parse MSF files */ - ffmpeg_codec_data *ffmpeg_data = init_ffmpeg_offset(streamFile, header_offset, streamFile->get_size(streamFile) ); +#if defined(VGM_USE_FFMPEG) && !defined(VGM_USE_MPEG) + case 0x07: { /* MPEG (CBR LAME MP3) [Dengeki Bunko Fighting Climax (PS3)] */ + ffmpeg_codec_data *ffmpeg_data = NULL; + + ffmpeg_data = init_ffmpeg_offset(streamFile, start_offset, streamFile->get_size(streamFile) ); if ( !ffmpeg_data ) goto fail; vgmstream->codec_data = ffmpeg_data; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; - /* vgmstream->num_samples = ffmpeg_data->totalSamples; */ /* duration may not be set/inaccurate */ vgmstream->num_samples = (int64_t)data_size * ffmpeg_data->sampleRate * 8 / ffmpeg_data->bitrate; if (loop_flag) { - //todo properly apply encoder delay, which seems to vary between 1152 (1f), 528, 576 or 528+576 - int frame_size = ffmpeg_data->frameSize; vgmstream->loop_start_sample = (int64_t)loop_start * ffmpeg_data->sampleRate * 8 / ffmpeg_data->bitrate; - vgmstream->loop_start_sample -= vgmstream->loop_start_sample==frame_size ? frame_size - : vgmstream->loop_start_sample % frame_size; vgmstream->loop_end_sample = (int64_t)loop_end * ffmpeg_data->sampleRate * 8 / ffmpeg_data->bitrate; - vgmstream->loop_end_sample -= vgmstream->loop_end_sample==frame_size ? frame_size - : vgmstream->loop_end_sample % frame_size; + /* loops are always CBR frame beginnings */ } + /* encoder delay varies between 1152 (1f), 528, 576, etc; probably not actually skipped */ break; } #endif -#if defined(VGM_USE_MPEG) && !defined(VGM_USE_FFMPEG) - case 0x7: { /* MPEG (LAME MP3 of any quality) */ - int frame_size = 576; /* todo incorrect looping calcs */ - +#ifdef VGM_USE_MPEG + case 0x07: { /* MPEG (CBR LAME MP3) []Dengeki Bunko Fighting Climax (PS3) */ mpeg_codec_data *mpeg_data = NULL; - coding_t ct; - mpeg_data = init_mpeg(streamFile, start_offset, &ct, vgmstream->channels); + mpeg_data = init_mpeg(streamFile, start_offset, &vgmstream->coding_type, vgmstream->channels); if (!mpeg_data) goto fail; vgmstream->codec_data = mpeg_data; - - vgmstream->coding_type = ct; vgmstream->layout_type = layout_none; + vgmstream->num_samples = mpeg_bytes_to_samples(data_size, mpeg_data); - vgmstream->num_samples -= vgmstream->num_samples % frame_size; if (loop_flag) { vgmstream->loop_start_sample = mpeg_bytes_to_samples(loop_start, mpeg_data); - vgmstream->loop_start_sample -= vgmstream->loop_start_sample % frame_size; vgmstream->loop_end_sample = mpeg_bytes_to_samples(loop_end, mpeg_data); - vgmstream->loop_end_sample -= vgmstream->loop_end_sample % frame_size; + /* loops are always CBR frame beginnings */ } - vgmstream->interleave_block_size = 0; + /* encoder delay varies between 1152 (1f), 528, 576, etc; probably not actually skipped */ break; } #endif - default: /* 8+: not defined */ + default: /* 0x08+: not defined */ goto fail; } - /* open the file for reading */ if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) goto fail; - return vgmstream; fail: diff --git a/src/meta/psx_cdxa.c b/src/meta/psx_cdxa.c index 1dd04d61..efdb52f6 100644 --- a/src/meta/psx_cdxa.c +++ b/src/meta/psx_cdxa.c @@ -105,7 +105,7 @@ VGMSTREAM * init_vgmstream_cdxa(STREAMFILE *streamFile) { vgmstream->xa_channel = xa_channel; vgmstream->coding_type = coding_XA; - vgmstream->layout_type = layout_xa_blocked; + vgmstream->layout_type = layout_blocked_xa; vgmstream->meta_type = meta_PSX_XA; if (is_blocked) @@ -115,7 +115,7 @@ VGMSTREAM * init_vgmstream_cdxa(STREAMFILE *streamFile) { if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) ) goto fail; - xa_block_update(start_offset,vgmstream); + block_update_xa(start_offset,vgmstream); return vgmstream; diff --git a/src/meta/riff.c b/src/meta/riff.c index 38264df2..9a9c9a6b 100644 --- a/src/meta/riff.c +++ b/src/meta/riff.c @@ -106,15 +106,22 @@ static int read_fmt(int big_endian, STREAMFILE * streamFile, off_t current_chunk fmt->codec = (uint16_t)read_16bit(current_chunk+0x8,streamFile); switch (fmt->codec) { + case 0x00: /* Yamaha ADPCM (raw) [Headhunter (DC), Bomber hehhe (DC)] (unofficial) */ + if (fmt->bps != 4) goto fail; + if (fmt->block_size != 0x02*fmt->channel_count) goto fail; + fmt->coding_type = coding_AICA_int; + fmt->interleave = 0x01; + break; + case 0x01: /* PCM */ switch (fmt->bps) { case 16: fmt->coding_type = big_endian ? coding_PCM16BE : coding_PCM16LE; - fmt->interleave = 2; + fmt->interleave = 0x02; break; case 8: fmt->coding_type = coding_PCM8_U_int; - fmt->interleave = 1; + fmt->interleave = 0x01; break; default: goto fail; @@ -131,12 +138,17 @@ static int read_fmt(int big_endian, STREAMFILE * streamFile, off_t current_chunk fmt->coding_type = coding_MS_IMA; break; + case 0x20: /* Yamaha ADPCM (raw) [Takuyo/Dynamix/etc DC games] */ + if (fmt->bps != 4) goto fail; + fmt->coding_type = coding_AICA; + break; + case 0x69: /* XBOX IMA ADPCM [Dynasty Warriors 5 (Xbox), Rayman Raving Rabbids 2 (PC) --maybe waa/wac/wam/wad?] */ if (fmt->bps != 4) goto fail; fmt->coding_type = coding_XBOX_IMA; break; - case 0x007A: /* MS IMA ADPCM [LA Rush (PC), Psi Ops (PC)] */ + case 0x007A: /* MS IMA ADPCM [LA Rush (PC), Psi Ops (PC)] (unofficial) */ /* 0x007A is apparently "Voxware SC3" but in .MED it's just MS-IMA (0x11) */ if (!check_extensions(streamFile,"med")) goto fail; @@ -149,13 +161,13 @@ static int read_fmt(int big_endian, STREAMFILE * streamFile, off_t current_chunk goto fail; break; - case 0x0555: /* Level-5 0x555 ADPCM */ + case 0x0555: /* Level-5 0x555 ADPCM (unofficial) */ if (!mwv) goto fail; fmt->coding_type = coding_L5_555; fmt->interleave = 0x12; break; - case 0x5050: /* Ubisoft LyN engine's DSP */ + case 0x5050: /* Ubisoft LyN engine's DSP (unofficial) */ if (!sns) goto fail; fmt->coding_type = coding_NGC_DSP; fmt->interleave = 0x08; @@ -249,8 +261,9 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { /* check extension */ /* .lwav: to avoid hijacking .wav, .xwav: fake for Xbox games (unneded anymore) */ /* .da: The Great Battle VI (PS), .cd: Exector (PS), .med: Psi Ops (PC), .snd: Layton Brothers (iOS/Android), - * .adx: Remember11 (PC) sfx */ - if ( check_extensions(streamFile, "wav,lwav,xwav,da,cd,med,snd,adx") ) { + * .adx: Remember11 (PC) sfx + * .adp: Headhunter (DC) */ + if ( check_extensions(streamFile, "wav,lwav,xwav,da,cd,med,snd,adx,adp") ) { ; } else if ( check_extensions(streamFile, "mwv") ) { @@ -286,6 +299,10 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { /* some Xbox games do this [Dynasty Warriors 3 (Xbox), BloodRayne (Xbox)] */ if (riff_size == file_size && read_16bitLE(0x14,streamFile)==0x0069) riff_size -= 0x08; + /* some Dreamcast/Naomi games do this [Headhunter (DC), Bomber hehhe (DC)] */ + if (riff_size + 0x04 == file_size && read_16bitLE(0x14,streamFile)==0x0000) + riff_size -= 0x04; + /* check for truncated RIFF */ if (file_size < riff_size+0x08) goto fail; @@ -315,6 +332,10 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { sns, mwv)) goto fail; + + /* some Dreamcast/Naomi games again [Headhunter (DC), Bomber hehhe (DC)] */ + if (fmt.codec == 0x0000 && chunk_size == 0x12) + chunk_size += 0x02; break; case 0x64617461: /* "data" */ @@ -483,6 +504,11 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { vgmstream->num_samples = fact_sample_count; break; + case coding_AICA: + case coding_AICA_int: + vgmstream->num_samples = aica_bytes_to_samples(data_size, fmt.channel_count); + break; + case coding_XBOX_IMA: vgmstream->num_samples = xbox_ima_bytes_to_samples(data_size, fmt.channel_count); if (fact_sample_count && fact_sample_count < vgmstream->num_samples) @@ -577,6 +603,7 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { switch (fmt.coding_type) { case coding_MSADPCM: case coding_MS_IMA: + case coding_AICA: case coding_XBOX_IMA: #ifdef VGM_USE_FFMPEG case coding_FFmpeg: @@ -849,7 +876,7 @@ static VGMSTREAM *parse_riff_ogg(STREAMFILE * streamFile, off_t start_offset, si io_data.patch_offset = patch_offset; - custom_streamFile = open_io_streamfile(open_wrap_streamfile(streamFile), &io_data,io_data_size, riff_ogg_io_read); + custom_streamFile = open_io_streamfile(open_wrap_streamfile(streamFile), &io_data,io_data_size, riff_ogg_io_read,NULL); if (!custom_streamFile) return NULL; vgmstream = init_vgmstream_ogg_vorbis_callbacks(custom_streamFile, NULL, start_offset, &ovmi); diff --git a/src/meta/rsd.c b/src/meta/rsd.c index bfd01a5c..c4af75e6 100644 --- a/src/meta/rsd.c +++ b/src/meta/rsd.c @@ -1179,7 +1179,7 @@ VGMSTREAM * init_vgmstream_rsd6wma(STREAMFILE *streamFile) { if (!vgmstream) goto fail; vgmstream->meta_type = meta_RSD6WMA; - //vgmstream->num_samples = read_32bitLE(start_offset + 0x00, streamFile); /* ? */ + //vgmstream->num_samples = read_32bitLE(start_offset + 0x00, streamFile) / channel_count / 2; /* may be PCM data size, but not exact */ vgmstream->sample_rate = read_32bitLE(start_offset + 0x04, streamFile); #ifdef VGM_USE_FFMPEG @@ -1193,12 +1193,11 @@ VGMSTREAM * init_vgmstream_rsd6wma(STREAMFILE *streamFile) { vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; - vgmstream->num_samples = (int32_t)ffmpeg_data->totalSamples; /* probably an estimation */ - + vgmstream->num_samples = (int32_t)ffmpeg_data->totalSamples; /* an estimation, sometimes cuts files a bit early */ + } #else goto fail; #endif - } if (!vgmstream_open_stream(vgmstream, streamFile, start_offset)) goto fail; diff --git a/src/meta/sab.c b/src/meta/sab.c index 6ce952b1..3fce2b60 100644 --- a/src/meta/sab.c +++ b/src/meta/sab.c @@ -109,7 +109,7 @@ static void get_stream_name(char * stream_name, STREAMFILE *streamFile, int targ size_t name_size = 0; off_t name_offset = 0x10; - streamInfo = open_stream_ext(streamFile, "sob"); + streamInfo = open_streamfile_by_ext(streamFile, "sob"); if (!streamInfo) goto end; if (read_32bitBE(0x00,streamInfo) != 0x43544632) /* "CTF2" */ goto end; diff --git a/src/meta/sgxd.c b/src/meta/sgxd.c index 33c04628..e3744463 100644 --- a/src/meta/sgxd.c +++ b/src/meta/sgxd.c @@ -24,7 +24,7 @@ VGMSTREAM * init_vgmstream_sgxd(STREAMFILE *streamFile) { /* SGB+SGH: use SGH as header; otherwise use the current file as header */ if (is_sgb) { - streamHeader = open_stream_ext(streamFile, "sgh"); + streamHeader = open_streamfile_by_ext(streamFile, "sgh"); if (!streamHeader) goto fail; } else { streamHeader = streamFile; diff --git a/src/meta/sqex_scd.c b/src/meta/sqex_scd.c index f39c0dc6..dd14dbf4 100644 --- a/src/meta/sqex_scd.c +++ b/src/meta/sqex_scd.c @@ -1,5 +1,6 @@ #include "meta.h" #include "../coding/coding.h" +#include "../layout/layout.h" #include "sqex_scd_streamfile.h" @@ -11,7 +12,6 @@ static void scd_ogg_v3_decryption_callback(void *ptr, size_t size, size_t nmemb, /* SCD - Square-Enix games (FF XIII, XIV) */ VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; off_t start_offset, tables_offset, meta_offset, extradata_offset, name_offset = 0; int32_t stream_size, extradata_size, loop_start, loop_end; @@ -26,7 +26,6 @@ VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE *streamFile) { /* check extension, case insensitive */ if ( !check_extensions(streamFile, "scd") ) goto fail; - streamFile->get_name(streamFile,filename,sizeof(filename)); /** main header **/ if (read_32bitBE(0x00,streamFile) != 0x53454442 && /* "SEDB" */ @@ -281,61 +280,54 @@ VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE *streamFile) { case 0x0A: /* DSP ADPCM [Dragon Quest X (Wii)] */ case 0x15: { /* DSP ADPCM [Dragon Quest X (Wii U)] (no apparent differences except higher sample rate) */ - STREAMFILE * file; - int i; const off_t interleave_size = 0x800; const off_t stride_size = interleave_size * channel_count; + int i; size_t total_size; - scd_int_codec_data * data = NULL; - + layered_layout_data * data = NULL; + /* interleaved DSPs including the header (so the first 0x800 is 0x60 header + 0x740 data) + * so interleave layout can't used; we'll setup de-interleaving streamfiles as layers/channels instead */ + //todo this could be simplified using a block layout or adding interleave_first_block vgmstream->coding_type = coding_NGC_DSP; - vgmstream->layout_type = layout_scd_int; + vgmstream->layout_type = layout_layered; - /* a normal DSP header... */ - total_size = (read_32bitBE(start_offset+0x04,streamFile)+1)/2; - vgmstream->num_samples = read_32bitBE(start_offset+0x00,streamFile); - if (loop_flag) { - vgmstream->loop_start_sample = loop_start; - vgmstream->loop_end_sample = loop_end+1; - } + /* read from the first DSP header and verify other channel headers */ + { + total_size = (read_32bitBE(start_offset+0x04,streamFile)+1)/2; /* rounded nibbles / 2 */ + vgmstream->num_samples = read_32bitBE(start_offset+0x00,streamFile); + if (loop_flag) { + vgmstream->loop_start_sample = loop_start; + vgmstream->loop_end_sample = loop_end+1; + } - /* verify other channel headers */ - for (i = 1; i < channel_count; i++) { - if (read_32bitBE(start_offset+interleave_size*i+0,streamFile) != vgmstream->num_samples || - (read_32bitBE(start_offset+4,streamFile)+1)/2 != total_size) { - goto fail; + for (i = 1; i < channel_count; i++) { + if ((read_32bitBE(start_offset+4,streamFile)+1)/2 != total_size || + read_32bitBE(start_offset+interleave_size*i+0x00,streamFile) != vgmstream->num_samples) { + goto fail; + } } } - /* the primary streamfile we'll be using */ - file = streamFile->open(streamFile,filename,stride_size); - if (!file) goto fail; + /* init layout */ + data = init_layout_layered(channel_count); + if (!data) goto fail; + vgmstream->layout_data = data; - vgmstream->ch[0].streamfile = file; + /* open each layer subfile */ + for (i = 0; i < channel_count; i++) { + STREAMFILE* temp_streamFile = setup_scd_dsp_streamfile(streamFile, start_offset+interleave_size*i, interleave_size, stride_size, total_size); + if (!temp_streamFile) goto fail; - data = malloc(sizeof(scd_int_codec_data)); - data->substream_count = channel_count; - data->substreams = calloc(channel_count, sizeof(VGMSTREAM *)); - data->intfiles = calloc(channel_count, sizeof(STREAMFILE *)); - - vgmstream->codec_data = data; - - for (i=0;isubstreams[i] = init_vgmstream_ngc_dsp_std(intfile); - data->intfiles[i] = intfile; - if (!data->substreams[i]) goto fail; - - /* TODO: only handles mono substreams, though that's all we have with DSP */ - /* save start things so we can restart for seeking/looping */ - memcpy(data->substreams[i]->start_ch,data->substreams[i]->ch,sizeof(VGMSTREAMCHANNEL)*1); - memcpy(data->substreams[i]->start_vgmstream,data->substreams[i],sizeof(VGMSTREAM)); + data->layers[i] = init_vgmstream_ngc_dsp_std(temp_streamFile); + close_streamfile(temp_streamFile); + if (!data->layers[i]) goto fail; } + /* setup layered VGMSTREAMs */ + if (!setup_layout_layered(data)) + goto fail; + break; } diff --git a/src/meta/sqex_scd_streamfile.h b/src/meta/sqex_scd_streamfile.h index 2d9f67c7..2d54bc83 100644 --- a/src/meta/sqex_scd_streamfile.h +++ b/src/meta/sqex_scd_streamfile.h @@ -2,72 +2,30 @@ #define _SQEX_SCD_STREAMFILE_H_ #include "../streamfile.h" -/* special streamfile type to handle deinterleaving of complete files (based heavily on AIXSTREAMFILE */ -typedef struct _SCDINTSTREAMFILE { - STREAMFILE sf; - STREAMFILE *real_file; - const char * filename; - off_t start_physical_offset; - off_t current_logical_offset; - off_t interleave_block_size; - off_t stride_size; - size_t total_size; -} SCDINTSTREAMFILE; +typedef struct { + off_t start_physical_offset; /* interleaved data start, for this substream */ + size_t interleave_block_size; /* max size that can be read before encountering other substreams */ + size_t stride_size; /* step size between interleave blocks (interleave*channels) */ + size_t total_size; /* final size of the deinterleaved substream */ +} scd_dsp_io_data; -/*static*/ STREAMFILE *open_scdint_with_STREAMFILE(STREAMFILE *file, const char * filename, off_t start_offset, off_t interleave_block_size, off_t stride_size, size_t total_size); - - -static STREAMFILE *open_scdint_impl(SCDINTSTREAMFILE *streamfile,const char * const filename,size_t buffersize) { - SCDINTSTREAMFILE *newfile; - - if (strcmp(filename, streamfile->filename)) - return NULL; - - newfile = malloc(sizeof(SCDINTSTREAMFILE)); - if (!newfile) - return NULL; - - memcpy(newfile,streamfile,sizeof(SCDINTSTREAMFILE)); - return &newfile->sf; -} - -static void close_scdint(SCDINTSTREAMFILE *streamfile) { - free(streamfile); - return; -} - -static size_t get_size_scdint(SCDINTSTREAMFILE *streamfile) { - return streamfile->total_size; -} - -static size_t get_offset_scdint(SCDINTSTREAMFILE *streamfile) { - return streamfile->current_logical_offset; -} - -static void get_name_scdint(SCDINTSTREAMFILE *streamfile, char *buffer, size_t length) { - strncpy(buffer,streamfile->filename,length); - buffer[length-1]='\0'; -} - -static size_t read_scdint(SCDINTSTREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length) { - size_t sz = 0; +/* Handles deinterleaving of complete files, skipping portions or other substreams. */ +static size_t scd_dsp_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length, scd_dsp_io_data* data) { + size_t total_read = 0; while (length > 0) { - off_t to_read; - off_t length_available; + size_t to_read; + size_t length_available; off_t block_num; off_t intrablock_offset; off_t physical_offset; - - block_num = offset / streamfile->interleave_block_size; - intrablock_offset = offset % streamfile->interleave_block_size; - streamfile->current_logical_offset = offset; - physical_offset = streamfile->start_physical_offset + block_num * streamfile->stride_size + intrablock_offset; - - length_available = streamfile->interleave_block_size - intrablock_offset; + block_num = offset / data->interleave_block_size; + intrablock_offset = offset % data->interleave_block_size; + physical_offset = data->start_physical_offset + block_num*data->stride_size + intrablock_offset; + length_available = data->interleave_block_size - intrablock_offset; if (length < length_available) { to_read = length; @@ -79,16 +37,11 @@ static size_t read_scdint(SCDINTSTREAMFILE *streamfile, uint8_t *dest, off_t off if (to_read > 0) { size_t bytes_read; - bytes_read = read_streamfile(dest, - physical_offset, - to_read, streamfile->real_file); - - sz += bytes_read; - - streamfile->current_logical_offset = offset + bytes_read; + bytes_read = read_streamfile(dest, physical_offset, to_read, streamfile); + total_read += bytes_read; if (bytes_read != to_read) { - return sz; /* an error which we will not attempt to handle here */ + return total_read; } dest += bytes_read; @@ -97,38 +50,43 @@ static size_t read_scdint(SCDINTSTREAMFILE *streamfile, uint8_t *dest, off_t off } } - return sz; + return total_read; } -/* start_offset is for *this* interleaved stream */ -/*static*/ STREAMFILE *open_scdint_with_STREAMFILE(STREAMFILE *file, const char * filename, off_t start_offset, off_t interleave_block_size, off_t stride_size, size_t total_size) { - SCDINTSTREAMFILE * scd = NULL; +static size_t scd_dsp_io_size(STREAMFILE *streamfile, scd_dsp_io_data* data) { + return data->total_size; +} - /* _scdint funcs can't handle this case */ - if (start_offset + total_size > file->get_size(file)) - return NULL; - scd = malloc(sizeof(SCDINTSTREAMFILE)); - if (!scd) - return NULL; +static STREAMFILE* setup_scd_dsp_streamfile(STREAMFILE *streamFile, off_t start_offset, size_t interleave_block_size, size_t stride_size, size_t total_size) { + STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL; + scd_dsp_io_data io_data = {0}; + size_t io_data_size = sizeof(scd_dsp_io_data); - scd->sf.read = (void*)read_scdint; - scd->sf.get_size = (void*)get_size_scdint; - scd->sf.get_offset = (void*)get_offset_scdint; - scd->sf.get_name = (void*)get_name_scdint; - scd->sf.get_realname = (void*)get_name_scdint; - scd->sf.open = (void*)open_scdint_impl; - scd->sf.close = (void*)close_scdint; + io_data.start_physical_offset = start_offset; + io_data.interleave_block_size = interleave_block_size; + io_data.stride_size = stride_size; + io_data.total_size = total_size; - scd->real_file = file; - scd->filename = filename; - scd->start_physical_offset = start_offset; - scd->current_logical_offset = 0; - scd->interleave_block_size = interleave_block_size; - scd->stride_size = stride_size; - scd->total_size = total_size; - return &scd->sf; + /* setup subfile */ + new_streamFile = open_wrap_streamfile(streamFile); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + new_streamFile = open_io_streamfile(temp_streamFile, &io_data,io_data_size, scd_dsp_io_read,scd_dsp_io_size); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + new_streamFile = open_fakename_streamfile(temp_streamFile, NULL,"dsp"); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + return temp_streamFile; + +fail: + close_streamfile(temp_streamFile); + return NULL; } #endif /* _SCD_STREAMFILE_H_ */ diff --git a/src/meta/sqex_sead.c b/src/meta/sqex_sead.c index 5da8e8e6..be1cafc5 100644 --- a/src/meta/sqex_sead.c +++ b/src/meta/sqex_sead.c @@ -365,7 +365,7 @@ static STREAMFILE* setup_sead_hca_streamfile(STREAMFILE *streamFile, off_t subfi io_data.header_size = header_size; io_data.key_start = key_start; - new_streamFile = open_io_streamfile(temp_streamFile, &io_data,io_data_size, sead_decryption_read); + new_streamFile = open_io_streamfile(temp_streamFile, &io_data,io_data_size, sead_decryption_read,NULL); if (!new_streamFile) goto fail; temp_streamFile = new_streamFile; } diff --git a/src/meta/sthd.c b/src/meta/sthd.c new file mode 100644 index 00000000..bec490b8 --- /dev/null +++ b/src/meta/sthd.c @@ -0,0 +1,69 @@ +#include "meta.h" +#include "../coding/coding.h" +#include "../layout/layout.h" + +/* STHD - Dream Factory .stx [Kakuto Chojin (Xbox)] */ +VGMSTREAM * init_vgmstream_sthd(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + off_t start_offset; + int loop_flag, channel_count; + + + /* checks */ + if (!check_extensions(streamFile, "stx")) + goto fail; + if (read_32bitBE(0x00,streamFile) != 0x53544844) /* "STHD" */ + goto fail; + /* first block has special values */ + if (read_32bitLE(0x04,streamFile) != 0x0800 && + read_32bitLE(0x0c,streamFile) != 0x0001 && + read_32bitLE(0x14,streamFile) != 0x0000) + goto fail; + + channel_count = read_16bitLE(0x06,streamFile); + loop_flag = read_16bitLE(0x18,streamFile) != -1; + start_offset = 0x800; + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count, loop_flag); + if (!vgmstream) goto fail; + + vgmstream->sample_rate = read_32bitLE(0x20, streamFile); /* repeated ~8 times? */ + vgmstream->meta_type = meta_STHD; + vgmstream->coding_type = coding_XBOX_IMA_int; + vgmstream->layout_type = layout_blocked_sthd; + + if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + goto fail; + + /* calc num_samples manually (blocks data varies in size) */ + { + /* loop values may change to +1 in first actual block, but this works ok enough */ + int loop_start_block = (uint16_t)read_16bitLE(0x1a,streamFile); + int loop_end_block = (uint16_t)read_16bitLE(0x1c,streamFile); + int block_count = 1; /* header block = 0 */ + + vgmstream->num_samples = 0; + block_update_sthd(start_offset,vgmstream); + do { + if (block_count == loop_start_block) + vgmstream->loop_start_sample = vgmstream->num_samples; + if (block_count == loop_end_block) + vgmstream->loop_end_sample = vgmstream->num_samples; + + vgmstream->num_samples += xbox_ima_bytes_to_samples(vgmstream->current_block_size, 1); + block_update_sthd(vgmstream->next_block_offset,vgmstream); + + block_count++; + } + while (vgmstream->next_block_offset < get_streamfile_size(streamFile)); + } + + block_update_sthd(start_offset, vgmstream); + + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} diff --git a/src/meta/str_snds.c b/src/meta/str_snds.c index b731f27c..6a43997a 100644 --- a/src/meta/str_snds.c +++ b/src/meta/str_snds.c @@ -122,7 +122,7 @@ VGMSTREAM * init_vgmstream_str_snds(STREAMFILE *streamFile) { default: goto fail; } - vgmstream->layout_type = layout_str_snds_blocked; + vgmstream->layout_type = layout_blocked_str_snds; vgmstream->meta_type = meta_STR_SNDS; /* channels and loop flag are set by allocate_vgmstream */ @@ -144,7 +144,7 @@ VGMSTREAM * init_vgmstream_str_snds(STREAMFILE *streamFile) { } /* start me up */ - str_snds_block_update(0,vgmstream); + block_update_str_snds(0,vgmstream); return vgmstream; diff --git a/src/meta/sxd.c b/src/meta/sxd.c index 224dc80a..8a8c85ac 100644 --- a/src/meta/sxd.c +++ b/src/meta/sxd.c @@ -25,7 +25,7 @@ VGMSTREAM * init_vgmstream_sxd(STREAMFILE *streamFile) { if (is_dual) { if (read_32bitBE(0x00,streamFile) != 0x53584453) /* "SXDS" */ goto fail; - streamHeader = open_stream_ext(streamFile, "sxd1"); + streamHeader = open_streamfile_by_ext(streamFile, "sxd1"); if (!streamHeader) goto fail; } else { streamHeader = streamFile; diff --git a/src/meta/thp.c b/src/meta/thp.c index 2a9c95b9..1244ff58 100644 --- a/src/meta/thp.c +++ b/src/meta/thp.c @@ -88,10 +88,10 @@ VGMSTREAM * init_vgmstream_thp(STREAMFILE *streamFile) { } vgmstream->full_block_size = read_32bitBE(0x18,streamFile); /* block size of current block, changes every time */ - thp_block_update(start_offset,vgmstream); + block_update_thp(start_offset,vgmstream); vgmstream->coding_type = coding_NGC_DSP; - vgmstream->layout_type = layout_thp_blocked; + vgmstream->layout_type = layout_blocked_thp; vgmstream->meta_type = meta_THP; return vgmstream; diff --git a/src/meta/txth.c b/src/meta/txth.c index 1edce04e..3923e08f 100644 --- a/src/meta/txth.c +++ b/src/meta/txth.c @@ -168,6 +168,8 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) { coding = coding_DVI_IMA_int; if (coding == coding_IMA) coding = coding_IMA_int; + if (coding == coding_AICA) + coding = coding_AICA_int; } /* to avoid endless loops */ @@ -176,7 +178,8 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) { coding == coding_PSX_badflags || coding == coding_IMA_int || coding == coding_DVI_IMA_int || - coding == coding_SDX2_int) ) { + coding == coding_SDX2_int || + coding == coding_AICA_int) ) { goto fail; } } else { @@ -184,7 +187,7 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) { } /* setup adpcm */ - if (coding == coding_AICA) { + if (coding == coding_AICA || coding == coding_AICA_int) { int i; for (i=0;ichannels;i++) { vgmstream->ch[i].adpcm_step_index = 0x7f; @@ -382,14 +385,14 @@ static STREAMFILE * open_txth(STREAMFILE * streamFile) { STREAMFILE * streamText; /* try "(path/)(name.ext).txth" */ - if (!get_streamfile_name(streamFile,filename,PATH_LIMIT)) goto fail; + get_streamfile_name(streamFile,filename,PATH_LIMIT); strcat(filename, ".txth"); streamText = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); if (streamText) return streamText; /* try "(path/)(.ext).txth" */ - if (!get_streamfile_path(streamFile,filename,PATH_LIMIT)) goto fail; - if (!get_streamfile_ext(streamFile,fileext,PATH_LIMIT)) goto fail; + get_streamfile_path(streamFile,filename,PATH_LIMIT); + get_streamfile_ext(streamFile,fileext,PATH_LIMIT); strcat(filename,"."); strcat(filename, fileext); strcat(filename, ".txth"); @@ -397,14 +400,13 @@ static STREAMFILE * open_txth(STREAMFILE * streamFile) { if (streamText) return streamText; /* try "(path/).txth" */ - if (!get_streamfile_path(streamFile,filename,PATH_LIMIT)) goto fail; + get_streamfile_path(streamFile,filename,PATH_LIMIT); strcat(filename, ".txth"); streamText = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); if (streamText) return streamText; -fail: /* not found */ - return 0; + return NULL; } /* Simple text parser of "key = value" lines. diff --git a/src/meta/ubi_sb.c b/src/meta/ubi_sb.c index ad0d9abb..5b9c3339 100644 --- a/src/meta/ubi_sb.c +++ b/src/meta/ubi_sb.c @@ -90,7 +90,7 @@ VGMSTREAM * init_vgmstream_ubi_sb(STREAMFILE *streamFile) { if (sb.autodetect_external) { /* works most of the time but could give false positives */ VGM_LOG("UBI SB: autodetecting external stream '%s'\n", sb.stream_name); - streamData = open_stream_name(streamFile,sb.stream_name); + streamData = open_streamfile_by_filename(streamFile,sb.stream_name); if (!streamData) { streamData = streamFile; /* assume internal */ if (sb.stream_size > get_streamfile_size(streamData)) { @@ -102,7 +102,7 @@ VGMSTREAM * init_vgmstream_ubi_sb(STREAMFILE *streamFile) { } } else if (sb.is_external) { - streamData = open_stream_name(streamFile,sb.stream_name); + streamData = open_streamfile_by_filename(streamFile,sb.stream_name); if (!streamData) { VGM_LOG("UBI SB: external stream '%s' not found\n", sb.stream_name); goto fail; @@ -674,7 +674,7 @@ static int config_sb_header_version(ubi_sb_header * sb, STREAMFILE *streamFile) /* two games with same id; use project file as identifier */ if (sb->version == 0x0012000C && is_sb4) { - STREAMFILE * streamTest = open_stream_name(streamFile, "BIAAUDIO.SP4"); + STREAMFILE * streamTest = open_streamfile_by_filename(streamFile, "BIAAUDIO.SP4"); if (streamTest) { is_biadd_psp = 1; close_streamfile(streamTest); diff --git a/src/meta/vs.c b/src/meta/vs.c index 9fe5ec34..0e7aa209 100644 --- a/src/meta/vs.c +++ b/src/meta/vs.c @@ -42,7 +42,7 @@ VGMSTREAM * init_vgmstream_vs(STREAMFILE *streamFile) { } #endif - vgmstream->layout_type = layout_vs_blocked; + vgmstream->layout_type = layout_blocked_vs; vgmstream->meta_type = meta_VS; @@ -55,15 +55,15 @@ VGMSTREAM * init_vgmstream_vs(STREAMFILE *streamFile) { } /* Calc num_samples */ - vs_block_update(start_offset,vgmstream); + block_update_vs(start_offset,vgmstream); vgmstream->num_samples=0; do { vgmstream->num_samples += vgmstream->current_block_size*28/16; - vs_block_update(vgmstream->next_block_offset,vgmstream); + block_update_vs(vgmstream->next_block_offset,vgmstream); } while (vgmstream->next_block_offsetmeta_type = meta_WS_AUD_old; } - vgmstream->layout_type = layout_ws_aud_blocked; + vgmstream->layout_type = layout_blocked_ws_aud; /* open the file for reading by each channel */ { @@ -118,9 +118,9 @@ VGMSTREAM * init_vgmstream_ws_aud(STREAMFILE *streamFile) { /* start me up */ if (new_type) { - ws_aud_block_update(0xc,vgmstream); + block_update_ws_aud(0xc,vgmstream); } else { - ws_aud_block_update(0x8,vgmstream); + block_update_ws_aud(0x8,vgmstream); } return vgmstream; diff --git a/src/meta/x360_tra.c b/src/meta/x360_tra.c index b9418911..8554076c 100644 --- a/src/meta/x360_tra.c +++ b/src/meta/x360_tra.c @@ -38,7 +38,7 @@ VGMSTREAM * init_vgmstream_x360_tra(STREAMFILE *streamFile) { vgmstream->coding_type = coding_DVI_IMA_int; vgmstream->num_samples = (int32_t)(get_streamfile_size(streamFile) - ((get_streamfile_size(streamFile)/0x204)*4)); - vgmstream->layout_type = layout_tra_blocked; + vgmstream->layout_type = layout_blocked_tra; vgmstream->meta_type = meta_X360_TRA; @@ -51,7 +51,7 @@ VGMSTREAM * init_vgmstream_x360_tra(STREAMFILE *streamFile) { } } - tra_block_update(0,vgmstream); + block_update_tra(0,vgmstream); return vgmstream; /* clean up anything we may have opened */ diff --git a/src/meta/xbox_ims.c b/src/meta/xbox_ims.c index 89aeef60..220eeac1 100644 --- a/src/meta/xbox_ims.c +++ b/src/meta/xbox_ims.c @@ -31,7 +31,7 @@ VGMSTREAM * init_vgmstream_xbox_matx(STREAMFILE *streamFile) { vgmstream->sample_rate = read_16bitLE(0x06,streamFile) & 0xffff; vgmstream->coding_type = coding_XBOX_IMA; - vgmstream->layout_type = layout_matx_blocked; + vgmstream->layout_type = layout_blocked_matx; vgmstream->meta_type = meta_XBOX_MATX; /* open the file for reading by each channel */ @@ -43,15 +43,15 @@ VGMSTREAM * init_vgmstream_xbox_matx(STREAMFILE *streamFile) { } /* Calc num_samples */ - matx_block_update(0,vgmstream); + block_update_matx(0,vgmstream); vgmstream->num_samples=0; do { vgmstream->num_samples += vgmstream->current_block_size/36*64; - matx_block_update(vgmstream->next_block_offset,vgmstream); + block_update_matx(vgmstream->next_block_offset,vgmstream); } while (vgmstream->next_block_offsetcoding_type = coding_XBOX_IMA; - vgmstream->layout_type = layout_xvas_blocked; + vgmstream->layout_type = layout_blocked_xvas; vgmstream->meta_type = meta_XBOX_XVAS; if (!vgmstream_open_stream(vgmstream, streamFile, start_offset)) goto fail; - xvas_block_update(start_offset,vgmstream); + block_update_xvas(start_offset,vgmstream); return vgmstream; fail: diff --git a/src/meta/xnb.c b/src/meta/xnb.c index 98aac098..c20efb11 100644 --- a/src/meta/xnb.c +++ b/src/meta/xnb.c @@ -41,7 +41,7 @@ VGMSTREAM * init_vgmstream_xnb(STREAMFILE *streamFile) { { char reader_name[255+1]; off_t current_offset = 0x0a; - int reader_string_len; + size_t reader_string_len; uint32_t fmt_chunk_size; const char * type_sound = "Microsoft.Xna.Framework.Content.SoundEffectReader"; /* partial "fmt" chunk or XMA */ //const char * type_song = "Microsoft.Xna.Framework.Content.SongReader"; /* just references a companion .wma */ @@ -76,6 +76,7 @@ VGMSTREAM * init_vgmstream_xnb(STREAMFILE *streamFile) { codec = read_16bit(current_offset+0x00, streamFile); channel_count = read_16bit(current_offset+0x02, streamFile); sample_rate = read_32bit(current_offset+0x04, streamFile); + /* 0x08: byte rate */ block_size = read_16bit(current_offset+0x0c, streamFile); bps = read_16bit(current_offset+0x0e, streamFile); @@ -102,6 +103,10 @@ VGMSTREAM * init_vgmstream_xnb(STREAMFILE *streamFile) { switch (codec) { case 0x01: /* Dragon's Blade (Android) */ + /* null in Metagalactic Blitz (PC) */ + if (!block_size) + block_size = (bps == 8 ? 0x01 : 0x02) * channel_count; + vgmstream->coding_type = bps == 8 ? coding_PCM8_U_int : coding_PCM16LE; vgmstream->layout_type = layout_interleave; vgmstream->interleave_block_size = block_size / channel_count; @@ -109,6 +114,7 @@ VGMSTREAM * init_vgmstream_xnb(STREAMFILE *streamFile) { break; case 0x02: /* White Noise Online (PC) */ + if (!block_size) goto fail; vgmstream->coding_type = coding_MSADPCM; vgmstream->layout_type = layout_none; vgmstream->interleave_block_size = block_size; @@ -116,6 +122,7 @@ VGMSTREAM * init_vgmstream_xnb(STREAMFILE *streamFile) { break; case 0x11: + if (!block_size) goto fail; vgmstream->coding_type = coding_MS_IMA; vgmstream->layout_type = layout_none; vgmstream->interleave_block_size = block_size; diff --git a/src/meta/xwb.c b/src/meta/xwb.c index fdb2f196..8c104f46 100644 --- a/src/meta/xwb.c +++ b/src/meta/xwb.c @@ -534,13 +534,13 @@ fail: /* try to get the stream name in the .xwb, though they are very rarely included */ static int get_xwb_name(char * buf, size_t maxsize, int target_subsong, xwb_header * xwb, STREAMFILE *streamFile) { - int read; + size_t read; if (!xwb->names_offset || !xwb->names_size || xwb->names_entry_size > maxsize) goto fail; read = read_string(buf,xwb->names_entry_size, xwb->names_offset + xwb->names_entry_size*(target_subsong-1),streamFile); - if (read <= 0) goto fail; + if (read == 0) goto fail; return 1; @@ -601,7 +601,7 @@ static int get_xsb_name(char * buf, size_t maxsize, int target_subsong, xwb_head xsb_header xsb = {0}; - streamFile = open_stream_ext(streamXwb, "xsb"); + streamFile = open_streamfile_by_ext(streamXwb, "xsb"); if (!streamFile) goto fail; /* check header */ diff --git a/src/streamfile.c b/src/streamfile.c index a35cb0b6..15b82881 100644 --- a/src/streamfile.c +++ b/src/streamfile.c @@ -383,16 +383,22 @@ typedef struct { STREAMFILE sf; STREAMFILE *inner_sf; - void* data; + void* data; /* state for custom reads, malloc'ed + copied on open (to re-open streamfiles cleanly) */ size_t data_size; - size_t (*read_callback)(STREAMFILE *, uint8_t *, off_t, size_t, void*); + size_t (*read_callback)(STREAMFILE *, uint8_t *, off_t, size_t, void*); /* custom read to modify data before copying into buffer */ + size_t (*size_callback)(STREAMFILE *, void*); /* size when custom reads make data smaller/bigger than underlying streamfile */ + //todo would need to make sure re-opened streamfiles work with this, maybe should use init_data_callback per call + //size_t (*close_data_callback)(STREAMFILE *, void*); /* called during close, allows to free stuff in data */ } IO_STREAMFILE; static size_t io_read(IO_STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length) { return streamfile->read_callback(streamfile->inner_sf, dest, offset, length, streamfile->data); } static size_t io_get_size(IO_STREAMFILE *streamfile) { - return streamfile->inner_sf->get_size(streamfile->inner_sf); /* default */ + if (streamfile->size_callback) + return streamfile->size_callback(streamfile->inner_sf, streamfile->data); + else + return streamfile->inner_sf->get_size(streamfile->inner_sf); /* default */ } static off_t io_get_offset(IO_STREAMFILE *streamfile) { return streamfile->inner_sf->get_offset(streamfile->inner_sf); /* default */ @@ -406,7 +412,7 @@ static void io_get_realname(IO_STREAMFILE *streamfile, char *buffer, size_t leng static STREAMFILE *io_open(IO_STREAMFILE *streamfile, const char * const filename, size_t buffersize) { //todo should have some flag to decide if opening other files with IO STREAMFILE *new_inner_sf = streamfile->inner_sf->open(streamfile->inner_sf,filename,buffersize); - return open_io_streamfile(new_inner_sf, streamfile->data, streamfile->data_size, streamfile->read_callback); + return open_io_streamfile(new_inner_sf, streamfile->data, streamfile->data_size, streamfile->read_callback, streamfile->size_callback); } static void io_close(IO_STREAMFILE *streamfile) { streamfile->inner_sf->close(streamfile->inner_sf); @@ -414,7 +420,7 @@ static void io_close(IO_STREAMFILE *streamfile) { free(streamfile); } -STREAMFILE *open_io_streamfile(STREAMFILE *streamfile, void* data, size_t data_size, void* read_callback) { +STREAMFILE *open_io_streamfile(STREAMFILE *streamfile, void* data, size_t data_size, void* read_callback, void* size_callback) { IO_STREAMFILE *this_sf; if (!streamfile) return NULL; @@ -444,6 +450,7 @@ STREAMFILE *open_io_streamfile(STREAMFILE *streamfile, void* data, size_t data_s } this_sf->data_size = data_size; this_sf->read_callback = read_callback; + this_sf->size_callback = size_callback; return &this_sf->sf; } @@ -684,6 +691,37 @@ fail: /* **************************************************** */ +STREAMFILE * open_streamfile_by_ext(STREAMFILE *streamFile, const char * ext) { + char filename_ext[PATH_LIMIT]; + + streamFile->get_name(streamFile,filename_ext,sizeof(filename_ext)); + strcpy(filename_ext + strlen(filename_ext) - strlen(filename_extension(filename_ext)), ext); + + return streamFile->open(streamFile,filename_ext,STREAMFILE_DEFAULT_BUFFER_SIZE); +} + +STREAMFILE * open_streamfile_by_filename(STREAMFILE *streamFile, const char * name) { + char foldername[PATH_LIMIT]; + char filename[PATH_LIMIT]; + const char *path; + + streamFile->get_name(streamFile,foldername,sizeof(foldername)); + + path = strrchr(foldername,DIR_SEPARATOR); + if (path!=NULL) path = path+1; + + if (path) { + strcpy(filename, foldername); + filename[path-foldername] = '\0'; + strcat(filename, name); + } else { + strcpy(filename, name); + } + + return streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); +} + + /* Read a line into dst. The source files are lines separated by CRLF (Windows) / LF (Unux) / CR (Mac). * The line will be null-terminated and CR/LF removed if found. * @@ -740,17 +778,17 @@ size_t get_streamfile_text_line(int dst_length, char * dst, off_t offset, STREAM } -/* reads a c-string, up to maxsize or NULL, returning size. buf is optional. */ -int read_string(char * buf, size_t maxsize, off_t offset, STREAMFILE *streamFile) { - int i; +/* reads a c-string (ANSI only), up to maxsize or NULL, returning size. buf is optional (works as get_string_size). */ +size_t read_string(char * buf, size_t maxsize, off_t offset, STREAMFILE *streamFile) { + size_t pos; - for (i=0; i < maxsize; i++) { - char c = read_8bit(offset + i, streamFile); - if (buf) buf[i] = c; + for (pos = 0; pos < maxsize; pos++) { + char c = read_8bit(offset + pos, streamFile); + if (buf) buf[pos] = c; if (c == '\0') - return i; - if (i+1 == maxsize) { /* null at maxsize and don't validate (expected to be garbage) */ - if (buf) buf[i] = '\0'; + return pos; + if (pos+1 == maxsize) { /* null at maxsize and don't validate (expected to be garbage) */ + if (buf) buf[pos] = '\0'; return maxsize; } if (c < 0x20 || c > 0xA5) @@ -762,37 +800,6 @@ fail: return 0; } -/* Opens an stream using the base streamFile name plus a new extension (ex. for headers in a separate file) */ -STREAMFILE * open_stream_ext(STREAMFILE *streamFile, const char * ext) { - char filename_ext[PATH_LIMIT]; - - streamFile->get_name(streamFile,filename_ext,sizeof(filename_ext)); - strcpy(filename_ext + strlen(filename_ext) - strlen(filename_extension(filename_ext)), ext); - - return streamFile->open(streamFile,filename_ext,STREAMFILE_DEFAULT_BUFFER_SIZE); -} - -/* Opens an stream using the passed name (in the same folder) */ -STREAMFILE * open_stream_name(STREAMFILE *streamFile, const char * name) { - char foldername[PATH_LIMIT]; - char filename[PATH_LIMIT]; - const char *path; - - streamFile->get_name(streamFile,foldername,sizeof(foldername)); - - path = strrchr(foldername,DIR_SEPARATOR); - if (path!=NULL) path = path+1; - - if (path) { - strcpy(filename, foldername); - filename[path-foldername] = '\0'; - strcat(filename, name); - } else { - strcpy(filename, name); - } - - return streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); -} /* Opens a file containing decryption keys and copies to buffer. * Tries combinations of keynames based on the original filename. @@ -869,53 +876,10 @@ fail: return 0; } -/** - * open file containing looping data and copy to buffer - * - * returns true if found and copied - */ -int read_pos_file(uint8_t * buf, size_t bufsize, STREAMFILE *streamFile) { - char posname[PATH_LIMIT]; - char filename[PATH_LIMIT]; - /*size_t bytes_read;*/ - STREAMFILE * streamFilePos= NULL; - - streamFile->get_name(streamFile,filename,sizeof(filename)); - - if (strlen(filename)+4 > sizeof(posname)) goto fail; - - /* try to open a posfile using variations: "(name.ext).pos" */ - { - strcpy(posname, filename); - strcat(posname, ".pos"); - streamFilePos = streamFile->open(streamFile,posname,STREAMFILE_DEFAULT_BUFFER_SIZE); - if (streamFilePos) goto found; - - goto fail; - } - -found: - //if (get_streamfile_size(streamFilePos) != bufsize) goto fail; - - /* allow pos files to be of different sizes in case of new features, just fill all we can */ - memset(buf, 0, bufsize); - read_streamfile(buf, 0, bufsize, streamFilePos); - - close_streamfile(streamFilePos); - - return 1; - -fail: - if (streamFilePos) close_streamfile(streamFilePos); - - return 0; -} - /** - * checks if the stream filename is one of the extensions (comma-separated, ex. "adx" or "adx,aix") - * - * returns 0 on failure + * Checks if the stream filename is one of the extensions (comma-separated, ex. "adx" or "adx,aix"). + * Empty is ok to accept files without extension ("", "adx,,aix"). Returns 0 on failure */ int check_extensions(STREAMFILE *streamFile, const char * cmp_exts) { char filename[PATH_LIMIT]; @@ -990,14 +954,15 @@ int find_chunk(STREAMFILE *streamFile, uint32_t chunk_id, off_t start_offset, in return 0; } -int get_streamfile_name(STREAMFILE *streamFile, char * buffer, size_t size) { +void get_streamfile_name(STREAMFILE *streamFile, char * buffer, size_t size) { streamFile->get_name(streamFile,buffer,size); - return 1; } -int get_streamfile_filename(STREAMFILE *streamFile, char * buffer, size_t size) { +/* copies the filename without path */ +void get_streamfile_filename(STREAMFILE *streamFile, char * buffer, size_t size) { char foldername[PATH_LIMIT]; const char *path; + streamFile->get_name(streamFile,foldername,sizeof(foldername)); //todo Windows CMD accepts both \\ and /, better way to handle this? @@ -1013,9 +978,8 @@ int get_streamfile_filename(STREAMFILE *streamFile, char * buffer, size_t size) } else { strcpy(buffer, foldername); } - return 1; } -int get_streamfile_path(STREAMFILE *streamFile, char * buffer, size_t size) { +void get_streamfile_path(STREAMFILE *streamFile, char * buffer, size_t size) { const char *path; streamFile->get_name(streamFile,buffer,size); @@ -1028,11 +992,8 @@ int get_streamfile_path(STREAMFILE *streamFile, char * buffer, size_t size) { } else { buffer[0] = '\0'; } - - return 1; } -int get_streamfile_ext(STREAMFILE *streamFile, char * filename, size_t size) { +void get_streamfile_ext(STREAMFILE *streamFile, char * filename, size_t size) { streamFile->get_name(streamFile,filename,size); strcpy(filename, filename_extension(filename)); - return 1; } diff --git a/src/streamfile.h b/src/streamfile.h index ff160027..2ee873ad 100644 --- a/src/streamfile.h +++ b/src/streamfile.h @@ -46,7 +46,8 @@ #endif /* struct representing a file with callbacks. Code should use STREAMFILEs and not std C functions - * to do file operations, as plugins may need to provide their own callbacks. */ + * to do file operations, as plugins may need to provide their own callbacks. + * Reads from arbitrary offsets, meaning internally may need fseek equivalents during reads. */ typedef struct _STREAMFILE { size_t (*read)(struct _STREAMFILE *,uint8_t * dest, off_t offset, size_t length); size_t (*get_size)(struct _STREAMFILE *); @@ -65,41 +66,51 @@ typedef struct _STREAMFILE { } STREAMFILE; -/* create a STREAMFILE from path */ -STREAMFILE * open_stdio_streamfile(const char * filename); +/* Opens a standard STREAMFILE, opening from path. + * Uses stdio (FILE) for operations, thus plugins may not want to use it. */ +STREAMFILE *open_stdio_streamfile(const char * filename); -/* create a STREAMFILE from pre-opened file path */ -STREAMFILE * open_stdio_streamfile_by_file(FILE * file, const char * filename); +/* Opens a standard STREAMFILE from a pre-opened FILE. */ +STREAMFILE *open_stdio_streamfile_by_file(FILE * file, const char * filename); -/* A STREAMFILE that doesn't close the underlying stream. +/* Opens a STREAMFILE that doesn't close the underlying streamfile. * Calls to open won't wrap the new SF (assumes it needs to be closed). * Can be used in metas to test custom IO without closing the external SF. */ STREAMFILE *open_wrap_streamfile(STREAMFILE *streamfile); -/* A STREAMFILE that clamps IO to a section of a larger stream. - * Can be used with subfiles inside a bigger file, so it looks standard to a meta. */ +/* Opens a STREAMFILE that clamps reads to a section of a larger streamfile. + * Can be used with subfiles inside a bigger file (to fool metas, or to simplify custom IO). */ STREAMFILE *open_clamp_streamfile(STREAMFILE *streamfile, off_t start, size_t size); -/* A STREAMFILE with custom IO, that clamps IO to a section of a larger stream. - * Can be used with subfiles inside a bigger file, so it looks standard to a meta. */ -STREAMFILE *open_io_streamfile(STREAMFILE *streamfile, void* data, size_t data_size, void* read_callback);//void* size_callback, void* seek_callback); +/* Opens a STREAMFILE that uses custom IO for streamfile reads. + * Can be used to modify data on the fly (ex. decryption), or even transform it from a format to another. */ +STREAMFILE *open_io_streamfile(STREAMFILE *streamfile, void* data, size_t data_size, void* read_callback, void* size_callback); -/* A STREAMFILE that reports a fake name, but still re-opens itself properly. +/* Opens a STREAMFILE that reports a fake name, but still re-opens itself properly. * Can be used to trick a meta's extension check (to call from another, with a modified SF). * When fakename isn't supplied it's read from the streamfile, and the extension swapped with fakeext. * If the fakename is an existing file, open won't work on it as it'll reopen the fake-named streamfile. */ STREAMFILE *open_fakename_streamfile(STREAMFILE *streamfile, const char * fakename, const char * fakeext); -/* A streamfile formed from multiple streamfiles, their data joined during reads. +//todo probably could simply use custom IO +/* Opens streamfile formed from multiple streamfiles, their data joined during reads. * Can be used when data is segmented in multiple separate files. * The first streamfile is used to get names, stream index and so on. */ STREAMFILE *open_multifile_streamfile(STREAMFILE **streamfiles, size_t streamfiles_size); +/* Opens a STREAMFILE from a base pathname + new extension + * Can be used to get companion headers. */ +STREAMFILE * open_streamfile_by_ext(STREAMFILE *streamFile, const char * ext); + +/* Opens a STREAMFILE from a base path + new filename + * Can be used to get companion files. */ +STREAMFILE * open_streamfile_by_filename(STREAMFILE *streamFile, const char * filename); + /* close a file, destroy the STREAMFILE object */ static inline void close_streamfile(STREAMFILE * streamfile) { - if (streamfile==NULL) return; - streamfile->close(streamfile); + if (streamfile!=NULL) + streamfile->close(streamfile); } /* read from a file, returns number of bytes read */ @@ -164,13 +175,9 @@ static inline int8_t read_8bit(off_t offset, STREAMFILE * streamfile) { size_t get_streamfile_text_line(int dst_length, char * dst, off_t offset, STREAMFILE * streamfile, int *line_done_ptr); -STREAMFILE * open_stream_ext(STREAMFILE *streamFile, const char * ext); -STREAMFILE * open_stream_name(STREAMFILE *streamFile, const char * ext); - -int read_string(char * buf, size_t bufsize, off_t offset, STREAMFILE *streamFile); +size_t read_string(char * buf, size_t bufsize, off_t offset, STREAMFILE *streamFile); size_t read_key_file(uint8_t * buf, size_t bufsize, STREAMFILE *streamFile); -int read_pos_file(uint8_t * buf, size_t bufsize, STREAMFILE *streamFile); int check_extensions(STREAMFILE *streamFile, const char * cmp_exts); @@ -178,8 +185,8 @@ int find_chunk_be(STREAMFILE *streamFile, uint32_t chunk_id, off_t start_offset, int find_chunk_le(STREAMFILE *streamFile, uint32_t chunk_id, off_t start_offset, int full_chunk_size, off_t *out_chunk_offset, size_t *out_chunk_size); int find_chunk(STREAMFILE *streamFile, uint32_t chunk_id, off_t start_offset, int full_chunk_size, off_t *out_chunk_offset, size_t *out_chunk_size, int size_big_endian, int zero_size_end); -int get_streamfile_name(STREAMFILE *streamFile, char * buffer, size_t size); -int get_streamfile_filename(STREAMFILE *streamFile, char * buffer, size_t size); -int get_streamfile_path(STREAMFILE *streamFile, char * buffer, size_t size); -int get_streamfile_ext(STREAMFILE *streamFile, char * filename, size_t size); +void get_streamfile_name(STREAMFILE *streamFile, char * buffer, size_t size); +void get_streamfile_filename(STREAMFILE *streamFile, char * buffer, size_t size); +void get_streamfile_path(STREAMFILE *streamFile, char * buffer, size_t size); +void get_streamfile_ext(STREAMFILE *streamFile, char * filename, size_t size); #endif diff --git a/src/vgmstream.c b/src/vgmstream.c index 2081afc7..3af15724 100644 --- a/src/vgmstream.c +++ b/src/vgmstream.c @@ -30,6 +30,7 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_halpst, init_vgmstream_rs03, init_vgmstream_ngc_dsp_std, + init_vgmstream_ngc_dsp_std_le, init_vgmstream_ngc_mdsp_std, init_vgmstream_ngc_dsp_csmp, init_vgmstream_cstr, @@ -154,6 +155,7 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_ps2_ccc, init_vgmstream_psx_fag, init_vgmstream_ps2_mihb, + init_vgmstream_ngc_pdt_split, init_vgmstream_ngc_pdt, init_vgmstream_wii_mus, init_vgmstream_dc_asd, @@ -387,6 +389,11 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_rsd6wma, init_vgmstream_smv, init_vgmstream_nxap, + init_vgmstream_ea_wve_au00, + init_vgmstream_ea_wve_ad10, + init_vgmstream_sthd, + init_vgmstream_pcm_sre, + init_vgmstream_dsp_mcadpcm, init_vgmstream_txth, /* should go at the end (lower priority) */ #ifdef VGM_USE_FFMPEG @@ -460,7 +467,7 @@ static VGMSTREAM * init_vgmstream_internal(STREAMFILE *streamFile) { /* check FFmpeg streams here, for lack of a better place */ if (vgmstream->coding_type == coding_FFmpeg) { ffmpeg_codec_data *data = (ffmpeg_codec_data *) vgmstream->codec_data; - if (data->streamCount && !vgmstream->num_streams) { + if (data && data->streamCount && !vgmstream->num_streams) { vgmstream->num_streams = data->streamCount; } } @@ -599,13 +606,8 @@ void reset_vgmstream(VGMSTREAM * vgmstream) { reset_layout_segmented(vgmstream->layout_data); } - if (vgmstream->layout_type==layout_scd_int) { - scd_int_codec_data *data = vgmstream->codec_data; - int i; - - for (i=0;isubstream_count;i++) { - reset_vgmstream(data->substreams[i]); - } + if (vgmstream->layout_type==layout_layered) { + reset_layout_layered(vgmstream->layout_data); } } @@ -790,28 +792,12 @@ void close_vgmstream(VGMSTREAM * vgmstream) { if (vgmstream->layout_type==layout_segmented) { free_layout_segmented(vgmstream->layout_data); - vgmstream->codec_data = NULL; + vgmstream->layout_data = NULL; } - if (vgmstream->layout_type==layout_scd_int) { - scd_int_codec_data *data = (scd_int_codec_data *) vgmstream->codec_data; - - if (data) { - if (data->substreams) { - int i; - for (i=0;isubstream_count;i++) { - /* note that the close_streamfile won't do anything but deallocate itself, - * there is only one open file in vgmstream->ch[0].streamfile */ - close_vgmstream(data->substreams[i]); - if(data->intfiles[i]) close_streamfile(data->intfiles[i]); - } - free(data->substreams); - free(data->intfiles); - } - - free(data); - } - vgmstream->codec_data = NULL; + if (vgmstream->layout_type==layout_layered) { + free_layout_layered(vgmstream->layout_data); + vgmstream->layout_data = NULL; } @@ -893,32 +879,32 @@ void render_vgmstream(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstre case layout_none: render_vgmstream_nolayout(buffer,sample_count,vgmstream); break; - case layout_mxch_blocked: - case layout_ast_blocked: - case layout_halpst_blocked: - case layout_xa_blocked: + case layout_blocked_mxch: + case layout_blocked_ast: + case layout_blocked_halpst: + case layout_blocked_xa: case layout_blocked_ea_schl: case layout_blocked_ea_1snh: case layout_blocked_caf: case layout_blocked_wsi: - case layout_str_snds_blocked: - case layout_ws_aud_blocked: - case layout_matx_blocked: + case layout_blocked_str_snds: + case layout_blocked_ws_aud: + case layout_blocked_matx: case layout_blocked_dec: - case layout_vs_blocked: - case layout_emff_ps2_blocked: - case layout_emff_ngc_blocked: - case layout_gsb_blocked: - case layout_xvas_blocked: - case layout_thp_blocked: - case layout_filp_blocked: + case layout_blocked_vs: + case layout_blocked_emff_ps2: + case layout_blocked_emff_ngc: + case layout_blocked_gsb: + case layout_blocked_xvas: + case layout_blocked_thp: + case layout_blocked_filp: case layout_blocked_ivaud: case layout_blocked_ea_swvr: case layout_blocked_adm: - case layout_dsp_bdsp_blocked: - case layout_tra_blocked: - case layout_ps2_iab_blocked: - case layout_ps2_strlr_blocked: + case layout_blocked_bdsp: + case layout_blocked_tra: + case layout_blocked_ps2_iab: + case layout_blocked_ps2_strlr: case layout_blocked_rws: case layout_blocked_hwas: case layout_blocked_ea_sns: @@ -926,6 +912,9 @@ void render_vgmstream(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstre case layout_blocked_vgs: case layout_blocked_vawx: case layout_blocked_xvag_subsong: + case layout_blocked_ea_wve_au00: + case layout_blocked_ea_wve_ad10: + case layout_blocked_sthd: render_vgmstream_blocked(buffer,sample_count,vgmstream); break; case layout_aix: @@ -934,8 +923,8 @@ void render_vgmstream(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstre case layout_segmented: render_vgmstream_segmented(buffer,sample_count,vgmstream); break; - case layout_scd_int: - render_vgmstream_scd_int(buffer,sample_count,vgmstream); + case layout_layered: + render_vgmstream_layered(buffer,sample_count,vgmstream); break; default: break; @@ -963,7 +952,6 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) { return 1; case coding_PCM16LE: - case coding_PCM16LE_XOR_int: case coding_PCM16BE: case coding_PCM16_int: case coding_PCM8: @@ -1029,7 +1017,6 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) { case coding_XA: case coding_PSX: case coding_PSX_badflags: - case coding_PSX_bmdx: case coding_HEVAG: return 28; case coding_PSX_cfg: @@ -1048,6 +1035,8 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) { case coding_WS: /* only works if output sample size is 8 bit, which always is for WS ADPCM */ return vgmstream->ws_output_size; case coding_AICA: + return 1; + case coding_AICA_int: return 2; case coding_YAMAHA: return (0x40-0x04*vgmstream->channels) * 2 / vgmstream->channels; @@ -1132,7 +1121,6 @@ int get_vgmstream_frame_size(VGMSTREAM * vgmstream) { return 0; case coding_PCM16LE: - case coding_PCM16LE_XOR_int: case coding_PCM16BE: case coding_PCM16_int: return 0x02; @@ -1192,7 +1180,6 @@ int get_vgmstream_frame_size(VGMSTREAM * vgmstream) { return 0x0e*vgmstream->channels; case coding_PSX: case coding_PSX_badflags: - case coding_PSX_bmdx: case coding_HEVAG: return 0x10; case coding_PSX_cfg: @@ -1214,6 +1201,7 @@ int get_vgmstream_frame_size(VGMSTREAM * vgmstream) { case coding_WS: return vgmstream->current_block_size; case coding_AICA: + case coding_AICA_int: return 0x01; case coding_YAMAHA: case coding_YAMAHA_NXAP: @@ -1340,13 +1328,6 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to samples_to_do); } break; - case coding_PCM16LE_XOR_int: - for (chan=0;chanchannels;chan++) { - decode_pcm16LE_XOR_int(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do); - } - break; case coding_PCM16BE: for (chan=0;chanchannels;chan++) { decode_pcm16BE(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, @@ -1518,13 +1499,6 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to samples_to_do); } break; - case coding_PSX_bmdx: - for (chan=0;chanchannels;chan++) { - decode_psx_bmdx(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do); - } - break; case coding_HEVAG: for (chan=0;chanchannels;chan++) { decode_hevag(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, @@ -1810,10 +1784,13 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to } break; case coding_AICA: + case coding_AICA_int: for (chan=0;chanchannels;chan++) { + int is_stereo = (vgmstream->channels > 1 && vgmstream->coding_type == coding_AICA); + decode_aica(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, vgmstream->channels,vgmstream->samples_into_block, - samples_to_do); + samples_to_do, chan, is_stereo); } break; case coding_YAMAHA: @@ -1953,7 +1930,6 @@ int vgmstream_do_loop(VGMSTREAM * vgmstream) { vgmstream->meta_type == meta_DSP_RS03 || vgmstream->meta_type == meta_DSP_CSTR || vgmstream->coding_type == coding_PSX || - vgmstream->coding_type == coding_PSX_bmdx || vgmstream->coding_type == coding_PSX_badflags) { int i; for (i=0;ichannels;i++) { @@ -2376,9 +2352,9 @@ static int get_vgmstream_average_bitrate_channel_count(VGMSTREAM * vgmstream) { //AAX, AIX, ACM? - if (vgmstream->layout_type==layout_scd_int) { - scd_int_codec_data *data = (scd_int_codec_data *) vgmstream->codec_data; - return (data) ? data->substream_count : 0; + if (vgmstream->layout_type==layout_layered) { + layered_layout_data *data = (layered_layout_data *) vgmstream->layout_data; + return (data) ? data->layer_count : 0; } #ifdef VGM_USE_VORBIS if (vgmstream->coding_type==coding_OGG_VORBIS) { @@ -2410,9 +2386,9 @@ static STREAMFILE * get_vgmstream_average_bitrate_channel_streamfile(VGMSTREAM * { //AAX, AIX? - if (vgmstream->layout_type==layout_scd_int) { - scd_int_codec_data *data = (scd_int_codec_data *) vgmstream->codec_data; - return data->intfiles[channel]; + if (vgmstream->layout_type==layout_layered) { + layered_layout_data *data = (layered_layout_data *) vgmstream->layout_data; + return data->layers[channel]->ch[0].streamfile; } if (vgmstream->coding_type==coding_NWA) { @@ -2535,7 +2511,7 @@ int vgmstream_open_stream(VGMSTREAM * vgmstream, STREAMFILE *streamFile, off_t s /* stream/offsets not needed, manage themselves */ if (vgmstream->layout_type == layout_aix || vgmstream->layout_type == layout_segmented || - vgmstream->layout_type == layout_scd_int) + vgmstream->layout_type == layout_layered) return 1; #ifdef VGM_USE_FFMPEG diff --git a/src/vgmstream.h b/src/vgmstream.h index 30fb844a..b73a692b 100644 --- a/src/vgmstream.h +++ b/src/vgmstream.h @@ -78,7 +78,6 @@ enum { STREAM_NAME_SIZE = 255 }; /* reasonable max */ typedef enum { /* PCM */ coding_PCM16LE, /* little endian 16-bit PCM */ - coding_PCM16LE_XOR_int, /* little endian 16-bit PCM with sample-level xor (for blocks) */ coding_PCM16BE, /* big endian 16-bit PCM */ coding_PCM16_int, /* 16-bit PCM with sample-level interleave (for blocks) */ @@ -111,7 +110,6 @@ typedef enum { coding_XA, /* CD-ROM XA */ coding_PSX, /* Sony PS ADPCM (VAG) */ coding_PSX_badflags, /* Sony PS ADPCM with custom flag byte */ - coding_PSX_bmdx, /* Sony PS ADPCM with BMDX encryption */ coding_PSX_cfg, /* Sony PS ADPCM with configurable frame size (FF XI, SGXD type 5, Bizarre Creations) */ coding_HEVAG, /* Sony PSVita ADPCM */ @@ -129,11 +127,11 @@ typedef enum { coding_MS_IMA, /* Microsoft IMA ADPCM */ coding_XBOX_IMA, /* XBOX IMA ADPCM */ coding_XBOX_IMA_mch, /* XBOX IMA ADPCM (multichannel) */ - coding_XBOX_IMA_int, /* XBOX IMA ADPCM (interleaved/mono) */ + coding_XBOX_IMA_int, /* XBOX IMA ADPCM (mono/interleave) */ coding_NDS_IMA, /* IMA ADPCM w/ NDS layout */ coding_DAT4_IMA, /* Eurocom 'DAT4' IMA ADPCM */ coding_RAD_IMA, /* Radical IMA ADPCM */ - coding_RAD_IMA_mono, /* Radical IMA ADPCM, mono (for interleave) */ + coding_RAD_IMA_mono, /* Radical IMA ADPCM (mono/interleave) */ coding_APPLE_IMA4, /* Apple Quicktime IMA4 */ coding_SNDS_IMA, /* Heavy Iron Studios .snds IMA ADPCM */ coding_OTNS_IMA, /* Omikron The Nomad Soul IMA ADPCM */ @@ -145,7 +143,8 @@ typedef enum { coding_MSADPCM, /* Microsoft ADPCM */ coding_WS, /* Westwood Studios VBR ADPCM */ - coding_AICA, /* Yamaha AICA ADPCM */ + coding_AICA, /* Yamaha AICA ADPCM (stereo) */ + coding_AICA_int, /* Yamaha AICA ADPCM (mono/interleave) */ coding_YAMAHA, /* Yamaha ADPCM */ coding_YAMAHA_NXAP, /* Yamaha ADPCM (NXAP variation) */ coding_NDS_PROCYON, /* Procyon Studio ADPCM */ @@ -217,32 +216,32 @@ typedef enum { layout_interleave, /* equal interleave throughout the stream */ /* headered blocks */ - layout_ast_blocked, - layout_halpst_blocked, - layout_xa_blocked, + layout_blocked_ast, + layout_blocked_halpst, + layout_blocked_xa, layout_blocked_ea_schl, layout_blocked_ea_1snh, layout_blocked_caf, layout_blocked_wsi, - layout_str_snds_blocked, - layout_ws_aud_blocked, - layout_matx_blocked, + layout_blocked_str_snds, + layout_blocked_ws_aud, + layout_blocked_matx, layout_blocked_dec, - layout_xvas_blocked, - layout_vs_blocked, - layout_emff_ps2_blocked, - layout_emff_ngc_blocked, - layout_gsb_blocked, - layout_thp_blocked, - layout_filp_blocked, + layout_blocked_xvas, + layout_blocked_vs, + layout_blocked_emff_ps2, + layout_blocked_emff_ngc, + layout_blocked_gsb, + layout_blocked_thp, + layout_blocked_filp, layout_blocked_ea_swvr, layout_blocked_adm, - layout_dsp_bdsp_blocked, - layout_mxch_blocked, + layout_blocked_bdsp, + layout_blocked_mxch, layout_blocked_ivaud, /* GTA IV .ivaud blocks */ - layout_tra_blocked, /* DefJam Rapstar .tra blocks */ - layout_ps2_iab_blocked, - layout_ps2_strlr_blocked, + layout_blocked_tra, /* DefJam Rapstar .tra blocks */ + layout_blocked_ps2_iab, + layout_blocked_ps2_strlr, layout_blocked_rws, layout_blocked_hwas, layout_blocked_ea_sns, /* newest Electronic Arts blocks, found in SNS/SNU/SPS/etc formats */ @@ -250,11 +249,14 @@ typedef enum { layout_blocked_vgs, /* Guitar Hero II (PS2) */ layout_blocked_vawx, /* No More Heroes 6ch (PS3) */ layout_blocked_xvag_subsong, /* XVAG subsongs [God of War III (PS4)] */ + layout_blocked_ea_wve_au00, /* EA WVE au00 blocks */ + layout_blocked_ea_wve_ad10, /* EA WVE Ad10 blocks */ + layout_blocked_sthd, /* Dream Factory STHD */ /* otherwise odd */ layout_aix, /* CRI AIX's wheels within wheels */ - layout_segmented, /* song divided in segments, each a complete VGMSTREAM */ - layout_scd_int, /* deinterleave done by the SCDINTSTREAMFILE */ + layout_segmented, /* song divided in segments (song sections) */ + layout_layered, /* song divided in layers (song channels) */ } layout_t; @@ -660,10 +662,13 @@ typedef enum { meta_WAVE_segmented, /* EngineBlack games, segmented [Shantae and the Pirate's Curse (PC)] */ meta_SMV, /* Cho Aniki Zero (PSP) */ meta_NXAP, /* Nex Entertainment games [Time Crisis 4 (PS3), Time Crisis Razing Storm (PS3)] */ + meta_EA_WVE_AU00, /* Electronic Arts PS movies [Future Cop - L.A.P.D. (PS), Supercross 2000 (PS)] */ + meta_EA_WVE_AD10, /* Electronic Arts PS movies [Wing Commander 3/4 (PS)] */ + meta_STHD, /* STHD .stx [Kakuto Chojin (Xbox)] */ + meta_MP4, /* MP4/AAC */ + meta_PCM_SRE, /* .PCM+SRE [Viewtiful Joe (PS2)] */ + meta_DSP_MCADPCM, /* Skyrim (Switch) */ -#ifdef VGM_USE_MP4V2 - meta_MP4, /* AAC (iOS) */ -#endif #ifdef VGM_USE_FFMPEG meta_FFmpeg, #endif @@ -716,12 +721,6 @@ typedef struct { uint16_t adx_mult; uint16_t adx_add; - /* BMDX encryption */ - uint8_t bmdx_xor; - uint8_t bmdx_add; - - /* generic encryption */ - uint16_t key_xor; } VGMSTREAMCHANNEL; /* main vgmstream info */ @@ -1057,25 +1056,25 @@ typedef struct { VGMSTREAM **adxs; } aix_codec_data; -/* for files made of segments, each a full subfile (VGMSTREAM) */ +/* for files made of "vertical" segments, one per section of a song (using a complete sub-VGMSTREAM) */ typedef struct { int segment_count; + VGMSTREAM **segments; int current_segment; int loop_segment; - VGMSTREAM **segments; } segmented_layout_data; +/* for files made of "horizontal" layers, one per group of channels (using a complete sub-VGMSTREAM) */ +typedef struct { + int layer_count; + VGMSTREAM **layers; +} layered_layout_data; + /* for compressed NWA */ typedef struct { NWAData *nwa; } nwa_codec_data; -/* SQEX SCD interleaved */ -typedef struct { - int substream_count; - VGMSTREAM **substreams; - STREAMFILE **intfiles; -} scd_int_codec_data; typedef struct { STREAMFILE *streamfile;