#include "meta.h" #include "../coding/coding.h" #define ATX_MAX_SEGMENTS 2 static STREAMFILE* setup_atx_streamfile(STREAMFILE *streamFile); /* .ATX - Media.Vision's segmented RIFF AT3 wrapper [Senjo no Valkyria 3 (PSP), Shining Blade (PSP)] */ VGMSTREAM * init_vgmstream_atx(STREAMFILE *streamFile) { VGMSTREAM *vgmstream = NULL; STREAMFILE *temp_streamFile = NULL; /* check extensions */ if ( !check_extensions(streamFile,"atx")) goto fail; if (read_32bitBE(0x00,streamFile) != 0x41504133) /* "APA3" */ goto fail; /* .ATX is made of subfile segments, handled by the streamFile. * Each segment has a header/footer, and part of the whole data * (i.e. ATRAC3 data ends in a subfile and continues in the next) */ temp_streamFile = setup_atx_streamfile(streamFile); if (!temp_streamFile) goto fail; vgmstream = init_vgmstream_riff(temp_streamFile); if (!vgmstream) goto fail; close_streamfile(temp_streamFile); return vgmstream; fail: close_streamfile(temp_streamFile); close_vgmstream(vgmstream); return NULL; } static STREAMFILE* setup_atx_streamfile(STREAMFILE *streamFile) { STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL; STREAMFILE *segment_streamFiles[ATX_MAX_SEGMENTS] = {0}; char filename[PATH_LIMIT]; size_t filename_len; int i, num_segments = 0; size_t riff_size; if (read_16bitLE(0x1c,streamFile) != 0) goto fail; /* this must be first segment */ if (read_16bitLE(0x1e,streamFile) < 1 || read_16bitLE(0x1e,streamFile) > ATX_MAX_SEGMENTS) goto fail; num_segments = read_16bitLE(0x1e,streamFile); /* expected segment name: X_XXX_XXX_0n.ATX, starting from n=1 */ get_streamfile_filename(streamFile, filename,PATH_LIMIT); filename_len = strlen(filename); if (filename_len < 7 || filename[filename_len - 5] != '1') goto fail; /* setup segments (could avoid reopening first segment but meh) */ for (i = 0; i < num_segments; i++) { off_t subfile_offset; size_t subfile_size; filename[filename_len - 5] = ('0'+i+1); /* ghetto digit conversion */ new_streamFile = open_streamfile_by_filename(streamFile, filename); if (!new_streamFile) goto fail; segment_streamFiles[i] = new_streamFile; if (read_32bitBE(0x00,segment_streamFiles[i]) != 0x41504133) /* "APA3" */ goto fail; /* parse block/segment header (other Media.Vision's files use it too) */ subfile_offset = read_32bitLE(0x08,segment_streamFiles[i]); /* header size */ subfile_size = read_32bitLE(0x14,segment_streamFiles[i]); /* can be 0 in other containers */ if (read_16bitLE(0x1c,segment_streamFiles[i]) != i) goto fail; /* segment sequence */ /* 0x04: block size (should match subfile_size in .ATX) */ /* 0x0c: flags? also in other files, 0x10/18: null, 0x1e: segments */ /* clamp to ignore header/footer during next reads */ new_streamFile = open_clamp_streamfile(segment_streamFiles[i], subfile_offset,subfile_size); if (!new_streamFile) goto fail; segment_streamFiles[i] = new_streamFile; } /* setup with all segments and clamp further using riff_size (last segment has padding) */ riff_size = read_32bitLE(read_32bitLE(0x08,streamFile) + 0x04,streamFile) + 0x08; new_streamFile = open_multifile_streamfile(segment_streamFiles, num_segments); if (!new_streamFile) goto fail; temp_streamFile = new_streamFile; new_streamFile = open_clamp_streamfile(temp_streamFile, 0,riff_size); if (!new_streamFile) goto fail; temp_streamFile = new_streamFile; new_streamFile = open_fakename_streamfile(temp_streamFile, NULL, "at3"); if (!new_streamFile) goto fail; temp_streamFile = new_streamFile; /* if all this worked we'll have this frankenstein streamfile: * fakename( clamp( multifile( segment0=clamp(standard(FILE)), segment1=clamp(standard(FILE)) ) ) ) */ return temp_streamFile; fail: if (!temp_streamFile) { for (i = 0; i < num_segments; i++) { close_streamfile(segment_streamFiles[i]); } } else { close_streamfile(temp_streamFile); /* closes all segments */ } return NULL; }