#include "meta.h" #include "../coding/coding.h" static void read_xps_name(VGMSTREAM *vgmstream, STREAMFILE *streamFile, int file_id); /* .XPS+DAT - From Software games streams [Metal Wolf Chaos (Xbox), Otogi (Xbox)] */ VGMSTREAM * init_vgmstream_xps_dat(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; STREAMFILE * streamData = NULL; off_t start_offset, header_offset; size_t stream_size; int loop_flag, channel_count, sample_rate, codec, loop_start_sample, loop_end_sample, file_id; int total_subsongs, target_subsong = streamFile->stream_index; /* checks */ if (!check_extensions(streamFile, "xps")) goto fail; if (read_32bitLE(0x00,streamFile) != get_streamfile_size(streamFile)) goto fail; if (read_32bitBE(0x0c,streamFile) != 0x64696666) /* "diff" */ goto fail; /* handle .xps+dat (bank .xps are done below) */ streamData = open_streamfile_by_ext(streamFile, "dat"); if (!streamData) goto fail; /* 0x00: approximate file size */ total_subsongs = read_32bitLE(0x04,streamData); if (target_subsong == 0) target_subsong = 1; if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail; header_offset = 0x20 + 0x94*(target_subsong-1); /* could start at 0x0c too */ file_id = read_32bitLE(header_offset+0x00,streamData); start_offset = read_32bitLE(header_offset+0x04,streamData); stream_size = read_32bitLE(header_offset+0x08,streamData); /* 0x0c: loop start offset? */ /* 0x10: loop end offset? */ /* 0x14: always null? */ codec = read_16bitLE(header_offset+0x18,streamData); channel_count = read_16bitLE(header_offset+0x1a,streamData); sample_rate = read_32bitLE(header_offset+0x1c,streamData); /* 0x20: average bitrate */ /* 0x24: block size, bps */ loop_flag = read_32bitLE(header_offset+0x5c,streamData); loop_start_sample = read_32bitLE(header_offset+0x6c,streamData); loop_end_sample = read_32bitLE(header_offset+0x70,streamData) + 1; /* a "smpl" chunk basically */ /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channel_count, loop_flag); if (!vgmstream) goto fail; vgmstream->sample_rate = sample_rate; vgmstream->meta_type = meta_XPS_DAT; vgmstream->loop_start_sample = loop_start_sample; vgmstream->loop_end_sample = loop_end_sample; vgmstream->num_streams = total_subsongs; vgmstream->stream_size = stream_size; switch(codec) { case 0x01: 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); break; case 0x69: vgmstream->coding_type = coding_XBOX_IMA; vgmstream->layout_type = layout_none; vgmstream->num_samples = xbox_ima_bytes_to_samples(stream_size, channel_count); break; default: goto fail; } read_xps_name(vgmstream, streamFile, file_id); if (!vgmstream_open_stream(vgmstream,streamData,start_offset)) goto fail; close_streamfile(streamData); return vgmstream; fail: close_streamfile(streamData); close_vgmstream(vgmstream); return NULL; } static void read_xps_name(VGMSTREAM *vgmstream, STREAMFILE *streamFile, int file_id) { int i, entries; int name_id = -1, udss_name_id; off_t entry_offset = 0x10; /* main section + stream sections (usually same number but not always) */ entries = read_32bitLE(0x04,streamFile); /* "sid\0" entries: find name_id of file_id */ for (i = 0; i < entries; i++) { off_t entry_base = entry_offset; size_t entry_size = read_32bitLE(entry_base+0x00,streamFile); uint32_t entry_id = read_32bitBE(entry_base+0x04,streamFile); size_t entry_pad = read_32bitLE(entry_base+0x08,streamFile); /* 0x0c: always null, rest: entry (format varies) */ entry_offset += entry_size + entry_pad + 0x10; /* sound info entry */ if (entry_id == 0x73696400) { /* "sid\0" */ int entry_file_id = read_32bitLE(entry_base+0x10,streamFile); int entry_name_id = read_32bitLE(entry_base+0x14,streamFile); if (entry_file_id == file_id && name_id == -1) { name_id = entry_name_id; } continue; } /* sound stream entry, otherwise no good */ if (entry_id != 0x75647373) { /* "udss" */ goto fail; } udss_name_id = read_32bitLE(entry_base+0x10,streamFile); if (udss_name_id == name_id) { off_t name_offset = entry_base + 0x10 + 0x08; size_t name_size = entry_size - 0x08; /* includes null */ read_string(vgmstream->stream_name,name_size, name_offset,streamFile); return; } } fail: return; } /* .XPS - From Software games banks [Metal Wolf Chaos (Xbox), Otogi (Xbox)] */ VGMSTREAM * init_vgmstream_xps(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; STREAMFILE * streamData = NULL; int i, entries; off_t entry_offset = 0x10; int total_subsongs, target_subsong = streamFile->stream_index; /* checks */ if (!check_extensions(streamFile, "xps")) goto fail; if (read_32bitLE(0x00,streamFile) != get_streamfile_size(streamFile)) goto fail; if (read_32bitBE(0x0c,streamFile) != 0x64696666) /* "diff" */ goto fail; /* handle .xps alone (stream .xps+data are done above) */ streamData = open_streamfile_by_ext(streamFile, "dat"); if (streamData) goto fail; /* main section + bank sections (usually same number but not always) */ entries = read_32bitLE(0x04,streamFile); total_subsongs = 0; if (target_subsong == 0) target_subsong = 1; if (target_subsong < 0 /*|| target_subsong > total_subsongs || total_subsongs < 1*/) goto fail; /* parse entries: skip (there is probably a stream/bank flag here) */ for (i = 0; i < entries; i++) { off_t entry_base = entry_offset; size_t entry_size = read_32bitLE(entry_base+0x00,streamFile); uint32_t entry_id = read_32bitBE(entry_base+0x04,streamFile); size_t entry_pad = read_32bitLE(entry_base+0x08,streamFile); /* 0x0c: always null, rest: entry (format varies) */ entry_offset += entry_size + entry_pad + 0x10; /* sound info entry */ if (entry_id == 0x73696400) { /* "sid\0" */ /* keep looking for sound banks */ continue; } /* sound bank entry, otherwise no good */ if (entry_id != 0x75647362) { /* "udsb" */ goto fail; } total_subsongs++; /* open internal RIFF */ if (target_subsong == total_subsongs && vgmstream == NULL) { STREAMFILE* temp_streamFile; off_t subsong_offset = entry_base+0x18; size_t subsong_size = read_32bitLE(entry_base+0x14,streamFile); temp_streamFile = setup_subfile_streamfile(streamFile, subsong_offset,subsong_size, "wav"); if (!temp_streamFile) goto fail; vgmstream = init_vgmstream_riff(temp_streamFile); close_streamfile(temp_streamFile); if (!vgmstream) goto fail; } } /* subsong not found */ if (!vgmstream) goto fail; vgmstream->num_streams = total_subsongs; return vgmstream; fail: close_streamfile(streamData); close_vgmstream(vgmstream); return NULL; }