mirror of
https://github.com/vgmstream/vgmstream.git
synced 2024-12-14 15:42:52 +01:00
8f9b22d46f
Meant to simplify layouts
386 lines
14 KiB
C
386 lines
14 KiB
C
#include "meta.h"
|
|
#include "../coding/coding.h"
|
|
#include "../layout/layout.h"
|
|
#include "../util.h"
|
|
|
|
|
|
|
|
/* known GENH types */
|
|
typedef enum {
|
|
PSX = 0, /* PSX ADPCM */
|
|
XBOX = 1, /* XBOX IMA ADPCM */
|
|
NGC_DTK = 2, /* NGC ADP/DTK ADPCM */
|
|
PCM16BE = 3, /* 16bit big endian PCM */
|
|
PCM16LE = 4, /* 16bit little endian PCM */
|
|
PCM8 = 5, /* 8bit PCM */
|
|
SDX2 = 6, /* SDX2 (3D0 games) */
|
|
DVI_IMA = 7, /* DVI IMA ADPCM */
|
|
MPEG = 8, /* MPEG (MP3) */
|
|
IMA = 9, /* IMA ADPCM */
|
|
AICA = 10, /* AICA ADPCM (dreamcast) */
|
|
MSADPCM = 11, /* MS ADPCM (windows) */
|
|
NGC_DSP = 12, /* NGC DSP (GC) */
|
|
PCM8_U_int = 13, /* 8bit unsigned PCM (interleaved) */
|
|
PSX_bf = 14, /* PSX ADPCM bad flagged */
|
|
MS_IMA = 15, /* Microsoft IMA ADPCM */
|
|
PCM8_U = 16, /* 8bit unsigned PCM */
|
|
APPLE_IMA4 = 17, /* Apple Quicktime 4-bit IMA ADPCM */
|
|
ATRAC3 = 18, /* raw ATRAC3 */
|
|
ATRAC3PLUS = 19, /* raw ATRAC3PLUS */
|
|
XMA1 = 20, /* raw XMA1 */
|
|
XMA2 = 21, /* raw XMA2 */
|
|
FFMPEG = 22, /* any headered FFmpeg format */
|
|
AC3 = 23, /* AC3/SPDIF */
|
|
} genh_type;
|
|
|
|
typedef struct {
|
|
genh_type codec;
|
|
int codec_mode;
|
|
size_t interleave;
|
|
|
|
int channels;
|
|
int32_t sample_rate;
|
|
|
|
size_t data_size;
|
|
off_t start_offset;
|
|
|
|
int32_t num_samples;
|
|
int32_t loop_start_sample;
|
|
int32_t loop_end_sample;
|
|
int skip_samples_mode;
|
|
int32_t skip_samples;
|
|
|
|
int loop_flag;
|
|
|
|
int32_t coef[2];
|
|
int32_t coef_splitted[2];
|
|
int32_t coef_type;
|
|
int32_t coef_interleave_type;
|
|
int coef_big_endian;
|
|
|
|
} genh_header;
|
|
|
|
static int parse_genh(STREAMFILE * streamFile, genh_header * genh);
|
|
|
|
/* GENH is an artificial "generic" header for headerless streams */
|
|
VGMSTREAM * init_vgmstream_genh(STREAMFILE *streamFile) {
|
|
VGMSTREAM * vgmstream = NULL;
|
|
genh_header genh = {0};
|
|
coding_t coding;
|
|
int i, j;
|
|
|
|
|
|
/* check extension, case insensitive */
|
|
if (!check_extensions(streamFile,"genh")) goto fail;
|
|
|
|
/* check header magic */
|
|
if (read_32bitBE(0x0,streamFile) != 0x47454e48) goto fail;
|
|
|
|
/* process the header */
|
|
if (!parse_genh(streamFile, &genh))
|
|
goto fail;
|
|
|
|
|
|
/* type to coding conversion */
|
|
switch (genh.codec) {
|
|
case PSX: coding = coding_PSX; break;
|
|
case XBOX: coding = coding_XBOX; break;
|
|
case NGC_DTK: coding = coding_NGC_DTK; break;
|
|
case PCM16BE: coding = coding_PCM16BE; break;
|
|
case PCM16LE: coding = coding_PCM16LE; break;
|
|
case PCM8: coding = coding_PCM8; break;
|
|
case SDX2: coding = coding_SDX2; break;
|
|
case DVI_IMA: coding = coding_DVI_IMA; break;
|
|
#ifdef VGM_USE_MPEG
|
|
case MPEG: coding = coding_MPEG_layer3; break; /* we later find out exactly which */
|
|
#endif
|
|
case IMA: coding = coding_IMA; break;
|
|
case AICA: coding = coding_AICA; break;
|
|
case MSADPCM: coding = coding_MSADPCM; break;
|
|
case NGC_DSP: coding = coding_NGC_DSP; break;
|
|
case PCM8_U_int: coding = coding_PCM8_U_int; break;
|
|
case PSX_bf: coding = coding_PSX_badflags; break;
|
|
case MS_IMA: coding = coding_MS_IMA; break;
|
|
case PCM8_U: coding = coding_PCM8_U; break;
|
|
case APPLE_IMA4: coding = coding_APPLE_IMA4; break;
|
|
#ifdef VGM_USE_FFMPEG
|
|
case ATRAC3:
|
|
case ATRAC3PLUS:
|
|
case XMA1:
|
|
case XMA2:
|
|
case AC3:
|
|
case FFMPEG: coding = coding_FFmpeg; break;
|
|
#endif
|
|
default:
|
|
goto fail;
|
|
}
|
|
|
|
|
|
/* build the VGMSTREAM */
|
|
vgmstream = allocate_vgmstream(genh.channels,genh.loop_flag);
|
|
if (!vgmstream) goto fail;
|
|
|
|
vgmstream->sample_rate = genh.sample_rate;
|
|
vgmstream->num_samples = genh.num_samples;
|
|
vgmstream->loop_start_sample = genh.loop_start_sample;
|
|
vgmstream->loop_end_sample = genh.loop_end_sample;
|
|
|
|
/* codec specific */
|
|
switch (coding) {
|
|
case coding_PCM8_U_int:
|
|
vgmstream->layout_type = layout_none;
|
|
break;
|
|
case coding_PCM16LE:
|
|
case coding_PCM16BE:
|
|
case coding_PCM8:
|
|
case coding_PCM8_U:
|
|
case coding_SDX2:
|
|
case coding_PSX:
|
|
case coding_PSX_badflags:
|
|
case coding_DVI_IMA:
|
|
case coding_IMA:
|
|
case coding_AICA:
|
|
case coding_APPLE_IMA4:
|
|
vgmstream->interleave_block_size = genh.interleave;
|
|
if (vgmstream->channels > 1)
|
|
{
|
|
if (coding == coding_SDX2) {
|
|
coding = coding_SDX2_int;
|
|
}
|
|
|
|
if (vgmstream->interleave_block_size==0xffffffff) {// || vgmstream->interleave_block_size == 0) {
|
|
vgmstream->layout_type = layout_none;
|
|
}
|
|
else {
|
|
vgmstream->layout_type = layout_interleave;
|
|
if (coding == coding_DVI_IMA)
|
|
coding = coding_DVI_IMA_int;
|
|
if (coding == coding_IMA)
|
|
coding = coding_IMA_int;
|
|
}
|
|
|
|
/* to avoid endless loops */
|
|
if (!genh.interleave && (
|
|
coding == coding_PSX ||
|
|
coding == coding_PSX_badflags ||
|
|
coding == coding_IMA_int ||
|
|
coding == coding_DVI_IMA_int ||
|
|
coding == coding_SDX2_int) ) {
|
|
goto fail;
|
|
}
|
|
} else {
|
|
vgmstream->layout_type = layout_none;
|
|
}
|
|
|
|
/* setup adpcm */
|
|
if (coding == coding_AICA) {
|
|
int i;
|
|
for (i=0;i<vgmstream->channels;i++) {
|
|
vgmstream->ch[i].adpcm_step_index = 0x7f;
|
|
}
|
|
}
|
|
|
|
break;
|
|
case coding_MS_IMA:
|
|
if (!genh.interleave) goto fail; /* creates garbage */
|
|
|
|
vgmstream->interleave_block_size = genh.interleave;
|
|
vgmstream->layout_type = layout_none;
|
|
break;
|
|
case coding_MSADPCM:
|
|
if (vgmstream->channels > 2) goto fail;
|
|
if (!genh.interleave) goto fail; /* creates garbage */
|
|
|
|
vgmstream->interleave_block_size = genh.interleave;
|
|
vgmstream->layout_type = layout_none;
|
|
break;
|
|
case coding_XBOX:
|
|
vgmstream->layout_type = layout_none;
|
|
break;
|
|
case coding_NGC_DTK:
|
|
if (vgmstream->channels != 2) goto fail;
|
|
vgmstream->layout_type = layout_none;
|
|
break;
|
|
case coding_NGC_DSP:
|
|
if (genh.coef_interleave_type == 0) {
|
|
if (!genh.interleave) goto fail;
|
|
vgmstream->layout_type = layout_interleave;
|
|
vgmstream->interleave_block_size = genh.interleave;
|
|
} else if (genh.coef_interleave_type == 1) {
|
|
if (!genh.interleave) goto fail;
|
|
coding = coding_NGC_DSP_subint;
|
|
vgmstream->interleave_block_size = genh.interleave;
|
|
vgmstream->layout_type = layout_none;
|
|
} else if (genh.coef_interleave_type == 2) {
|
|
vgmstream->layout_type = layout_none;
|
|
}// else {
|
|
// goto fail;
|
|
//}
|
|
|
|
/* get coefs */
|
|
for (i=0;i<vgmstream->channels;i++) {
|
|
int16_t (*read_16bit)(off_t , STREAMFILE*) = genh.coef_big_endian ? read_16bitBE : read_16bitLE;
|
|
|
|
/* normal/split coefs bit flag */
|
|
if ((genh.coef_type & 1) == 0) { /* not set: normal coefs, all 16 interleaved into one array */
|
|
for (j=0;j<16;j++) {
|
|
vgmstream->ch[i].adpcm_coef[j] = read_16bit(genh.coef[i]+j*2,streamFile);
|
|
}
|
|
}
|
|
else { /* set: split coefs, 8 coefs in the main array, additional offset to 2nd array given at 0x34 for left, 0x38 for right */
|
|
for (j=0;j<8;j++) {
|
|
vgmstream->ch[i].adpcm_coef[j*2]=read_16bit(genh.coef[i]+j*2,streamFile);
|
|
vgmstream->ch[i].adpcm_coef[j*2+1]=read_16bit(genh.coef_splitted[i]+j*2,streamFile);
|
|
}
|
|
}
|
|
}
|
|
|
|
break;
|
|
#ifdef VGM_USE_MPEG
|
|
case coding_MPEG_layer3:
|
|
vgmstream->layout_type = layout_none;
|
|
vgmstream->codec_data = init_mpeg_codec_data(streamFile, genh.start_offset, &coding, vgmstream->channels);
|
|
if (!vgmstream->codec_data) goto fail;
|
|
|
|
break;
|
|
#endif
|
|
#ifdef VGM_USE_FFMPEG
|
|
case coding_FFmpeg: {
|
|
ffmpeg_codec_data *ffmpeg_data = NULL;
|
|
|
|
if (genh.codec == FFMPEG || genh.codec == AC3) {
|
|
/* default FFmpeg */
|
|
ffmpeg_data = init_ffmpeg_offset(streamFile, genh.start_offset,genh.data_size);
|
|
if ( !ffmpeg_data ) goto fail;
|
|
|
|
//if (vgmstream->num_samples == 0)
|
|
// vgmstream->num_samples = ffmpeg_data->totalSamples; /* sometimes works */
|
|
}
|
|
else {
|
|
/* fake header FFmpeg */
|
|
uint8_t buf[200];
|
|
int32_t bytes;
|
|
|
|
if (genh.codec == ATRAC3) {
|
|
int block_size = genh.interleave;
|
|
int joint_stereo;
|
|
switch(genh.codec_mode) {
|
|
case 0: joint_stereo = vgmstream->channels > 1 && genh.interleave/vgmstream->channels==0x60 ? 1 : 0; break; /* autodetect */
|
|
case 1: joint_stereo = 1; break; /* force joint stereo */
|
|
case 2: joint_stereo = 0; break; /* force stereo */
|
|
default: goto fail;
|
|
}
|
|
|
|
bytes = ffmpeg_make_riff_atrac3(buf, 200, vgmstream->num_samples, genh.data_size, vgmstream->channels, vgmstream->sample_rate, block_size, joint_stereo, genh.skip_samples);
|
|
}
|
|
else if (genh.codec == ATRAC3PLUS) {
|
|
int block_size = genh.interleave;
|
|
|
|
bytes = ffmpeg_make_riff_atrac3plus(buf, 200, vgmstream->num_samples, genh.data_size, vgmstream->channels, vgmstream->sample_rate, block_size, genh.skip_samples);
|
|
}
|
|
else if (genh.codec == XMA1) {
|
|
int xma_stream_mode = genh.codec_mode == 1 ? 1 : 0;
|
|
|
|
bytes = ffmpeg_make_riff_xma1(buf, 100, vgmstream->num_samples, genh.data_size, vgmstream->channels, vgmstream->sample_rate, xma_stream_mode);
|
|
}
|
|
else if (genh.codec == XMA2) {
|
|
int block_size = genh.interleave ? genh.interleave : 2048;
|
|
int block_count = genh.data_size / block_size;
|
|
|
|
bytes = ffmpeg_make_riff_xma2(buf, 200, vgmstream->num_samples, genh.data_size, vgmstream->channels, vgmstream->sample_rate, block_count, block_size);
|
|
}
|
|
else {
|
|
goto fail;
|
|
}
|
|
if (bytes <= 0) goto fail;
|
|
|
|
ffmpeg_data = init_ffmpeg_header_offset(streamFile, buf,bytes, genh.start_offset,genh.data_size);
|
|
if ( !ffmpeg_data ) goto fail;
|
|
}
|
|
|
|
vgmstream->codec_data = ffmpeg_data;
|
|
vgmstream->layout_type = layout_none;
|
|
|
|
/* force encoder delay */
|
|
if (genh.skip_samples_mode && genh.skip_samples >= 0) {
|
|
ffmpeg_set_skip_samples(ffmpeg_data, genh.skip_samples);
|
|
}
|
|
|
|
break;
|
|
}
|
|
#endif
|
|
default:
|
|
break;
|
|
}
|
|
|
|
vgmstream->coding_type = coding;
|
|
vgmstream->meta_type = meta_GENH;
|
|
|
|
|
|
if ( !vgmstream_open_stream(vgmstream,streamFile,genh.start_offset) )
|
|
goto fail;
|
|
|
|
return vgmstream;
|
|
|
|
fail:
|
|
close_vgmstream(vgmstream);
|
|
return NULL;
|
|
}
|
|
|
|
|
|
static int parse_genh(STREAMFILE * streamFile, genh_header * genh) {
|
|
size_t header_size;
|
|
|
|
genh->channels = read_32bitLE(0x4,streamFile);
|
|
|
|
genh->interleave = read_32bitLE(0x8,streamFile);
|
|
genh->sample_rate = read_32bitLE(0xc,streamFile);
|
|
genh->loop_start_sample = read_32bitLE(0x10,streamFile);
|
|
genh->loop_end_sample = read_32bitLE(0x14,streamFile);
|
|
|
|
genh->codec = read_32bitLE(0x18,streamFile);
|
|
genh->start_offset = read_32bitLE(0x1C,streamFile);
|
|
header_size = read_32bitLE(0x20,streamFile);
|
|
/* HACK to support old genh */
|
|
if (header_size == 0) {
|
|
genh->start_offset = 0x800;
|
|
header_size = 0x800;
|
|
}
|
|
/* check for audio data start past header end */
|
|
if (header_size > genh->start_offset) goto fail;
|
|
|
|
genh->coef[0] = read_32bitLE(0x24,streamFile);
|
|
genh->coef[1] = read_32bitLE(0x28,streamFile);
|
|
genh->coef_interleave_type = read_32bitLE(0x2C,streamFile);
|
|
|
|
/* DSP coefficient variants */
|
|
/* bit 0 flag - split coefs (2 arrays) */
|
|
/* bit 1 flag - little endian coefs (for some 3DS) */
|
|
genh->coef_type = read_32bitLE(0x30,streamFile);
|
|
genh->coef_big_endian = ((genh->coef_type & 2) == 0);
|
|
|
|
/* when using split coefficients, 2nd array is at: */
|
|
genh->coef_splitted[0] = read_32bitLE(0x34,streamFile);
|
|
genh->coef_splitted[1] = read_32bitLE(0x38,streamFile);
|
|
|
|
/* other fields */
|
|
genh->num_samples = read_32bitLE(0x40,streamFile);
|
|
genh->skip_samples = read_32bitLE(0x44,streamFile); /* for FFmpeg based codecs */
|
|
genh->skip_samples_mode = read_8bit(0x48,streamFile); /* 0=autodetect, 1=force manual value @ 0x44 */
|
|
if (genh->codec == ATRAC3 || genh->codec == ATRAC3PLUS)
|
|
genh->codec_mode = read_8bit(0x49,streamFile); /* 0=autodetect, 1=force joint stereo, 2=force full stereo */
|
|
if (genh->codec == XMA1 || genh->codec == XMA2)
|
|
genh->codec_mode = read_8bit(0x4a,streamFile); /* 0=default (4ch = 2ch + 2ch), 1=single (4ch = 1ch + 1ch + 1ch + 1ch) */
|
|
genh->data_size = read_32bitLE(0x50,streamFile);
|
|
if (genh->data_size == 0)
|
|
genh->data_size = get_streamfile_size(streamFile) - genh->start_offset;
|
|
|
|
genh->num_samples = genh->num_samples > 0 ? genh->num_samples : genh->loop_end_sample;
|
|
genh->loop_flag = genh->loop_start_sample != -1;
|
|
|
|
return 1;
|
|
|
|
fail:
|
|
return 0;
|
|
}
|