mirror of
https://github.com/vgmstream/vgmstream.git
synced 2024-11-24 15:00:11 +01:00
commit
b5fd056cb9
@ -197,6 +197,7 @@ static const char* extension_list[] = {
|
||||
"omu",
|
||||
"otm",
|
||||
|
||||
"p1d", //txth/reserved [Farming Simulator 18 (3DS)]
|
||||
"p2bt",
|
||||
"p3d",
|
||||
"past",
|
||||
@ -316,11 +317,13 @@ static const char* extension_list[] = {
|
||||
"wb",
|
||||
"wem",
|
||||
"wii",
|
||||
"wip", //txth/reserved [Colin McRae DiRT (PC)]
|
||||
"wmus",
|
||||
"wp2",
|
||||
"wpd",
|
||||
"wsd",
|
||||
"wsi",
|
||||
"wv2", //txth/reserved [Slave Zero (PC)]
|
||||
"wvs",
|
||||
|
||||
"xa",
|
||||
@ -873,6 +876,7 @@ static const meta_info meta_info_list[] = {
|
||||
{meta_NGC_ULW, "Criterion ULW raw header"},
|
||||
{meta_PC_XA30, "Reflections XA30 PC header"},
|
||||
{meta_WII_04SW, "Reflections 04SW header"},
|
||||
{meta_TXTH, "TXTH Generic Header"},
|
||||
|
||||
#ifdef VGM_USE_VORBIS
|
||||
{meta_OGG_VORBIS, "Ogg Vorbis"},
|
||||
|
@ -1126,6 +1126,10 @@
|
||||
RelativePath=".\meta\tun.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\meta\txth.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\meta\ubi_ckd.c"
|
||||
>
|
||||
|
@ -166,6 +166,7 @@
|
||||
<ClCompile Include="meta\seg.c" />
|
||||
<ClCompile Include="meta\sqex_scd.c" />
|
||||
<ClCompile Include="meta\tun.c" />
|
||||
<ClCompile Include="meta\txth.c" />
|
||||
<ClCompile Include="meta\wii_ras.c" />
|
||||
<ClCompile Include="meta\wpd.c" />
|
||||
<ClCompile Include="meta\x360_ast.c" />
|
||||
|
@ -1033,6 +1033,9 @@
|
||||
<ClCompile Include="meta\tun.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\txth.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\wpd.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
|
@ -678,4 +678,6 @@ VGMSTREAM * init_vgmstream_pc_xa30(STREAMFILE * streamFile);
|
||||
|
||||
VGMSTREAM * init_vgmstream_wii_04sw(STREAMFILE * streamFile);
|
||||
|
||||
VGMSTREAM * init_vgmstream_txth(STREAMFILE * streamFile);
|
||||
|
||||
#endif /*_META_H*/
|
||||
|
@ -4,50 +4,43 @@
|
||||
|
||||
VGMSTREAM * init_vgmstream_ngc_adpdtk(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
STREAMFILE * chstreamfile;
|
||||
char filename[PATH_LIMIT];
|
||||
|
||||
size_t file_size;
|
||||
int i;
|
||||
off_t start_offset = 0;
|
||||
int channel_count = 2, loop_flag = 0; /* always stereo, no loop */
|
||||
|
||||
/* check extension, case insensitive */
|
||||
streamFile->get_name(streamFile,filename,sizeof(filename));
|
||||
if (strcasecmp("adp",filename_extension(filename)) &&
|
||||
strcasecmp("dtk",filename_extension(filename))) goto fail;
|
||||
if ( !check_extensions(streamFile,"dtk,adp"))
|
||||
goto fail;
|
||||
|
||||
/* file size is the only way to determine sample count */
|
||||
file_size = get_streamfile_size(streamFile);
|
||||
/* .adp files have no header, and the ext is common, so all we can do is look for valid first frames */
|
||||
if (check_extensions(streamFile,"adp")) {
|
||||
int i;
|
||||
for (i = 0; i < 10; i++) { /* try a bunch of frames */
|
||||
if (read_8bit(0x00 + i*0x20,streamFile) != read_8bit(0x02 + i*0x20,streamFile) ||
|
||||
read_8bit(0x01 + i*0x20,streamFile) != read_8bit(0x03 + i*0x20,streamFile))
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
/* .adp files have no header, so all we can do is look for a valid first frame */
|
||||
if (read_8bit(0,streamFile)!=read_8bit(2,streamFile) || read_8bit(1,streamFile)!=read_8bit(3,streamFile)) goto fail;
|
||||
|
||||
/* Hopefully we haven't falsely detected something else... */
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(2,0); /* always stereo, no loop */
|
||||
vgmstream = allocate_vgmstream(channel_count, loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->num_samples = file_size/32*28;
|
||||
vgmstream->num_samples = get_streamfile_size(streamFile) / 32 * 28;
|
||||
vgmstream->sample_rate = 48000;
|
||||
vgmstream->coding_type = coding_NGC_DTK;
|
||||
vgmstream->layout_type = layout_none;
|
||||
vgmstream->meta_type = meta_NGC_ADPDTK;
|
||||
|
||||
/* locality is such that two streamfiles is silly */
|
||||
chstreamfile = streamFile->open(streamFile,filename,32*0x400);
|
||||
if (!chstreamfile) goto fail;
|
||||
|
||||
for (i=0;i<2;i++) {
|
||||
vgmstream->ch[i].channel_start_offset =
|
||||
vgmstream->ch[i].offset = 0;
|
||||
|
||||
vgmstream->ch[i].streamfile = chstreamfile;
|
||||
}
|
||||
/* open the file for reading */
|
||||
if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) )
|
||||
goto fail;
|
||||
|
||||
return vgmstream;
|
||||
|
||||
/* clean up anything we may have opened */
|
||||
fail:
|
||||
if (vgmstream) close_vgmstream(vgmstream);
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -60,9 +60,9 @@ VGMSTREAM * init_vgmstream_pc_adp_otns(STREAMFILE *streamFile) {
|
||||
/* no ID, only a basic 0x10 header with filesize and nulls; do some extra checks */
|
||||
datasize = read_32bitLE(0x00,streamFile) & 0x00FFFFFF; /*24 bit*/
|
||||
if (datasize + 0x10 != streamFile->get_size(streamFile)
|
||||
&& read_32bitLE(0x04,streamFile) != 0
|
||||
&& read_32bitLE(0x08,streamFile) != 0
|
||||
&& read_32bitLE(0x10,streamFile) != 0)
|
||||
|| read_32bitLE(0x04,streamFile) != 0
|
||||
|| read_32bitLE(0x08,streamFile) != 0
|
||||
|| read_32bitLE(0x10,streamFile) != 0)
|
||||
goto fail;
|
||||
|
||||
stereo_flag = read_8bit(0x03, streamFile);
|
||||
|
@ -23,8 +23,14 @@ VGMSTREAM * init_vgmstream_ps2_strlr(STREAMFILE *streamFile) {
|
||||
goto fail;
|
||||
#endif
|
||||
|
||||
/* don't hijack Sonic & Sega All Stars Racing X360 (xma) */
|
||||
if (read_32bitBE(0x00,streamFile) == 0x52494646) /* "RIFF"*/
|
||||
/* don't hijack Sonic & Sega All Stars Racing X360 (xma) */
|
||||
if (read_32bitBE(0x00,streamFile) == 0x52494646)
|
||||
goto fail; /* "RIFF"*/
|
||||
/* don't hijack Mad Dash Racing (Xbox) */
|
||||
if (read_32bitLE(0x0c,streamFile) == 1
|
||||
&& read_32bitLE(0x010,streamFile) == 0
|
||||
&& read_32bitLE(0x400,streamFile) == 0
|
||||
&& read_32bitLE(0x7f0,streamFile) == 0)
|
||||
goto fail;
|
||||
|
||||
loop_flag = 0;
|
||||
|
672
src/meta/txth.c
Normal file
672
src/meta/txth.c
Normal file
@ -0,0 +1,672 @@
|
||||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
#include "../layout/layout.h"
|
||||
#include "../util.h"
|
||||
|
||||
/* known TXTH 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 */
|
||||
} txth_type;
|
||||
|
||||
typedef struct {
|
||||
txth_type codec;
|
||||
txth_type codec_mode;
|
||||
uint32_t interleave;
|
||||
|
||||
uint32_t id_value;
|
||||
uint32_t id_offset;
|
||||
|
||||
uint32_t channels;
|
||||
uint32_t sample_rate;
|
||||
|
||||
uint32_t data_size;
|
||||
int data_size_set;
|
||||
uint32_t start_offset;
|
||||
|
||||
int sample_type_bytes;
|
||||
uint32_t num_samples;
|
||||
uint32_t loop_start_sample;
|
||||
uint32_t loop_end_sample;
|
||||
uint32_t loop_adjust;
|
||||
int skip_samples_set;
|
||||
uint32_t skip_samples;
|
||||
|
||||
uint32_t loop_flag;
|
||||
int loop_flag_set;
|
||||
|
||||
uint32_t coef_offset;
|
||||
uint32_t coef_spacing;
|
||||
uint32_t coef_mode;
|
||||
uint32_t coef_big_endian;
|
||||
|
||||
} txth_header;
|
||||
|
||||
static int parse_txth(STREAMFILE * streamFile, STREAMFILE * streamText, txth_header * txth);
|
||||
static int parse_keyval(STREAMFILE * streamFile, STREAMFILE * streamText, txth_header * txth, const char * key, const char * val);
|
||||
static int parse_num(STREAMFILE * streamFile, const char * val, uint32_t * out_value);
|
||||
static int get_bytes_to_samples(txth_header * txth, uint32_t bytes);
|
||||
|
||||
|
||||
/* TXTH - an artificial "generic" header for headerless streams.
|
||||
* Similar to GENH, but with a single separate .txth file in the dir and text-based. */
|
||||
VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
STREAMFILE * streamText = NULL;
|
||||
txth_header txth;
|
||||
coding_t coding;
|
||||
int i, j;
|
||||
|
||||
/* no need for ID or ext checks -- if a .TXTH exists all is good
|
||||
* (player still needs to accept the ext, so at worst rename to .vgmstream) */
|
||||
{
|
||||
char filename[PATH_LIMIT];
|
||||
char fileext[PATH_LIMIT];
|
||||
|
||||
/* try "(path/)(name.ext).txth" */
|
||||
if (!get_streamfile_name(streamFile,filename,PATH_LIMIT)) goto fail;
|
||||
strcat(filename, ".txth");
|
||||
streamText = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE);
|
||||
if (streamText) goto found;
|
||||
|
||||
/* try "(path/)(.ext).txth" */
|
||||
if (!get_streamfile_path(streamFile,filename,PATH_LIMIT)) goto fail;
|
||||
if (!get_streamfile_ext(streamFile,fileext,PATH_LIMIT)) goto fail;
|
||||
strcat(filename,".");
|
||||
strcat(filename, fileext);
|
||||
strcat(filename, ".txth");
|
||||
streamText = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE);
|
||||
if (streamText) goto found;
|
||||
|
||||
/* try "(path/).txth" */
|
||||
if (!get_streamfile_path(streamFile,filename,PATH_LIMIT)) goto fail;
|
||||
strcat(filename, ".txth");
|
||||
streamText = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE);
|
||||
if (streamText) goto found;
|
||||
|
||||
/* not found */
|
||||
goto fail;
|
||||
}
|
||||
found:
|
||||
|
||||
/* process the text file */
|
||||
memset(&txth,0,sizeof(txth_header));
|
||||
if (!parse_txth(streamFile, streamText, &txth))
|
||||
goto fail;
|
||||
|
||||
|
||||
/* type to coding conversion */
|
||||
switch (txth.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_MPEG1_L3; 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 FFMPEG: coding = coding_FFmpeg; break;
|
||||
#endif
|
||||
default:
|
||||
goto fail;
|
||||
}
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(txth.channels,txth.loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->sample_rate = txth.sample_rate;
|
||||
vgmstream->num_samples = txth.num_samples;
|
||||
vgmstream->loop_start_sample = txth.loop_start_sample;
|
||||
vgmstream->loop_end_sample = txth.loop_end_sample;
|
||||
|
||||
/* codec specific (taken from GENH with minimal changes) */
|
||||
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 = txth.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 (!txth.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 (!txth.interleave) goto fail; /* creates garbage */
|
||||
|
||||
vgmstream->interleave_block_size = txth.interleave;
|
||||
vgmstream->layout_type = layout_none;
|
||||
break;
|
||||
case coding_MSADPCM:
|
||||
if (vgmstream->channels > 2) goto fail;
|
||||
if (!txth.interleave) goto fail; /* creates garbage */
|
||||
|
||||
vgmstream->interleave_block_size = txth.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 (txth.channels > 1 && txth.codec_mode == 0) {
|
||||
if (!txth.interleave) goto fail;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = txth.interleave;
|
||||
} else if (txth.channels > 1 && txth.codec_mode == 1) {
|
||||
if (!txth.interleave) goto fail;
|
||||
vgmstream->layout_type = layout_interleave_byte;
|
||||
vgmstream->interleave_block_size = txth.interleave;
|
||||
} else if (txth.channels == 1 || txth.codec_mode == 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*) = txth.coef_big_endian ? read_16bitBE : read_16bitLE;
|
||||
|
||||
/* normal/split coefs */
|
||||
if (txth.coef_mode == 0) {
|
||||
for (j=0;j<16;j++) {
|
||||
vgmstream->ch[i].adpcm_coef[j] = read_16bit(txth.coef_offset + i*txth.coef_spacing + j*2,streamFile);
|
||||
}
|
||||
} else {
|
||||
goto fail; //IDK what is this
|
||||
/*
|
||||
for (j=0;j<8;j++) {
|
||||
vgmstream->ch[i].adpcm_coef[j*2]=read_16bit(coef[i]+j*2,streamFile);
|
||||
vgmstream->ch[i].adpcm_coef[j*2+1]=read_16bit(coef_splitted[i]+j*2,streamFile);
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
#ifdef VGM_USE_MPEG
|
||||
case coding_MPEG1_L3:
|
||||
vgmstream->layout_type = layout_mpeg;
|
||||
vgmstream->codec_data = init_mpeg_codec_data(streamFile, txth.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 (txth.codec == FFMPEG) {
|
||||
/* default FFmpeg */
|
||||
ffmpeg_data = init_ffmpeg_offset(streamFile, txth.start_offset,txth.data_size);
|
||||
if ( !ffmpeg_data ) goto fail;
|
||||
}
|
||||
else {
|
||||
/* fake header FFmpeg */
|
||||
uint8_t buf[200];
|
||||
int32_t bytes;
|
||||
|
||||
if (txth.codec == ATRAC3) {
|
||||
int block_size = txth.interleave;
|
||||
int joint_stereo;
|
||||
switch(txth.codec_mode) {
|
||||
case 0: joint_stereo = vgmstream->channels > 1 && txth.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, txth.data_size, vgmstream->channels, vgmstream->sample_rate, block_size, joint_stereo, txth.skip_samples);
|
||||
}
|
||||
else if (txth.codec == ATRAC3PLUS) {
|
||||
int block_size = txth.interleave;
|
||||
|
||||
bytes = ffmpeg_make_riff_atrac3plus(buf, 200, vgmstream->num_samples, txth.data_size, vgmstream->channels, vgmstream->sample_rate, block_size, txth.skip_samples);
|
||||
}
|
||||
else if (txth.codec == XMA1) {
|
||||
int xma_stream_mode = txth.codec_mode == 1 ? 1 : 0;
|
||||
|
||||
bytes = ffmpeg_make_riff_xma1(buf, 100, vgmstream->num_samples, txth.data_size, vgmstream->channels, vgmstream->sample_rate, xma_stream_mode);
|
||||
}
|
||||
else if (txth.codec == XMA2) {
|
||||
int block_size = txth.interleave ? txth.interleave : 2048;
|
||||
int block_count = txth.data_size / block_size;
|
||||
|
||||
bytes = ffmpeg_make_riff_xma2(buf, 200, vgmstream->num_samples, txth.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, txth.start_offset,txth.data_size);
|
||||
if ( !ffmpeg_data ) goto fail;
|
||||
}
|
||||
|
||||
vgmstream->codec_data = ffmpeg_data;
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
||||
/* force encoder delay */
|
||||
if (txth.skip_samples_set) {
|
||||
ffmpeg_set_skip_samples(ffmpeg_data, txth.skip_samples);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
if (txth.sample_type_bytes && (txth.codec == XMA1 || txth.codec == XMA2)) {
|
||||
/* manually find sample offsets */
|
||||
ms_sample_data msd;
|
||||
memset(&msd,0,sizeof(ms_sample_data));
|
||||
|
||||
msd.xma_version = 1;
|
||||
msd.channels = txth.channels;
|
||||
msd.data_offset = txth.start_offset;
|
||||
msd.data_size = txth.data_size;
|
||||
msd.loop_flag = txth.loop_flag;
|
||||
msd.loop_start_b = txth.loop_start_sample;
|
||||
msd.loop_end_b = txth.loop_end_sample;
|
||||
msd.loop_start_subframe = txth.loop_adjust & 0xF; /* lower 4b: subframe where the loop starts, 0..4 */
|
||||
msd.loop_end_subframe = txth.loop_adjust >> 4; /* upper 4b: subframe where the loop ends, 0..3 */
|
||||
|
||||
xma_get_samples(&msd, streamFile);
|
||||
vgmstream->num_samples = msd.num_samples;
|
||||
vgmstream->loop_start_sample = msd.loop_start_sample;
|
||||
vgmstream->loop_end_sample = msd.loop_end_sample;
|
||||
//skip_samples = msd.skip_samples; //todo add skip samples
|
||||
}
|
||||
#endif
|
||||
|
||||
vgmstream->coding_type = coding;
|
||||
vgmstream->meta_type = meta_TXTH;
|
||||
|
||||
|
||||
if ( !vgmstream_open_stream(vgmstream,streamFile,txth.start_offset) )
|
||||
goto fail;
|
||||
|
||||
if (streamText) close_streamfile(streamText);
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
if (streamText) close_streamfile(streamText);
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Simple text parser of "key = value" lines.
|
||||
* The code is meh and error handling not exactly the best. */
|
||||
static int parse_txth(STREAMFILE * streamFile, STREAMFILE * streamText, txth_header * txth) {
|
||||
off_t off = 0;
|
||||
off_t file_size = get_streamfile_size(streamText);
|
||||
const size_t LINE_MAX = 0x2000; /* arbitrary max */
|
||||
|
||||
txth->data_size = get_streamfile_size(streamFile); /* for later use */
|
||||
|
||||
/* skip BOM if needed */
|
||||
if (read_16bitLE(0x00, streamText) == 0xFFFE || read_16bitLE(0x00, streamText) == 0xFEFF)
|
||||
off = 0x02;
|
||||
|
||||
/* read lines */
|
||||
while (off < file_size) {
|
||||
char line[LINE_MAX];
|
||||
char key[LINE_MAX];
|
||||
char val[LINE_MAX]; /* at least as big as a line to avoid overflows (I hope) */
|
||||
int ok;
|
||||
off_t line_start = off, line_end = 0;
|
||||
line[0] = key[0] = val[0] = 0;
|
||||
|
||||
/* find line end */
|
||||
while (line_end == 0 && off - line_start < LINE_MAX) {
|
||||
char c = (char)read_8bit(off, streamText);
|
||||
if (c == '\n')
|
||||
line_end = off;
|
||||
else if (off >= file_size)
|
||||
line_end = off-1;
|
||||
|
||||
off++;
|
||||
}
|
||||
|
||||
if (line_end == 0)
|
||||
goto fail; /* bad file / line too long */
|
||||
|
||||
/* get key/val (ignores lead/trail spaces, stops at space/comment/separator) */
|
||||
read_streamfile((uint8_t*)line,line_start,line_end, streamText);
|
||||
line[line_end - line_start + 1] = '\0';
|
||||
ok = sscanf(line, " %[^ \t#=] = %[^ \t#\r\n] ", key,val);
|
||||
//VGM_LOG("TXTH: ok=%i, key=\"%s\", val=\"%s\" from 0x%lx to 0x%lx\n", ok, key, val, line_start, line_end);
|
||||
|
||||
if (ok != 2) /* ignore line if no key=val (comment or garbage) */
|
||||
continue;
|
||||
|
||||
if (!parse_keyval(streamFile, streamText, txth, key, val)) /* read key/val */
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!txth->loop_flag_set)
|
||||
txth->loop_flag = txth->loop_end_sample && txth->loop_end_sample != 0xFFFFFFFF;
|
||||
|
||||
return 1;
|
||||
fail:
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_keyval(STREAMFILE * streamFile, STREAMFILE * streamText, txth_header * txth, const char * key, const char * val) {
|
||||
|
||||
if (0==strcmp(key,"codec")) {
|
||||
if (0==strcmp(val,"PSX")) txth->codec = PSX;
|
||||
else if (0==strcmp(val,"XBOX")) txth->codec = XBOX;
|
||||
else if (0==strcmp(val,"NGC_DTK")) txth->codec = NGC_DTK;
|
||||
else if (0==strcmp(val,"PCM16BE")) txth->codec = PCM16BE;
|
||||
else if (0==strcmp(val,"PCM16LE")) txth->codec = PCM16LE;
|
||||
else if (0==strcmp(val,"PCM8")) txth->codec = PCM8;
|
||||
else if (0==strcmp(val,"SDX2")) txth->codec = SDX2;
|
||||
else if (0==strcmp(val,"DVI_IMA")) txth->codec = DVI_IMA;
|
||||
else if (0==strcmp(val,"MPEG")) txth->codec = MPEG;
|
||||
else if (0==strcmp(val,"IMA")) txth->codec = IMA;
|
||||
else if (0==strcmp(val,"AICA")) txth->codec = AICA;
|
||||
else if (0==strcmp(val,"MSADPCM")) txth->codec = MSADPCM;
|
||||
else if (0==strcmp(val,"NGC_DSP")) txth->codec = NGC_DSP;
|
||||
else if (0==strcmp(val,"PCM8_U_int")) txth->codec = PCM8_U_int;
|
||||
else if (0==strcmp(val,"PSX_bf")) txth->codec = PSX_bf;
|
||||
else if (0==strcmp(val,"MS_IMA")) txth->codec = MS_IMA;
|
||||
else if (0==strcmp(val,"PCM8_U")) txth->codec = PCM8_U;
|
||||
else if (0==strcmp(val,"APPLE_IMA4")) txth->codec = APPLE_IMA4;
|
||||
else if (0==strcmp(val,"ATRAC3")) txth->codec = ATRAC3;
|
||||
else if (0==strcmp(val,"ATRAC3PLUS")) txth->codec = ATRAC3PLUS;
|
||||
else if (0==strcmp(val,"XMA1")) txth->codec = XMA1;
|
||||
else if (0==strcmp(val,"XMA2")) txth->codec = XMA2;
|
||||
else if (0==strcmp(val,"FFMPEG")) txth->codec = FFMPEG;
|
||||
else goto fail;
|
||||
}
|
||||
else if (0==strcmp(key,"codec_mode")) {
|
||||
if (!parse_num(streamFile,val, &txth->codec_mode)) goto fail;
|
||||
}
|
||||
else if (0==strcmp(key,"interleave")) {
|
||||
if (!parse_num(streamFile,val, &txth->interleave)) goto fail;
|
||||
}
|
||||
else if (0==strcmp(key,"id_value")) {
|
||||
if (!parse_num(streamFile,val, &txth->id_value)) goto fail;
|
||||
}
|
||||
else if (0==strcmp(key,"id_offset")) {
|
||||
if (!parse_num(streamFile,val, &txth->id_offset)) goto fail;
|
||||
if (txth->id_value != txth->id_offset) /* evaluate current ID */
|
||||
goto fail;
|
||||
}
|
||||
else if (0==strcmp(key,"channels")) {
|
||||
if (!parse_num(streamFile,val, &txth->channels)) goto fail;
|
||||
}
|
||||
else if (0==strcmp(key,"sample_rate")) {
|
||||
if (!parse_num(streamFile,val, &txth->sample_rate)) goto fail;
|
||||
}
|
||||
else if (0==strcmp(key,"start_offset")) {
|
||||
if (!parse_num(streamFile,val, &txth->start_offset)) goto fail;
|
||||
if (!txth->data_size_set)
|
||||
txth->data_size = get_streamfile_size(streamFile) - txth->start_offset; /* re-evaluate */
|
||||
}
|
||||
else if (0==strcmp(key,"data_size")) {
|
||||
if (!parse_num(streamFile,val, &txth->data_size)) goto fail;
|
||||
txth->data_size_set = 1;
|
||||
}
|
||||
else if (0==strcmp(key,"sample_type")) {
|
||||
if (0==strcmp(val,"bytes")) txth->sample_type_bytes = 1;
|
||||
else if (0==strcmp(val,"samples")) txth->sample_type_bytes = 0;
|
||||
else goto fail;
|
||||
}
|
||||
else if (0==strcmp(key,"num_samples")) {
|
||||
if (0==strcmp(val,"data_size")) {
|
||||
txth->num_samples = get_bytes_to_samples(txth, txth->data_size);
|
||||
}
|
||||
else {
|
||||
if (!parse_num(streamFile,val, &txth->num_samples)) goto fail;
|
||||
if (txth->sample_type_bytes)
|
||||
txth->num_samples = get_bytes_to_samples(txth, txth->num_samples);
|
||||
}
|
||||
}
|
||||
else if (0==strcmp(key,"loop_start_sample")) {
|
||||
if (!parse_num(streamFile,val, &txth->loop_start_sample)) goto fail;
|
||||
if (txth->sample_type_bytes)
|
||||
txth->loop_start_sample = get_bytes_to_samples(txth, txth->loop_start_sample);
|
||||
if (txth->loop_adjust)
|
||||
txth->loop_start_sample += txth->loop_adjust;
|
||||
}
|
||||
else if (0==strcmp(key,"loop_end_sample")) {
|
||||
if (0==strcmp(val,"data_size")) {
|
||||
txth->loop_end_sample = get_bytes_to_samples(txth, txth->data_size);
|
||||
}
|
||||
else {
|
||||
if (!parse_num(streamFile,val, &txth->loop_end_sample)) goto fail;
|
||||
if (txth->sample_type_bytes)
|
||||
txth->loop_end_sample = get_bytes_to_samples(txth, txth->loop_end_sample);
|
||||
}
|
||||
if (txth->loop_adjust)
|
||||
txth->loop_end_sample += txth->loop_adjust;
|
||||
}
|
||||
else if (0==strcmp(key,"skip_samples")) {
|
||||
if (!parse_num(streamFile,val, &txth->skip_samples)) goto fail;
|
||||
txth->skip_samples_set = 1;
|
||||
if (txth->sample_type_bytes)
|
||||
txth->skip_samples = get_bytes_to_samples(txth, txth->skip_samples);
|
||||
}
|
||||
else if (0==strcmp(key,"loop_adjust")) {
|
||||
if (!parse_num(streamFile,val, &txth->loop_adjust)) goto fail;
|
||||
if (txth->sample_type_bytes)
|
||||
txth->loop_adjust = get_bytes_to_samples(txth, txth->loop_adjust);
|
||||
}
|
||||
else if (0==strcmp(key,"loop_flag")) {
|
||||
if (!parse_num(streamFile,val, &txth->loop_flag)) goto fail;
|
||||
txth->loop_flag_set = 1;
|
||||
}
|
||||
else if (0==strcmp(key,"coef_offset")) {
|
||||
if (!parse_num(streamFile,val, &txth->coef_offset)) goto fail;
|
||||
}
|
||||
else if (0==strcmp(key,"coef_spacing")) {
|
||||
if (!parse_num(streamFile,val, &txth->coef_spacing)) goto fail;
|
||||
}
|
||||
else if (0==strcmp(key,"coef_endianness")) {
|
||||
if (val[0]=='B' && val[1]=='E')
|
||||
txth->coef_big_endian = 1;
|
||||
else if (val[0]=='L' && val[1]=='E')
|
||||
txth->coef_big_endian = 0;
|
||||
else if (!parse_num(streamFile,val, &txth->coef_big_endian)) goto fail;
|
||||
}
|
||||
else if (0==strcmp(key,"coef_mode")) {
|
||||
if (!parse_num(streamFile,val, &txth->coef_mode)) goto fail;
|
||||
}
|
||||
else {
|
||||
VGM_LOG("TXTH: unknown key=%s, val=%s\n", key,val);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
return 1;
|
||||
fail:
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_num(STREAMFILE * streamFile, const char * val, uint32_t * out_value) {
|
||||
|
||||
if (val[0] == '@') { /* offset */
|
||||
uint32_t off = 0;
|
||||
char ed1 = 'L', ed2 = 'E';
|
||||
int size = 4;
|
||||
int big_endian = 0;
|
||||
int hex = (val[1]=='0' && val[2]=='x');
|
||||
|
||||
/* read exactly N fields in the expected format */
|
||||
if (strchr(val,':') && strchr(val,'$')) {
|
||||
if (sscanf(val, hex ? "@%x:%c%c$%i" : "@%u:%c%c$%i", &off, &ed1,&ed2, &size) != 4) goto fail;
|
||||
} else if (strchr(val,':')) {
|
||||
if (sscanf(val, hex ? "@%x:%c%c" : "@%u:%c%c", &off, &ed1,&ed2) != 3) goto fail;
|
||||
} else if (strchr(val,'$')) {
|
||||
if (sscanf(val, hex ? "@%x$%i" : "@%u$%i", &off, &size) != 2) goto fail;
|
||||
} else {
|
||||
if (sscanf(val, hex ? "@%x" : "@%u", &off) != 1) goto fail;
|
||||
}
|
||||
|
||||
if (off < 0 || off > get_streamfile_size(streamFile))
|
||||
goto fail;
|
||||
|
||||
if (ed1 == 'B' && ed2 == 'E')
|
||||
big_endian = 1;
|
||||
else if (!(ed1 == 'L' && ed2 == 'E'))
|
||||
goto fail;
|
||||
|
||||
switch(size) {
|
||||
case 1: *out_value = read_8bit(off,streamFile); break;
|
||||
case 2: *out_value = big_endian ? (uint16_t)read_16bitBE(off,streamFile) : (uint16_t)read_16bitLE(off,streamFile); break;
|
||||
case 3: *out_value = (big_endian ? (uint32_t)read_32bitBE(off,streamFile) : (uint32_t)read_32bitLE(off,streamFile)) & 0x00FFFFFF; break;
|
||||
case 4: *out_value = big_endian ? (uint32_t)read_32bitBE(off,streamFile) : (uint32_t)read_32bitLE(off,streamFile); break;
|
||||
default: goto fail;
|
||||
}
|
||||
}
|
||||
else { /* constant */
|
||||
int hex = (val[0]=='0' && val[1]=='x');
|
||||
|
||||
if (sscanf(val, hex ? "%x" : "%u", out_value)!=1) goto fail;
|
||||
}
|
||||
|
||||
//VGM_LOG("TXTH: value=%s, read %u\n", val, *out_value);
|
||||
return 1;
|
||||
fail:
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_bytes_to_samples(txth_header * txth, uint32_t bytes) {
|
||||
switch(txth->codec) {
|
||||
case MS_IMA:
|
||||
return ms_ima_bytes_to_samples(bytes, txth->interleave, txth->channels);
|
||||
case XBOX:
|
||||
return ms_ima_bytes_to_samples(bytes, 0x24 * txth->channels, txth->channels);
|
||||
case NGC_DSP:
|
||||
return dsp_bytes_to_samples(bytes, txth->channels);
|
||||
case PSX:
|
||||
case PSX_bf:
|
||||
return ps_bytes_to_samples(bytes, txth->channels);
|
||||
case PCM16BE:
|
||||
case PCM16LE:
|
||||
return pcm_bytes_to_samples(bytes, txth->channels, 16);
|
||||
case PCM8:
|
||||
case PCM8_U_int:
|
||||
case PCM8_U:
|
||||
return pcm_bytes_to_samples(bytes, txth->channels, 8);
|
||||
case MSADPCM:
|
||||
return msadpcm_bytes_to_samples(bytes, txth->interleave, txth->channels);
|
||||
case ATRAC3:
|
||||
return atrac3_bytes_to_samples(bytes, txth->interleave);
|
||||
case ATRAC3PLUS:
|
||||
return atrac3plus_bytes_to_samples(bytes, txth->interleave);
|
||||
|
||||
/* XMA bytes-to-samples is done at the end as the value meanings are a bit different */
|
||||
case XMA1:
|
||||
case XMA2:
|
||||
return bytes; /* preserve */
|
||||
|
||||
/* untested */
|
||||
case IMA:
|
||||
case DVI_IMA:
|
||||
case SDX2:
|
||||
return bytes;
|
||||
case AICA:
|
||||
return bytes * 2 / txth->channels;
|
||||
case NGC_DTK:
|
||||
return bytes / 32 * 28; /* always stereo? */
|
||||
case APPLE_IMA4:
|
||||
return (bytes / txth->interleave) * (txth->interleave - 2) * 2;
|
||||
|
||||
case MPEG: /* a bit complex */
|
||||
case FFMPEG: /* too complex */
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
@ -8,6 +8,7 @@ VGMSTREAM * init_vgmstream_xma(STREAMFILE *streamFile) {
|
||||
size_t data_size, chunk_size;
|
||||
int loop_flag, channel_count, sample_rate, is_xma2_old = 0, is_xma1 = 0;
|
||||
int num_samples, loop_start_sample, loop_end_sample, loop_start_b = 0, loop_end_b = 0, loop_subframe = 0;
|
||||
int fmt_be = 0;
|
||||
|
||||
|
||||
/* check extension, case insensitive */
|
||||
@ -34,13 +35,24 @@ VGMSTREAM * init_vgmstream_xma(STREAMFILE *streamFile) {
|
||||
}
|
||||
else if ( find_chunk_le(streamFile, 0x666d7420,first_offset,0, &chunk_offset,&chunk_size)) {
|
||||
int format = read_16bitLE(chunk_offset,streamFile);
|
||||
|
||||
/* some Fable Heroes and Fable 3 XMA have a BE fmt chunk, but the rest of the file is still LE
|
||||
* (incidentally they come with a "frsk" chunk) */
|
||||
if (format == 0x6601) { /* new XMA2 but BE */
|
||||
fmt_be = 1;
|
||||
format = 0x0166;
|
||||
}
|
||||
|
||||
if (format == 0x165) { /* XMA1 */
|
||||
is_xma1 = 1;
|
||||
xma1_parse_fmt_chunk(streamFile, chunk_offset, &channel_count,&sample_rate, &loop_flag, &loop_start_b, &loop_end_b, &loop_subframe, 0);
|
||||
xma1_parse_fmt_chunk(streamFile, chunk_offset, &channel_count,&sample_rate, &loop_flag, &loop_start_b, &loop_end_b, &loop_subframe, fmt_be);
|
||||
} else if (format == 0x166) { /* new XMA2 */
|
||||
channel_count = read_16bitLE(chunk_offset+0x02,streamFile);
|
||||
sample_rate = read_32bitLE(chunk_offset+0x04,streamFile);
|
||||
xma2_parse_fmt_chunk_extra(streamFile, chunk_offset, &loop_flag, &num_samples, &loop_start_sample, &loop_end_sample, 0);
|
||||
int32_t (*read_32bit)(off_t,STREAMFILE*) = fmt_be ? read_32bitBE : read_32bitLE;
|
||||
int16_t (*read_16bit)(off_t,STREAMFILE*) = fmt_be ? read_16bitBE : read_16bitLE;
|
||||
|
||||
channel_count = read_16bit(chunk_offset+0x02,streamFile);
|
||||
sample_rate = read_32bit(chunk_offset+0x04,streamFile);
|
||||
xma2_parse_fmt_chunk_extra(streamFile, chunk_offset, &loop_flag, &num_samples, &loop_start_sample, &loop_end_sample, fmt_be);
|
||||
} else {
|
||||
goto fail;
|
||||
}
|
||||
@ -99,7 +111,7 @@ VGMSTREAM * init_vgmstream_xma(STREAMFILE *streamFile) {
|
||||
if (is_xma2_old) {
|
||||
bytes = ffmpeg_make_riff_xma2_from_xma2_chunk(buf,0x100, chunk_offset,chunk_size, data_size, streamFile);
|
||||
} else {
|
||||
bytes = ffmpeg_make_riff_xma_from_fmt_chunk(buf,0x100, chunk_offset,chunk_size, data_size, streamFile, 0);
|
||||
bytes = ffmpeg_make_riff_xma_from_fmt_chunk(buf,0x100, chunk_offset,chunk_size, data_size, streamFile, fmt_be);
|
||||
}
|
||||
if (bytes <= 0) goto fail;
|
||||
|
||||
|
@ -553,3 +553,29 @@ int find_chunk(STREAMFILE *streamFile, uint32_t chunk_id, off_t start_offset, in
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int get_streamfile_name(STREAMFILE *streamFile, char * buffer, size_t size) {
|
||||
streamFile->get_name(streamFile,buffer,size);
|
||||
return 1;
|
||||
}
|
||||
int get_streamfile_path(STREAMFILE *streamFile, char * buffer, size_t size) {
|
||||
const char *path;
|
||||
|
||||
streamFile->get_name(streamFile,buffer,size);
|
||||
|
||||
path = strrchr(buffer,DIR_SEPARATOR);
|
||||
if (path!=NULL) path = path+1; /* includes "/" */
|
||||
|
||||
if (path) {
|
||||
buffer[path - buffer] = '\0';
|
||||
} else {
|
||||
buffer[0] = '\0';
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
int get_streamfile_ext(STREAMFILE *streamFile, char * filename, size_t size) {
|
||||
streamFile->get_name(streamFile,filename,size);
|
||||
strcpy(filename, filename_extension(filename));
|
||||
return 1;
|
||||
}
|
||||
|
@ -159,4 +159,8 @@ int check_extensions(STREAMFILE *streamFile, const char * cmp_exts);
|
||||
int find_chunk_be(STREAMFILE *streamFile, uint32_t chunk_id, off_t start_offset, int full_chunk_size, off_t *out_chunk_offset, size_t *out_chunk_size);
|
||||
int find_chunk_le(STREAMFILE *streamFile, uint32_t chunk_id, off_t start_offset, int full_chunk_size, off_t *out_chunk_offset, size_t *out_chunk_size);
|
||||
int find_chunk(STREAMFILE *streamFile, uint32_t chunk_id, off_t start_offset, int full_chunk_size, off_t *out_chunk_offset, size_t *out_chunk_size, int size_big_endian, int zero_size_end);
|
||||
|
||||
int get_streamfile_name(STREAMFILE *streamFile, char * buffer, size_t size);
|
||||
int get_streamfile_path(STREAMFILE *streamFile, char * buffer, size_t size);
|
||||
int get_streamfile_ext(STREAMFILE *streamFile, char * filename, size_t size);
|
||||
#endif
|
||||
|
@ -347,6 +347,10 @@ VGMSTREAM * (*init_vgmstream_fcns[])(STREAMFILE *streamFile) = {
|
||||
init_vgmstream_dsp_adx,
|
||||
init_vgmstream_akb_multi,
|
||||
init_vgmstream_akb2_multi,
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
init_vgmstream_mp4_aac_ffmpeg,
|
||||
init_vgmstream_bik,
|
||||
#endif
|
||||
init_vgmstream_x360_ast,
|
||||
init_vgmstream_wwise,
|
||||
init_vgmstream_ubi_raki,
|
||||
@ -365,10 +369,8 @@ VGMSTREAM * (*init_vgmstream_fcns[])(STREAMFILE *streamFile) = {
|
||||
init_vgmstream_pc_xa30,
|
||||
init_vgmstream_wii_04sw,
|
||||
|
||||
init_vgmstream_txth, /* should go at the end (lower priority) */
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
init_vgmstream_mp4_aac_ffmpeg,
|
||||
init_vgmstream_bik,
|
||||
|
||||
init_vgmstream_ffmpeg, /* should go at the end */
|
||||
#endif
|
||||
};
|
||||
|
@ -619,6 +619,7 @@ typedef enum {
|
||||
meta_NGC_ULW, /* Burnout 1 (GC only) */
|
||||
meta_PC_XA30, /* Driver - Parallel Lines (PC) */
|
||||
meta_WII_04SW, /* Driver - Parallel Lines (Wii) */
|
||||
meta_TXTH, /* generic text header */
|
||||
|
||||
#ifdef VGM_USE_VORBIS
|
||||
meta_OGG_VORBIS, /* Ogg Vorbis */
|
||||
|
Loading…
Reference in New Issue
Block a user