vgmstream/src/meta/atx.c

114 lines
4.3 KiB
C

#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;
}