VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; char filename[PATH_LIMIT]; off_t start_offset, meta_offset_offset, meta_offset, post_meta_offset; int32_t loop_start, loop_end; int loop_flag = 0; int channel_count; int codec_id; int aux_chunk_count; int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL; int16_t (*read_16bit)(off_t,STREAMFILE*) = NULL; /* check extension, case insensitive */ streamFile->get_name(streamFile,filename,sizeof(filename)); if (strcasecmp("scd",filename_extension(filename))) goto fail; /* SEDB */ if (read_32bitBE(0,streamFile) != 0x53454442) goto fail; /* SSCF */ if (read_32bitBE(4,streamFile) != 0x53534346) goto fail; if (read_32bitBE(8,streamFile) == 2 || read_32bitBE(8,streamFile) == 3) { /* version 2 BE, as seen in FFXIII demo for PS3 */ /* version 3 BE, as seen in FFXIII for PS3 */ read_32bit = read_32bitBE; read_16bit = read_16bitBE; //size_offset = 0x14; meta_offset_offset = 0x40 + read_16bit(0xe,streamFile); } else if (read_32bitLE(8,streamFile) == 3 || read_32bitLE(8,streamFile) == 2) { /* version 2/3 LE, as seen in FFXIV for ?? */ read_32bit = read_32bitLE; read_16bit = read_16bitLE; //size_offset = 0x10; meta_offset_offset = 0x40 + read_16bit(0xe,streamFile); } else goto fail; /* never mind, FFXIII music_68tak.ps3.scd is 0x80 shorter */ #if 0 /* check file size with header value */ if (read_32bit(size_offset,streamFile) != get_streamfile_size(streamFile)) goto fail; #endif meta_offset = read_32bit(meta_offset_offset,streamFile); /* check that chunk size equals stream size (?) */ loop_start = read_32bit(meta_offset+0x10,streamFile); loop_end = read_32bit(meta_offset+0x14,streamFile); loop_flag = (loop_end > 0); channel_count = read_32bit(meta_offset+4,streamFile); codec_id = read_32bit(meta_offset+0xc,streamFile); post_meta_offset = meta_offset + 0x20; /* data at meta_offset is only 0x20 bytes, but there may be auxiliary chunks before anything else */ aux_chunk_count = read_32bit(meta_offset+0x1c,streamFile); for (; aux_chunk_count > 0; aux_chunk_count --) { /* skip aux chunks */ /*printf("skipping %08x\n", read_32bitBE(post_meta_offset, streamFile));*/ post_meta_offset += read_32bit(post_meta_offset+4,streamFile); } start_offset = post_meta_offset + read_32bit(meta_offset+0x18,streamFile); #ifdef VGM_USE_VORBIS if (codec_id == 0x6) { vgm_vorbis_info_t inf; uint32_t seek_table_size = read_32bit(post_meta_offset+0x10, streamFile); uint32_t vorb_header_size = read_32bit(post_meta_offset+0x14, streamFile); VGMSTREAM * result = NULL; memset(&inf, 0, sizeof(inf)); inf.loop_start = loop_start; inf.loop_end = loop_end; inf.loop_flag = loop_flag; inf.loop_end_found = loop_flag; inf.loop_length_found = 0; inf.layout_type = layout_ogg_vorbis; inf.meta_type = meta_SQEX_SCD; result = init_vgmstream_ogg_vorbis_callbacks(streamFile, filename, NULL, start_offset, &inf); if (result != NULL) { return result; } // try skipping seek table { if ((post_meta_offset-meta_offset) + seek_table_size + vorb_header_size != read_32bit(meta_offset+0x18, streamFile)) { return NULL; } start_offset = post_meta_offset + 0x20 + seek_table_size; result = init_vgmstream_ogg_vorbis_callbacks(streamFile, filename, NULL, start_offset, &inf); if (result != NULL) { return result; } } // failed with Ogg, try deobfuscating header { // skip chunks before xor_byte unsigned char xor_byte; xor_byte = read_8bit(post_meta_offset+2, streamFile); if (xor_byte == 0) { return NULL; } inf.scd_xor = xor_byte; inf.scd_xor_len = vorb_header_size; result = init_vgmstream_ogg_vorbis_callbacks(streamFile, filename, NULL, start_offset, &inf); return result; } } #endif /* 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_32bit(meta_offset+8,streamFile); switch (codec_id) { case 0x1: /* PCM */ vgmstream->coding_type = coding_PCM16LE_int; vgmstream->layout_type = layout_none; vgmstream->num_samples = read_32bit(meta_offset+0,streamFile) / 2 / channel_count; if (loop_flag) { vgmstream->loop_start_sample = loop_start / 2 / channel_count; vgmstream->loop_end_sample = loop_end / 2 / channel_count; } break; #ifdef VGM_USE_MPEG case 0x7: /* MPEG */ { mpeg_codec_data *mpeg_data = NULL; struct mpg123_frameinfo mi; coding_t ct; if (vgmstream->sample_rate == 47999) vgmstream->sample_rate = 48000; if (vgmstream->sample_rate == 44099) vgmstream->sample_rate = 44100; mpeg_data = init_mpeg_codec_data(streamFile, start_offset, vgmstream->sample_rate, vgmstream->channels, &ct, NULL, NULL); if (!mpeg_data) goto fail; vgmstream->codec_data = mpeg_data; if (MPG123_OK != mpg123_info(mpeg_data->m, &mi)) goto fail; vgmstream->coding_type = ct; vgmstream->layout_type = layout_mpeg; if (mi.vbr != MPG123_CBR) goto fail; vgmstream->num_samples = mpeg_bytes_to_samples(read_32bit(meta_offset+0,streamFile), &mi); vgmstream->num_samples -= vgmstream->num_samples%576; if (loop_flag) { vgmstream->loop_start_sample = mpeg_bytes_to_samples(loop_start, &mi); vgmstream->loop_start_sample -= vgmstream->loop_start_sample%576; vgmstream->loop_end_sample = mpeg_bytes_to_samples(loop_end, &mi); vgmstream->loop_end_sample -= vgmstream->loop_end_sample%576; } vgmstream->interleave_block_size = 0; } break; #endif case 0xC: /* MS ADPCM */ vgmstream->coding_type = coding_MSADPCM; vgmstream->layout_type = layout_none; vgmstream->interleave_block_size = read_16bit(post_meta_offset+0xc,streamFile); vgmstream->num_samples = msadpcm_bytes_to_samples(read_32bit(meta_offset+0,streamFile), vgmstream->interleave_block_size, vgmstream->channels); if (loop_flag) { vgmstream->loop_start_sample = msadpcm_bytes_to_samples(loop_start, vgmstream->interleave_block_size, vgmstream->channels); vgmstream->loop_end_sample = msadpcm_bytes_to_samples(loop_end, vgmstream->interleave_block_size, vgmstream->channels); } break; case 0xA: /* GC/Wii DSP ADPCM */ { STREAMFILE * file; int i; const off_t interleave_size = 0x800; const off_t stride_size = interleave_size * channel_count; size_t total_size; scd_int_codec_data * data = NULL; vgmstream->coding_type = coding_NGC_DSP; vgmstream->layout_type = layout_scd_int; /* a normal DSP header... */ vgmstream->num_samples = read_32bitBE(start_offset+0,streamFile); total_size = (read_32bitBE(start_offset+4,streamFile)+1)/2; 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; } } /* the primary streamfile we'll be using */ file = streamFile->open(streamFile,filename,stride_size); if (!file) goto fail; vgmstream->ch[0].streamfile = file; 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;i<channel_count;i++) { STREAMFILE * intfile = open_scdint_with_STREAMFILE(file, "ARBITRARY.DSP", start_offset+interleave_size*i, interleave_size, stride_size, total_size); data->substreams[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 */ /* copy the channels */ memcpy(data->substreams[i]->start_ch,data->substreams[i]->ch,sizeof(VGMSTREAMCHANNEL)*1); /* copy the whole VGMSTREAM */ memcpy(data->substreams[i]->start_vgmstream,data->substreams[i],sizeof(VGMSTREAM)); } } break; #ifdef VGM_USE_FFMPEG case 0xB: /* XMA1/XMA2 */ { uint16_t codec_id = read_16bit(post_meta_offset, streamFile); if (codec_id == 0x165 || codec_id == 0x166) { ffmpeg_codec_data *ffmpeg_data = init_ffmpeg_faux_riff(streamFile, post_meta_offset, start_offset, streamFile->get_size(streamFile) - start_offset, read_32bit == read_32bitBE); 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; if (loop_flag) { vgmstream->loop_start_sample = loop_start; vgmstream->loop_end_sample = loop_end; } } else goto fail; } break; #endif default: goto fail; } vgmstream->meta_type = meta_SQEX_SCD; /* open the file for reading */ if (vgmstream->layout_type != layout_scd_int #ifdef VGM_USE_FFMPEG && vgmstream->coding_type != coding_FFmpeg #endif ) { 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; } } return vgmstream; /* clean up anything we may have opened */ fail: if (vgmstream) close_vgmstream(vgmstream); return NULL; }
/* SCD - Square-Enix games (FF XIII, XIV) */ VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; off_t start_offset, tables_offset, meta_offset, extradata_offset, name_offset = 0; int32_t stream_size, extradata_size, loop_start, loop_end; int loop_flag = 0, channel_count, codec, sample_rate; int version, target_entry, aux_chunk_count; int total_subsongs, target_subsong = streamFile->stream_index; int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL; int16_t (*read_16bit)(off_t,STREAMFILE*) = NULL; /* check extension, case insensitive */ if ( !check_extensions(streamFile, "scd") ) goto fail; /** main header **/ if (read_32bitBE(0x00,streamFile) != 0x53454442 && /* "SEDB" */ read_32bitBE(0x04,streamFile) != 0x53534346) /* "SSCF" */ goto fail; if (read_8bit(0x0c,streamFile) == 0x01) { /* big endian flag */ //size_offset = 0x14; read_32bit = read_32bitBE; read_16bit = read_16bitBE; } else { //size_offset = 0x10; read_32bit = read_32bitLE; read_16bit = read_16bitLE; } /* SSCF version? (older SSCFs from Crisis Core/FFXI X360 seem to be V3/2) */ if (read_8bit(0x0d,streamFile) != 0x04) goto fail; /* v2: FFXIII demo (PS3), FFT0 test files (PC); v3: common; v4: Kingdom Hearts 2.8 (PS4) */ version = read_32bit(0x08,streamFile); if (version != 2 && version != 3 && version != 4) goto fail; tables_offset = read_16bit(0x0e,streamFile); /* usually 0x30 or 0x20 */ #if 0 /* never mind, FFXIII music_68tak.ps3.scd is 0x80 shorter */ /* check file size with header value */ if (read_32bit(size_offset,streamFile) != get_streamfile_size(streamFile)) goto fail; #endif /** offset tables **/ /* 0x00(2): table1/4 (unknown) entries */ /* 0x02(2): table2 (unknown) entries */ /* 0x04(2): table3 (headers) entries */ /* 0x06(2): unknown, varies even for clone files */ /* (implicit: table1 starts at 0x20) */ /* 0x08: table2 (unknown) start offset */ /* 0x0c: table3 (headers) start offset */ /* 0x10: table4 (unknown) start offset */ /* 0x14: always null? */ /* 0x18: table5? (unknown) start offset? */ /* 0x1c: unknown, often null */ /* each table entry is an uint32_t offset; after entries there is padding */ /* if a table isn't present entries is 0 and offset points to next table */ /* find meta_offset in table3 (headers) and total subsongs */ { int i; int headers_entries = read_16bit(tables_offset+0x04,streamFile); off_t headers_offset = read_32bit(tables_offset+0x0c,streamFile); if (target_subsong == 0) target_subsong = 1; total_subsongs = 0; meta_offset = 0; /* manually find subsongs as entries can be dummy (ex. sfx banks in FF XIV or FF Type-0) */ for (i = 0; i < headers_entries; i++) { off_t entry_offset = read_32bit(headers_offset + i*0x04,streamFile); if (read_32bit(entry_offset+0x0c,streamFile) == -1) continue; /* codec -1 when dummy */ total_subsongs++; if (!meta_offset && total_subsongs == target_subsong) { meta_offset = entry_offset; target_entry = i; } } if (meta_offset == 0) goto fail; /* SCD can contain 0 entries too */ } /** stream header **/ stream_size = read_32bit(meta_offset+0x00,streamFile); channel_count = read_32bit(meta_offset+0x04,streamFile); sample_rate = read_32bit(meta_offset+0x08,streamFile); codec = read_32bit(meta_offset+0x0c,streamFile); loop_start = read_32bit(meta_offset+0x10,streamFile); loop_end = read_32bit(meta_offset+0x14,streamFile); extradata_size = read_32bit(meta_offset+0x18,streamFile); aux_chunk_count = read_32bit(meta_offset+0x1c,streamFile); /* 0x01e(2): unknown, seen in some FF XIV sfx (MSADPCM) */ loop_flag = (loop_end > 0); extradata_offset = meta_offset + 0x20; start_offset = extradata_offset + extradata_size; /* only "MARK" chunk is known (some FF XIV PS3 have "STBL" but it's not counted) */ if (aux_chunk_count > 1 && aux_chunk_count < 0xFFFF) { /* some FF XIV Heavensward IMA sfx have 0x01000000 */ VGM_LOG("SCD: unknown aux chunk count %i\n", aux_chunk_count); goto fail; } /* skips aux chunks, sometimes needed (Lightning Returns X360, FF XIV PC) */ if (aux_chunk_count && read_32bitBE(extradata_offset, streamFile) == 0x4D41524B) { /* "MARK" */ extradata_offset += read_32bit(extradata_offset+0x04, streamFile); } /* find name if possible */ if (version == 4) { int info_entries = read_16bit(tables_offset+0x00,streamFile); int headers_entries = read_16bit(tables_offset+0x04,streamFile); off_t info_offset = tables_offset+0x20; /* not very exact as table1 and table3 entries may differ in V3, not sure about V4 */ if (info_entries == headers_entries) { off_t entry_offset = read_16bit(info_offset + 0x04*target_entry,streamFile); name_offset = entry_offset+0x30; } } #ifdef VGM_USE_VORBIS /* special case using init_vgmstream_ogg_vorbis */ if (codec == 0x06) { VGMSTREAM *ogg_vgmstream; uint8_t ogg_version, ogg_byte; ogg_vorbis_meta_info_t ovmi = {0}; ovmi.meta_type = meta_SQEX_SCD; ovmi.total_subsongs = total_subsongs; /* loop values are in bytes, let init_vgmstream_ogg_vorbis find loop comments instead */ ogg_version = read_8bit(extradata_offset + 0x00, streamFile); /* 0x01(1): 0x20 in v2/3, this ogg miniheader size? */ ogg_byte = read_8bit(extradata_offset + 0x02, streamFile); /* 0x03(1): ? in v3 */ if (ogg_version == 0) { /* 0x10? header, then custom Vorbis header before regular Ogg (FF XIV PC v1) */ ovmi.stream_size = stream_size; } else { /* 0x20 header, then seek table */ size_t seek_table_size = read_32bit(extradata_offset+0x10, streamFile); size_t vorb_header_size = read_32bit(extradata_offset+0x14, streamFile); /* 0x18(4): ? (can be 0) */ if ((extradata_offset-meta_offset) + seek_table_size + vorb_header_size != extradata_size) goto fail; ovmi.stream_size = vorb_header_size + stream_size; start_offset = extradata_offset + 0x20 + seek_table_size; /* extradata_size skips vorb_header */ if (ogg_version == 2) { /* header is XOR'ed using byte (FF XIV PC) */ ovmi.decryption_callback = scd_ogg_v2_decryption_callback; ovmi.scd_xor = ogg_byte; ovmi.scd_xor_length = vorb_header_size; } else if (ogg_version == 3) { /* file is XOR'ed using table (FF XIV Heavensward PC) */ ovmi.decryption_callback = scd_ogg_v3_decryption_callback; ovmi.scd_xor = stream_size & 0xFF; /* ogg_byte not used? */ ovmi.scd_xor_length = vorb_header_size + stream_size; } else { VGM_LOG("SCD: unknown ogg_version 0x%x\n", ogg_version); } } /* actual Ogg init */ ogg_vgmstream = init_vgmstream_ogg_vorbis_callbacks(streamFile, NULL, start_offset, &ovmi); if (ogg_vgmstream && name_offset) read_string(ogg_vgmstream->stream_name, PATH_LIMIT, name_offset, streamFile); return ogg_vgmstream; } #endif /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; vgmstream->sample_rate = sample_rate; vgmstream->num_streams = total_subsongs; vgmstream->stream_size = stream_size; vgmstream->meta_type = meta_SQEX_SCD; if (name_offset) read_string(vgmstream->stream_name, PATH_LIMIT, name_offset, streamFile); switch (codec) { case 0x01: /* PCM */ vgmstream->coding_type = coding_PCM16LE; vgmstream->layout_type = layout_interleave; vgmstream->interleave_block_size = 0x02; vgmstream->num_samples = pcm_bytes_to_samples(stream_size, channel_count, 16); if (loop_flag) { 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 0x03: /* PS-ADPCM [Final Fantasy Type-0] */ vgmstream->coding_type = coding_PSX; vgmstream->layout_type = layout_interleave; vgmstream->interleave_block_size = 0x10; vgmstream->num_samples = ps_bytes_to_samples(stream_size, channel_count); if (loop_flag) { 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; case 0x06: /* OGG [Final Fantasy XIII-2 (PC), Final Fantasy XIV (PC)] */ goto fail; /* handled above */ #ifdef VGM_USE_MPEG case 0x07: { /* MPEG [Final Fantasy XIII (PS3)] */ mpeg_codec_data *mpeg_data = NULL; mpeg_custom_config cfg = {0}; cfg.interleave = 0x800; /* for multistream [Final Fantasy XIII-2 (PS3)], otherwise ignored */ cfg.data_size = stream_size; mpeg_data = init_mpeg_custom(streamFile, start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_SCD, &cfg); if (!mpeg_data) goto fail; vgmstream->codec_data = mpeg_data; vgmstream->layout_type = layout_none; /* some Drakengard 3, Kingdom Hearts HD have adjusted sample rate (47999, 44099), for looping? */ vgmstream->num_samples = mpeg_bytes_to_samples(stream_size, mpeg_data); vgmstream->loop_start_sample = mpeg_bytes_to_samples(loop_start, mpeg_data); vgmstream->loop_end_sample = mpeg_bytes_to_samples(loop_end, mpeg_data); /* somehow loops offsets aren't always frame-aligned, and the code below supposedly helped, * but there isn't much difference since MPEG loops are rough (1152-aligned). Seems it * would help more loop_start - ~1000, loop_end + ~1000 (ex. FFXIII-2 music_SunMizu.ps3.scd) */ //vgmstream->num_samples -= vgmstream->num_samples % 576; //vgmstream->loop_start_sample -= vgmstream->loop_start_sample % 576; //vgmstream->loop_end_sample -= vgmstream->loop_end_sample % 576; break; } #endif case 0x0C: /* MS ADPCM [Final Fantasy XIV (PC) sfx] */ vgmstream->coding_type = coding_MSADPCM; vgmstream->layout_type = layout_none; vgmstream->interleave_block_size = read_16bit(extradata_offset+0x0c,streamFile); /* in extradata_offset is a WAVEFORMATEX (including coefs and all) */ vgmstream->num_samples = msadpcm_bytes_to_samples(stream_size, vgmstream->interleave_block_size, vgmstream->channels); if (loop_flag) { vgmstream->loop_start_sample = msadpcm_bytes_to_samples(loop_start, vgmstream->interleave_block_size, vgmstream->channels); vgmstream->loop_end_sample = msadpcm_bytes_to_samples(loop_end, vgmstream->interleave_block_size, vgmstream->channels); } break; case 0x0A: /* DSP ADPCM [Dragon Quest X (Wii)] */ case 0x15: { /* DSP ADPCM [Dragon Quest X (Wii U)] (no apparent differences except higher sample rate) */ const off_t interleave_size = 0x800; const off_t stride_size = interleave_size * channel_count; int i; size_t total_size; 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_layered; /* 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; } 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; } } } /* init layout */ data = init_layout_layered(channel_count); if (!data) goto fail; vgmstream->layout_data = data; /* 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->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; } #ifdef VGM_USE_FFMPEG case 0x0B: { /* XMA2 [Final Fantasy (X360), Lightning Returns (X360) sfx, Kingdom Hearts 2.8 (X1)] */ ffmpeg_codec_data *ffmpeg_data = NULL; uint8_t buf[200]; int32_t bytes; /* extradata_offset+0x00: fmt0x166 header (BE), extradata_offset+0x34: seek table */ bytes = ffmpeg_make_riff_xma_from_fmt_chunk(buf,200, extradata_offset,0x34, stream_size, streamFile, 1); ffmpeg_data = init_ffmpeg_header_offset(streamFile, buf,bytes, start_offset,stream_size); 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; vgmstream->loop_start_sample = loop_start; vgmstream->loop_end_sample = loop_end; xma_fix_raw_samples(vgmstream, streamFile, start_offset,stream_size, 0, 0,0); /* samples are ok, loops? */ break; } case 0x0E: { /* ATRAC3/ATRAC3plus [Lord of Arcana (PSP), Final Fantasy Type-0] */ ffmpeg_codec_data *ffmpeg_data = NULL; /* full RIFF header at start_offset/extradata_offset (same) */ ffmpeg_data = init_ffmpeg_offset(streamFile, start_offset,stream_size); 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; /* fact samples */ vgmstream->loop_start_sample = loop_start; vgmstream->loop_end_sample = loop_end; if (ffmpeg_data->skipSamples <= 0) /* in case FFmpeg didn't get them */ ffmpeg_set_skip_samples(ffmpeg_data, riff_get_fact_skip_samples(streamFile, start_offset)); /* SCD loop/sample values are relative (without skip samples) vs RIFF (with skip samples), no need to adjust */ break; } #endif #ifdef VGM_USE_ATRAC9 case 0x16: { /* ATRAC9 [Kingdom Hearts 2.8 (PS4)] */ atrac9_config cfg = {0}; /* post header has various typical ATRAC9 values */ cfg.channels = vgmstream->channels; cfg.config_data = read_32bit(extradata_offset+0x0c,streamFile); cfg.encoder_delay = read_32bit(extradata_offset+0x18,streamFile); vgmstream->codec_data = init_atrac9(&cfg); if (!vgmstream->codec_data) goto fail; vgmstream->coding_type = coding_ATRAC9; vgmstream->layout_type = layout_none; vgmstream->num_samples = read_32bit(extradata_offset+0x10,streamFile); /* loop values above are also weird and ignored */ vgmstream->loop_start_sample = read_32bit(extradata_offset+0x20, streamFile) - (loop_flag ? cfg.encoder_delay : 0); //loop_start vgmstream->loop_end_sample = read_32bit(extradata_offset+0x24, streamFile) - (loop_flag ? cfg.encoder_delay : 0); //loop_end break; } #endif case -1: /* used for dummy entries */ default: VGM_LOG("SCD: unknown codec 0x%x\n", codec); goto fail; } if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) ) goto fail; return vgmstream; fail: close_vgmstream(vgmstream); return NULL; }
VGMSTREAM * init_vgmstream_rifx(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; char filename[260]; struct riff_fmt_chunk fmt; off_t file_size = -1; int sample_count = 0; int fact_sample_count = -1; off_t start_offset = -1; off_t wiih_offset = -1; uint32_t wiih_size = 0; int loop_flag = 0; off_t loop_start_offset = -1; off_t loop_end_offset = -1; uint32_t riff_size; uint32_t data_size = 0; int FormatChunkFound = 0; int DataChunkFound = 0; /* check extension, case insensitive */ streamFile->get_name(streamFile,filename,sizeof(filename)); if (strcasecmp("wav",filename_extension(filename)) && strcasecmp("lwav",filename_extension(filename))) { goto fail; } /* check header */ if ((uint32_t)read_32bitBE(0,streamFile)!=0x52494658) /* "RIFX" */ goto fail; /* check for WAVE form */ if ((uint32_t)read_32bitBE(8,streamFile)!=0x57415645) /* "WAVE" */ goto fail; riff_size = read_32bitBE(4,streamFile); file_size = get_streamfile_size(streamFile); /* check for tructated RIFF */ if (file_size < riff_size+8) goto fail; /* read through chunks to verify format and find metadata */ { off_t current_chunk = 0xc; /* start with first chunk */ while (current_chunk < file_size && current_chunk < riff_size+8) { uint32_t chunk_type = read_32bitBE(current_chunk,streamFile); off_t chunk_size = read_32bitBE(current_chunk+4,streamFile); if (current_chunk+8+chunk_size > file_size) goto fail; switch(chunk_type) { case 0x666d7420: /* "fmt " */ /* only one per file */ if (FormatChunkFound) goto fail; FormatChunkFound = 1; if (-1 == read_fmt(1, /* big endian == true */ streamFile, current_chunk, &fmt, 0, /* sns == false */ 0)) /* mwv == false */ goto fail; break; case 0x64617461: /* data */ /* at most one per file */ if (DataChunkFound) goto fail; DataChunkFound = 1; start_offset = current_chunk + 8; data_size = chunk_size; break; case 0x736D706C: /* smpl */ /* check loop count */ if (read_32bitBE(current_chunk+0x24, streamFile)==1) { /* check loop info */ if (read_32bitBE(current_chunk+0x2c+4, streamFile)==0) { loop_flag = 1; loop_start_offset = read_32bitBE(current_chunk+0x2c+8, streamFile); loop_end_offset = read_32bitBE(current_chunk+0x2c+0xc,streamFile); } } break; case 0x66616374: /* fact */ if (chunk_size != 4) break; fact_sample_count = read_32bitBE(current_chunk+8, streamFile); break; case 0x57696948: /* WiiH */ wiih_size = read_32bitBE(current_chunk+4, streamFile); wiih_offset = current_chunk+8; break; default: /* ignorance is bliss */ break; } current_chunk += 8+chunk_size; } } if (!FormatChunkFound || !DataChunkFound) goto fail; switch (fmt.coding_type) { case coding_PCM16BE: sample_count = data_size/2/fmt.channel_count; break; case coding_PCM8_U_int: sample_count = data_size/fmt.channel_count; break; case coding_NGC_DSP: /* the only way of getting DSP info right now */ if (wiih_offset < 0 || wiih_size != 0x2e*fmt.channel_count) goto fail; sample_count = data_size/8/fmt.channel_count*14; break; #if 0 /* found in RE:ORC, looks like it should be MS_IMA instead */ case coding_MSADPCM: sample_count = msadpcm_bytes_to_samples(data_size, fmt.block_size, fmt.channel_count); break; #endif default: goto fail; } /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(fmt.channel_count,loop_flag); if (!vgmstream) goto fail; /* fill in the vital statistics */ vgmstream->num_samples = sample_count; vgmstream->sample_rate = fmt.sample_rate; vgmstream->coding_type = fmt.coding_type; vgmstream->layout_type = layout_none; if (fmt.channel_count > 1) { switch (fmt.coding_type) { case coding_PCM8_U_int: case coding_MS_IMA: case coding_MSADPCM: // use layout_none from above break; default: vgmstream->layout_type = layout_interleave; break; } } vgmstream->interleave_block_size = fmt.interleave; switch (fmt.coding_type) { case coding_MSADPCM: case coding_MS_IMA: // override interleave_block_size with frame size vgmstream->interleave_block_size = fmt.block_size; break; default: // use interleave from above break; } if (fmt.coding_type == coding_MS_IMA) vgmstream->interleave_block_size = fmt.block_size; if (loop_flag) { if (loop_start_offset >= 0) { vgmstream->loop_start_sample = loop_start_offset; vgmstream->loop_end_sample = loop_end_offset; vgmstream->meta_type = meta_RIFX_WAVE_smpl; } } else { vgmstream->meta_type = meta_RIFX_WAVE; } /* read from WiiH */ if (wiih_offset >= 0) { int i,j; for (i=0;i<fmt.channel_count;i++) { for (j=0;j<16;j++) vgmstream->ch[i].adpcm_coef[j] = read_16bitBE(wiih_offset + i * 0x2e + j * 2,streamFile); vgmstream->ch[i].adpcm_history1_16 = read_16bitBE(wiih_offset + i * 0x2e + 0x24,streamFile); vgmstream->ch[i].adpcm_history2_16 = read_16bitBE(wiih_offset + i * 0x2e + 0x26,streamFile); } } /* open the file, set up each channel */ { int i; vgmstream->ch[0].streamfile = streamFile->open(streamFile,filename, STREAMFILE_DEFAULT_BUFFER_SIZE); if (!vgmstream->ch[0].streamfile) goto fail; for (i=0;i<fmt.channel_count;i++) { vgmstream->ch[i].streamfile = vgmstream->ch[0].streamfile; vgmstream->ch[i].offset = vgmstream->ch[i].channel_start_offset = start_offset+i*fmt.interleave; } } return vgmstream; /* clean up anything we may have opened */ fail: if (vgmstream) close_vgmstream(vgmstream); return NULL; }
/* XNBm (Windows 7 Phone) */ VGMSTREAM * init_vgmstream_xnbm(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; char filename[260]; struct riff_fmt_chunk fmt; off_t file_size = -1; int sample_count = 0; off_t start_offset = -1; int loop_flag = 0; #if 0 long loop_start_ms = -1; long loop_end_ms = -1; off_t loop_start_offset = -1; off_t loop_end_offset = -1; #endif uint32_t xnbm_size; uint32_t data_size = 0; /* check extension, case insensitive */ streamFile->get_name(streamFile,filename,sizeof(filename)); if (strcasecmp("xnb",filename_extension(filename))) { goto fail; } /* check header */ if ((uint32_t)read_32bitBE(0,streamFile)!=0x584E426d) /* "XNBm" */ goto fail; /* check version? */ if ((uint32_t)read_16bitLE(4,streamFile)!=5) goto fail; xnbm_size = read_32bitLE(6,streamFile); file_size = get_streamfile_size(streamFile); /* check for tructated XNBm */ if (file_size < xnbm_size) goto fail; /* read through chunks to verify format and find metadata */ { off_t current_chunk = 0xa; /* start with first chunk */ int id_string_len; uint32_t fmt_chunk_size; /* flag? count of strings? */ if (read_8bit(current_chunk ++, streamFile) != 1) goto fail; /* string length */ id_string_len = read_8bit(current_chunk ++, streamFile); /* skip string */ /* may want to check this ID "Microsoft.Xna.Framework.Content.SoundEffectReader" */ current_chunk += id_string_len; /* ???? */ if (read_32bitLE(current_chunk, streamFile) != 0) goto fail; current_chunk += 4; /* ???? */ if (read_8bit(current_chunk ++, streamFile) != 0) goto fail; /* flag? count of chunks? */ if (read_8bit(current_chunk++, streamFile) != 1) goto fail; /* fmt size */ fmt_chunk_size = read_32bitLE(current_chunk, streamFile); current_chunk += 4; if (-1 == read_fmt(0, /* big endian == false */ streamFile, current_chunk-8, /* read_fmt() expects to skip "fmt "+size */ &fmt, 0, /* sns == false */ 0)) /* mwv == false */ goto fail; current_chunk += fmt_chunk_size; /* data size! */ data_size = read_32bitLE(current_chunk, streamFile); current_chunk += 4; start_offset = current_chunk; } switch (fmt.coding_type) { case coding_PCM16LE: sample_count = data_size/2/fmt.channel_count; break; case coding_PCM8_U_int: sample_count = data_size/fmt.channel_count; break; case coding_MSADPCM: sample_count = msadpcm_bytes_to_samples(data_size, fmt.block_size, fmt.channel_count); break; case coding_MS_IMA: sample_count = (data_size / fmt.block_size) * (fmt.block_size - 4 * fmt.channel_count) * 2 / fmt.channel_count + ((data_size % fmt.block_size) ? (data_size % fmt.block_size - 4 * fmt.channel_count) * 2 / fmt.channel_count : 0); break; default: goto fail; } /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(fmt.channel_count,loop_flag); if (!vgmstream) goto fail; /* fill in the vital statistics */ vgmstream->num_samples = sample_count; vgmstream->sample_rate = fmt.sample_rate; vgmstream->coding_type = fmt.coding_type; vgmstream->layout_type = layout_none; if (fmt.channel_count > 1) { switch (fmt.coding_type) { case coding_PCM8_U_int: case coding_MS_IMA: case coding_MSADPCM: // use layout_none from above break; default: vgmstream->layout_type = layout_interleave; break; } } vgmstream->interleave_block_size = fmt.interleave; switch (fmt.coding_type) { case coding_MSADPCM: case coding_MS_IMA: // override interleave_block_size with frame size vgmstream->interleave_block_size = fmt.block_size; break; default: // use interleave from above break; } vgmstream->meta_type = meta_XNBm; /* open the file, set up each channel */ { int i; vgmstream->ch[0].streamfile = streamFile->open(streamFile,filename, STREAMFILE_DEFAULT_BUFFER_SIZE); if (!vgmstream->ch[0].streamfile) goto fail; for (i=0;i<fmt.channel_count;i++) { vgmstream->ch[i].streamfile = vgmstream->ch[0].streamfile; vgmstream->ch[i].offset = vgmstream->ch[i].channel_start_offset = start_offset+i*fmt.interleave; } } return vgmstream; /* clean up anything we may have opened */ fail: if (vgmstream) close_vgmstream(vgmstream); return NULL; }
VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; char filename[260]; struct riff_fmt_chunk fmt; off_t file_size = -1; int sample_count = 0; int fact_sample_count = -1; off_t start_offset = -1; int loop_flag = 0; long loop_start_ms = -1; long loop_end_ms = -1; off_t loop_start_offset = -1; off_t loop_end_offset = -1; uint32_t riff_size; uint32_t data_size = 0; int FormatChunkFound = 0; int DataChunkFound = 0; /* Level-5 mwv */ int mwv = 0; off_t mwv_pflt_offset = -1; off_t mwv_ctrl_offset = -1; /* Ubisoft sns */ int sns = 0; /* check extension, case insensitive */ streamFile->get_name(streamFile,filename,sizeof(filename)); if (strcasecmp("wav",filename_extension(filename)) && strcasecmp("lwav",filename_extension(filename))) { if (!strcasecmp("mwv",filename_extension(filename))) mwv = 1; else if (!strcasecmp("sns",filename_extension(filename))) sns = 1; else goto fail; } /* check header */ if ((uint32_t)read_32bitBE(0,streamFile)!=0x52494646) /* "RIFF" */ goto fail; /* check for WAVE form */ if ((uint32_t)read_32bitBE(8,streamFile)!=0x57415645) /* "WAVE" */ goto fail; riff_size = read_32bitLE(4,streamFile); file_size = get_streamfile_size(streamFile); /* check for tructated RIFF */ if (file_size < riff_size+8) goto fail; /* read through chunks to verify format and find metadata */ { off_t current_chunk = 0xc; /* start with first chunk */ while (current_chunk < file_size && current_chunk < riff_size+8) { uint32_t chunk_type = read_32bitBE(current_chunk,streamFile); off_t chunk_size = read_32bitLE(current_chunk+4,streamFile); if (current_chunk+8+chunk_size > file_size) goto fail; switch(chunk_type) { case 0x666d7420: /* "fmt " */ /* only one per file */ if (FormatChunkFound) goto fail; FormatChunkFound = 1; if (-1 == read_fmt(0, /* big endian == false*/ streamFile, current_chunk, &fmt, sns, mwv)) goto fail; break; case 0x64617461: /* data */ /* at most one per file */ if (DataChunkFound) goto fail; DataChunkFound = 1; start_offset = current_chunk + 8; data_size = chunk_size; break; case 0x4C495354: /* LIST */ /* what lurks within?? */ switch (read_32bitBE(current_chunk + 8, streamFile)) { case 0x6164746C: /* adtl */ /* yay, atdl is its own little world */ parse_adtl(current_chunk + 8, chunk_size, streamFile, &loop_start_ms,&loop_end_ms,&loop_flag); break; default: break; } break; case 0x736D706C: /* smpl */ /* check loop count */ if (read_32bitLE(current_chunk+0x24, streamFile)==1) { /* check loop info */ if (read_32bitLE(current_chunk+0x2c+4, streamFile)==0) { loop_flag = 1; loop_start_offset = read_32bitLE(current_chunk+0x2c+8, streamFile); loop_end_offset = read_32bitLE(current_chunk+0x2c+0xc,streamFile); } } break; case 0x70666c74: /* pflt */ if (!mwv) break; /* ignore if not in an mwv */ /* predictor filters */ mwv_pflt_offset = current_chunk; break; case 0x6374726c: /* ctrl */ if (!mwv) break; /* ignore if not in an mwv */ /* loops! */ if (read_32bitLE(current_chunk+8, streamFile)) { loop_flag = 1; } mwv_ctrl_offset = current_chunk; break; case 0x66616374: /* fact */ if (chunk_size != 4 && (!(sns && chunk_size == 0x10))) break; fact_sample_count = read_32bitLE(current_chunk+8, streamFile); break; default: /* ignorance is bliss */ break; } current_chunk += 8+chunk_size; } } if (!FormatChunkFound || !DataChunkFound) goto fail; switch (fmt.coding_type) { case coding_PCM16LE: sample_count = data_size/2/fmt.channel_count; break; case coding_PCM8_U_int: sample_count = data_size/fmt.channel_count; break; case coding_L5_555: sample_count = data_size/0x12/fmt.channel_count*32; break; case coding_MSADPCM: sample_count = msadpcm_bytes_to_samples(data_size, fmt.block_size, fmt.channel_count); break; case coding_MS_IMA: sample_count = (data_size / fmt.block_size) * (fmt.block_size - 4 * fmt.channel_count) * 2 / fmt.channel_count + ((data_size % fmt.block_size) ? (data_size % fmt.block_size - 4 * fmt.channel_count) * 2 / fmt.channel_count : 0); break; case coding_NGC_DSP: break; default: goto fail; } /* .sns uses fact chunk */ if (sns) { if (-1 == fact_sample_count) goto fail; sample_count = fact_sample_count; } /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(fmt.channel_count,loop_flag); if (!vgmstream) goto fail; /* fill in the vital statistics */ vgmstream->num_samples = sample_count; vgmstream->sample_rate = fmt.sample_rate; vgmstream->coding_type = fmt.coding_type; vgmstream->layout_type = layout_none; if (fmt.channel_count > 1) { switch (fmt.coding_type) { case coding_PCM8_U_int: case coding_MS_IMA: case coding_MSADPCM: // use layout_none from above break; default: vgmstream->layout_type = layout_interleave; break; } } vgmstream->interleave_block_size = fmt.interleave; switch (fmt.coding_type) { case coding_MSADPCM: case coding_MS_IMA: // override interleave_block_size with frame size vgmstream->interleave_block_size = fmt.block_size; break; default: // use interleave from above break; } if (loop_flag) { if (loop_start_ms >= 0) { vgmstream->loop_start_sample = (long long)loop_start_ms*fmt.sample_rate/1000; vgmstream->loop_end_sample = (long long)loop_end_ms*fmt.sample_rate/1000; vgmstream->meta_type = meta_RIFF_WAVE_labl_Marker; } else if (loop_start_offset >= 0) { vgmstream->loop_start_sample = loop_start_offset; vgmstream->loop_end_sample = loop_end_offset; vgmstream->meta_type = meta_RIFF_WAVE_smpl; } else if (mwv && mwv_ctrl_offset != -1) { vgmstream->loop_start_sample = read_32bitLE(mwv_ctrl_offset+12, streamFile); vgmstream->loop_end_sample = sample_count; } } else { vgmstream->meta_type = meta_RIFF_WAVE; } if (mwv) { int i, c; if (fmt.coding_type == coding_L5_555) { const int filter_order = 3; int filter_count = read_32bitLE(mwv_pflt_offset+12, streamFile); if (mwv_pflt_offset == -1 || read_32bitLE(mwv_pflt_offset+8, streamFile) != filter_order || read_32bitLE(mwv_pflt_offset+4, streamFile) < 8 + filter_count * 4 * filter_order) goto fail; if (filter_count > 0x20) goto fail; for (c = 0; c < fmt.channel_count; c++) { for (i = 0; i < filter_count * filter_order; i++) { vgmstream->ch[c].adpcm_coef_3by32[i] = read_32bitLE( mwv_pflt_offset+16+i*4, streamFile ); } } } vgmstream->meta_type = meta_RIFF_WAVE_MWV; } if (sns) { int c; /* common codebook? */ static const int16_t coef[16] = {0x04ab,0xfced,0x0789,0xfedf,0x09a2,0xfae5,0x0c90,0xfac1, 0x084d,0xfaa4,0x0982,0xfdf7,0x0af6,0xfafa,0x0be6,0xfbf5}; for (c = 0; c < fmt.channel_count; c++) { int i; for (i = 0; i < 16; i++) { vgmstream->ch[c].adpcm_coef[i] = coef[i]; } } vgmstream->meta_type = meta_RIFF_WAVE_SNS; } /* open the file, set up each channel */ { int i; vgmstream->ch[0].streamfile = streamFile->open(streamFile,filename, STREAMFILE_DEFAULT_BUFFER_SIZE); if (!vgmstream->ch[0].streamfile) goto fail; for (i=0;i<fmt.channel_count;i++) { vgmstream->ch[i].streamfile = vgmstream->ch[0].streamfile; vgmstream->ch[i].offset = vgmstream->ch[i].channel_start_offset = start_offset+i*fmt.interleave; } } return vgmstream; /* clean up anything we may have opened */ fail: if (vgmstream) close_vgmstream(vgmstream); return NULL; }