mirror of
https://github.com/vgmstream/vgmstream.git
synced 2024-11-12 01:30:49 +01:00
Added support for Xenoblade Chronicles 3D adx
This commit is contained in:
parent
4aec20fe21
commit
51dfc3339a
@ -16,7 +16,7 @@ VGMSTREAM * init_vgmstream_adx(STREAMFILE *streamFile) {
|
||||
off_t stream_offset;
|
||||
uint16_t version_signature;
|
||||
int loop_flag=0;
|
||||
int channel_count;
|
||||
int channel_count, i, j, channel_header_spacing;
|
||||
int32_t loop_start_sample=0;
|
||||
int32_t loop_end_sample=0;
|
||||
meta_t header_type;
|
||||
@ -25,160 +25,197 @@ VGMSTREAM * init_vgmstream_adx(STREAMFILE *streamFile) {
|
||||
char filename[PATH_LIMIT];
|
||||
int coding_type = coding_CRI_ADX;
|
||||
uint16_t xor_start=0,xor_mult=0,xor_add=0;
|
||||
|
||||
/* Xenoblade Chronicles 3D uses an adx extension as with
|
||||
the Wii version, but it's actually DSP ADPCM. Adding
|
||||
this flag to account for it. */
|
||||
int xb3d_flag = 0;
|
||||
|
||||
/* check extension, case insensitive */
|
||||
streamFile->get_name(streamFile,filename,sizeof(filename));
|
||||
if (strcasecmp("adx",filename_extension(filename))) goto fail;
|
||||
|
||||
/* check first 2 bytes */
|
||||
if ((uint16_t)read_16bitBE(0,streamFile)!=0x8000) goto fail;
|
||||
if ((uint16_t)read_16bitBE(0,streamFile)!=0x8000) {
|
||||
if (read_8bit(0,streamFile)!=2)goto fail;
|
||||
else {xb3d_flag = 1; coding_type = coding_NGC_DSP;}
|
||||
}
|
||||
|
||||
if (xb3d_flag) {
|
||||
channel_count = read_32bitLE(0, streamFile);
|
||||
loop_flag = read_16bitLE(0x6e, streamFile);
|
||||
channel_header_spacing = 0x34;
|
||||
}
|
||||
else {
|
||||
/* get stream offset, check for CRI signature just before */
|
||||
stream_offset = (uint16_t)read_16bitBE(2,streamFile) + 4;
|
||||
if ((uint16_t)read_16bitBE(stream_offset-6,streamFile)!=0x2863 ||/* "(c" */
|
||||
(uint32_t)read_32bitBE(stream_offset-4,streamFile)!=0x29435249 /* ")CRI" */
|
||||
) goto fail;
|
||||
|
||||
/* get stream offset, check for CRI signature just before */
|
||||
stream_offset = (uint16_t)read_16bitBE(2,streamFile) + 4;
|
||||
if ((uint16_t)read_16bitBE(stream_offset-6,streamFile)!=0x2863 ||/* "(c" */
|
||||
(uint32_t)read_32bitBE(stream_offset-4,streamFile)!=0x29435249 /* ")CRI" */
|
||||
) goto fail;
|
||||
/* check for encoding type */
|
||||
/* 2 is for some unknown fixed filter, 3 is standard ADX, 4 is
|
||||
* ADX with exponential scale, 0x11 is AHX */
|
||||
if (read_8bit(4,streamFile) != 3) goto fail;
|
||||
|
||||
/* check for encoding type */
|
||||
/* 2 is for some unknown fixed filter, 3 is standard ADX, 4 is
|
||||
* ADX with exponential scale, 0x11 is AHX */
|
||||
if (read_8bit(4,streamFile) != 3) goto fail;
|
||||
/* check for frame size (only 18 is supported at the moment) */
|
||||
if (read_8bit(5,streamFile) != 18) goto fail;
|
||||
|
||||
/* check for frame size (only 18 is supported at the moment) */
|
||||
if (read_8bit(5,streamFile) != 18) goto fail;
|
||||
/* check for bits per sample? (only 4 makes sense for ADX) */
|
||||
if (read_8bit(6,streamFile) != 4) goto fail;
|
||||
|
||||
/* check for bits per sample? (only 4 makes sense for ADX) */
|
||||
if (read_8bit(6,streamFile) != 4) goto fail;
|
||||
/* check version signature, read loop info */
|
||||
version_signature = read_16bitBE(0x12,streamFile);
|
||||
/* encryption */
|
||||
if (version_signature == 0x0408) {
|
||||
if (find_key(streamFile, 8, &xor_start, &xor_mult, &xor_add))
|
||||
{
|
||||
coding_type = coding_CRI_ADX_enc_8;
|
||||
version_signature = 0x0400;
|
||||
}
|
||||
}
|
||||
else if (version_signature == 0x0409) {
|
||||
if (find_key(streamFile, 9, &xor_start, &xor_mult, &xor_add))
|
||||
{
|
||||
coding_type = coding_CRI_ADX_enc_9;
|
||||
version_signature = 0x0400;
|
||||
}
|
||||
}
|
||||
|
||||
/* check version signature, read loop info */
|
||||
version_signature = read_16bitBE(0x12,streamFile);
|
||||
/* encryption */
|
||||
if (version_signature == 0x0408) {
|
||||
if (find_key(streamFile, 8, &xor_start, &xor_mult, &xor_add))
|
||||
{
|
||||
coding_type = coding_CRI_ADX_enc_8;
|
||||
version_signature = 0x0400;
|
||||
}
|
||||
}
|
||||
else if (version_signature == 0x0409) {
|
||||
if (find_key(streamFile, 9, &xor_start, &xor_mult, &xor_add))
|
||||
{
|
||||
coding_type = coding_CRI_ADX_enc_9;
|
||||
version_signature = 0x0400;
|
||||
}
|
||||
}
|
||||
if (version_signature == 0x0300) { /* type 03 */
|
||||
header_type = meta_ADX_03;
|
||||
if (stream_offset-6 >= 0x2c) { /* enough space for loop info? */
|
||||
loop_flag = (read_32bitBE(0x18,streamFile) != 0);
|
||||
loop_start_sample = read_32bitBE(0x1c,streamFile);
|
||||
//loop_start_offset = read_32bitBE(0x20,streamFile);
|
||||
loop_end_sample = read_32bitBE(0x24,streamFile);
|
||||
//loop_end_offset = read_32bitBE(0x28,streamFile);
|
||||
}
|
||||
} else if (version_signature == 0x0400) {
|
||||
|
||||
if (version_signature == 0x0300) { /* type 03 */
|
||||
header_type = meta_ADX_03;
|
||||
if (stream_offset-6 >= 0x2c) { /* enough space for loop info? */
|
||||
loop_flag = (read_32bitBE(0x18,streamFile) != 0);
|
||||
loop_start_sample = read_32bitBE(0x1c,streamFile);
|
||||
//loop_start_offset = read_32bitBE(0x20,streamFile);
|
||||
loop_end_sample = read_32bitBE(0x24,streamFile);
|
||||
//loop_end_offset = read_32bitBE(0x28,streamFile);
|
||||
}
|
||||
} else if (version_signature == 0x0400) {
|
||||
off_t ainf_info_length=0;
|
||||
|
||||
off_t ainf_info_length=0;
|
||||
if((uint32_t)read_32bitBE(0x24,streamFile)==0x41494E46) /* AINF Header */
|
||||
ainf_info_length = (off_t)read_32bitBE(0x28,streamFile);
|
||||
|
||||
if((uint32_t)read_32bitBE(0x24,streamFile)==0x41494E46) /* AINF Header */
|
||||
ainf_info_length = (off_t)read_32bitBE(0x28,streamFile);
|
||||
header_type = meta_ADX_04;
|
||||
if (stream_offset-ainf_info_length-6 >= 0x38) { /* enough space for loop info? */
|
||||
if (read_32bitBE(0x24,streamFile) == 0xFFFEFFFE)
|
||||
loop_flag = 0;
|
||||
else
|
||||
loop_flag = (read_32bitBE(0x24,streamFile) != 0);
|
||||
|
||||
header_type = meta_ADX_04;
|
||||
if (stream_offset-ainf_info_length-6 >= 0x38) { /* enough space for loop info? */
|
||||
if (read_32bitBE(0x24,streamFile) == 0xFFFEFFFE)
|
||||
loop_start_sample = read_32bitBE(0x28,streamFile);
|
||||
//loop_start_offset = read_32bitBE(0x2c,streamFile);
|
||||
loop_end_sample = read_32bitBE(0x30,streamFile);
|
||||
//loop_end_offset = read_32bitBE(0x34,streamFile);
|
||||
}
|
||||
} else if (version_signature == 0x0500) { /* found in some SFD : Buggy Heat, appears to have no loop */
|
||||
header_type = meta_ADX_05;
|
||||
} else goto fail; /* not a known/supported version signature */
|
||||
|
||||
/* At this point we almost certainly have an ADX file,
|
||||
* so let's build the VGMSTREAM. */
|
||||
|
||||
/* high-pass cutoff frequency, always 500 that I've seen */
|
||||
cutoff = (uint16_t)read_16bitBE(0x10,streamFile);
|
||||
|
||||
if (loop_start_sample == 0 && loop_end_sample == 0) {
|
||||
loop_flag = 0;
|
||||
else
|
||||
loop_flag = (read_32bitBE(0x24,streamFile) != 0);
|
||||
}
|
||||
|
||||
loop_start_sample = read_32bitBE(0x28,streamFile);
|
||||
//loop_start_offset = read_32bitBE(0x2c,streamFile);
|
||||
loop_end_sample = read_32bitBE(0x30,streamFile);
|
||||
//loop_end_offset = read_32bitBE(0x34,streamFile);
|
||||
}
|
||||
} else if (version_signature == 0x0500) { /* found in some SFD : Buggy Heat, appears to have no loop */
|
||||
header_type = meta_ADX_05;
|
||||
} else goto fail; /* not a known/supported version signature */
|
||||
|
||||
/* At this point we almost certainly have an ADX file,
|
||||
* so let's build the VGMSTREAM. */
|
||||
|
||||
/* high-pass cutoff frequency, always 500 that I've seen */
|
||||
cutoff = (uint16_t)read_16bitBE(0x10,streamFile);
|
||||
|
||||
if (loop_start_sample == 0 && loop_end_sample == 0) {
|
||||
loop_flag = 0;
|
||||
}
|
||||
|
||||
channel_count = read_8bit(7,streamFile);
|
||||
channel_count = read_8bit(7,streamFile);
|
||||
}
|
||||
vgmstream = allocate_vgmstream(channel_count,loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
/* fill in the vital statistics */
|
||||
vgmstream->num_samples = read_32bitBE(0xc,streamFile);
|
||||
vgmstream->sample_rate = read_32bitBE(8,streamFile);
|
||||
/* channels and loop flag are set by allocate_vgmstream */
|
||||
vgmstream->loop_start_sample = loop_start_sample;
|
||||
vgmstream->loop_end_sample = loop_end_sample;
|
||||
|
||||
vgmstream->coding_type = coding_type;
|
||||
if (channel_count==1)
|
||||
vgmstream->layout_type = layout_none;
|
||||
else
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->meta_type = header_type;
|
||||
|
||||
vgmstream->interleave_block_size=18;
|
||||
|
||||
/* calculate filter coefficients */
|
||||
{
|
||||
double x,y,z,a,b,c;
|
||||
|
||||
x = cutoff;
|
||||
y = vgmstream->sample_rate;
|
||||
z = cos(2.0*M_PI*x/y);
|
||||
|
||||
a = M_SQRT2-z;
|
||||
b = M_SQRT2-1.0;
|
||||
c = (a-sqrt((a+b)*(a-b)))/b;
|
||||
|
||||
coef1 = floor(c*8192);
|
||||
coef2 = floor(c*c*-4096);
|
||||
}
|
||||
|
||||
{
|
||||
int i;
|
||||
STREAMFILE * chstreamfile;
|
||||
|
||||
/* ADX is so tightly interleaved that having two buffers is silly */
|
||||
chstreamfile = streamFile->open(streamFile,filename,18*0x400);
|
||||
if (!chstreamfile) goto fail;
|
||||
|
||||
for (i=0;i<channel_count;i++) {
|
||||
vgmstream->ch[i].streamfile = chstreamfile;
|
||||
|
||||
vgmstream->ch[i].channel_start_offset=
|
||||
vgmstream->ch[i].offset=
|
||||
stream_offset+18*i;
|
||||
|
||||
vgmstream->ch[i].adpcm_coef[0] = coef1;
|
||||
vgmstream->ch[i].adpcm_coef[1] = coef2;
|
||||
|
||||
if (coding_type == coding_CRI_ADX_enc_8 ||
|
||||
coding_type == coding_CRI_ADX_enc_9)
|
||||
{
|
||||
int j;
|
||||
vgmstream->ch[i].adx_channels = channel_count;
|
||||
vgmstream->ch[i].adx_xor = xor_start;
|
||||
vgmstream->ch[i].adx_mult = xor_mult;
|
||||
vgmstream->ch[i].adx_add = xor_add;
|
||||
|
||||
for (j=0;j<i;j++)
|
||||
adx_next_key(&vgmstream->ch[i]);
|
||||
|
||||
if (xb3d_flag) {
|
||||
for (j=0;j<vgmstream->channels;j++) {
|
||||
for (i=0;i<16;i++) {
|
||||
vgmstream->ch[j].adpcm_coef[i]=read_16bitLE(4+j*channel_header_spacing+i*2,streamFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
vgmstream->layout_type = layout_none;
|
||||
vgmstream->coding_type = coding_type;
|
||||
vgmstream->meta_type = meta_XB3D_ADX;
|
||||
vgmstream->sample_rate = read_32bitLE(0x70,streamFile);
|
||||
vgmstream->num_samples = read_32bitLE(0x74, streamFile);
|
||||
vgmstream->loop_start_sample = read_32bitLE(0x78, streamFile);
|
||||
vgmstream->loop_end_sample = read_32bitLE(0x7c, streamFile);
|
||||
|
||||
for (i = 0; i<channel_count; i++) {
|
||||
vgmstream->ch[i].streamfile = streamFile->open(streamFile, filename, 0x1000);
|
||||
vgmstream->ch[i].channel_start_offset = vgmstream->ch[i].offset
|
||||
= read_32bitLE(0x34+i*channel_header_spacing, streamFile);
|
||||
if (!vgmstream->ch[i].streamfile) goto fail;
|
||||
}
|
||||
|
||||
}
|
||||
else {
|
||||
vgmstream->num_samples = read_32bitBE(0xc,streamFile);
|
||||
vgmstream->sample_rate = read_32bitBE(8,streamFile);
|
||||
/* channels and loop flag are set by allocate_vgmstream */
|
||||
vgmstream->loop_start_sample = loop_start_sample;
|
||||
vgmstream->loop_end_sample = loop_end_sample;
|
||||
|
||||
vgmstream->coding_type = coding_type;
|
||||
if (channel_count==1)
|
||||
vgmstream->layout_type = layout_none;
|
||||
else
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->meta_type = header_type;
|
||||
|
||||
vgmstream->interleave_block_size=18;
|
||||
/* calculate filter coefficients */
|
||||
{
|
||||
double x,y,z,a,b,c;
|
||||
|
||||
x = cutoff;
|
||||
y = vgmstream->sample_rate;
|
||||
z = cos(2.0*M_PI*x/y);
|
||||
|
||||
a = M_SQRT2-z;
|
||||
b = M_SQRT2-1.0;
|
||||
c = (a-sqrt((a+b)*(a-b)))/b;
|
||||
|
||||
coef1 = floor(c*8192);
|
||||
coef2 = floor(c*c*-4096);
|
||||
}
|
||||
|
||||
{
|
||||
int i;
|
||||
STREAMFILE * chstreamfile;
|
||||
|
||||
/* ADX is so tightly interleaved that having two buffers is silly */
|
||||
chstreamfile = streamFile->open(streamFile,filename,18*0x400);
|
||||
if (!chstreamfile) goto fail;
|
||||
|
||||
for (i=0;i<channel_count;i++) {
|
||||
vgmstream->ch[i].streamfile = chstreamfile;
|
||||
|
||||
vgmstream->ch[i].channel_start_offset=
|
||||
vgmstream->ch[i].offset=
|
||||
stream_offset+18*i;
|
||||
|
||||
vgmstream->ch[i].adpcm_coef[0] = coef1;
|
||||
vgmstream->ch[i].adpcm_coef[1] = coef2;
|
||||
|
||||
if (coding_type == coding_CRI_ADX_enc_8 ||
|
||||
coding_type == coding_CRI_ADX_enc_9)
|
||||
{
|
||||
int j;
|
||||
vgmstream->ch[i].adx_channels = channel_count;
|
||||
vgmstream->ch[i].adx_xor = xor_start;
|
||||
vgmstream->ch[i].adx_mult = xor_mult;
|
||||
vgmstream->ch[i].adx_add = xor_add;
|
||||
|
||||
for (j=0;j<i;j++)
|
||||
adx_next_key(&vgmstream->ch[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return vgmstream;
|
||||
|
||||
/* clean up anything we may have opened */
|
||||
|
@ -3148,6 +3148,9 @@ void describe_vgmstream(VGMSTREAM * vgmstream, char * desc, int length) {
|
||||
case meta_MCA:
|
||||
snprintf(temp,TEMPSIZE,"Capcom MCA Header");
|
||||
break;
|
||||
case meta_XB3D_ADX:
|
||||
snprintf(temp, TEMPSIZE,"Xenoblade 3D ADX Header");
|
||||
break;
|
||||
default:
|
||||
snprintf(temp,TEMPSIZE,"THEY SHOULD HAVE SENT A POET");
|
||||
}
|
||||
|
@ -580,6 +580,7 @@ typedef enum {
|
||||
meta_3DS_IDSP, // Nintendo 3DS IDSP
|
||||
meta_G1L, // Tecmo Koei G1L
|
||||
meta_MCA, // Capcom MCA "MADP"
|
||||
meta_XB3D_ADX, // Xenoblade Chronicles 3D ADX
|
||||
#ifdef VGM_USE_MP4V2
|
||||
meta_MP4,
|
||||
#endif
|
||||
|
@ -1,6 +1,8 @@
|
||||
export SHELL = /bin/sh
|
||||
export CFLAGS=-Wall -O3 -I../ext_includes
|
||||
export LDFLAGS=-L../src -L ../ext_libs -lvgmstream -lvorbisfile -lmpg123-0 -lm
|
||||
export CFLAGS=-Wall -O3 -I../ext_includes -DUSE_ALLOCA -DVGM_USE_MAIATRAC3PLUS
|
||||
# -DVGM_USE_G7221 -DVGM_USE_G719
|
||||
export LDFLAGS=-L../src -L ../ext_libs -lvgmstream -lvorbisfile -lmpg123 -lat3plusdecoder -lpthread -lm
|
||||
#-lg719_decode -lg7221_decode -lsiren_decode
|
||||
export CC=gcc
|
||||
export AR=ar
|
||||
export STRIP=strip
|
||||
@ -8,8 +10,8 @@ export STRIP=strip
|
||||
.PHONY: libvgmstream.a
|
||||
|
||||
test.exe: libvgmstream.a
|
||||
$(CC) $(CFLAGS) $(LDFLAGS) "-DVERSION=\"`../version.sh`\"" test.c $(LDFLAGS) -o test
|
||||
$(STRIP) test
|
||||
$(CC) $(CFLAGS) $(LDFLAGS) "-DVERSION=\"`../version.sh`\"" test.c $(LDFLAGS) -o vgmstream
|
||||
$(STRIP) vgmstream
|
||||
|
||||
libvgmstream.a:
|
||||
$(MAKE) -C ../src $@
|
||||
|
29
test/test.c
29
test/test.c
@ -29,13 +29,12 @@ void usage(const char * name) {
|
||||
" -x: decode and print adxencd command line to encode as ADX\n"
|
||||
" -g: decode and print oggenc command line to encode as OGG\n"
|
||||
" -b: decode and print batch variable commands\n"
|
||||
" -L: append a smpl chunk and create a looping wav\n"
|
||||
" -L: append a smpl chunk and create a looping wav\n"
|
||||
" -e: force end-to-end looping\n"
|
||||
" -E: force end-to-end looping even if file has real loop points\n"
|
||||
" -r outfile2.wav: output a second time after resetting\n"
|
||||
" -2 N: only output the Nth (first is 0) set of stereo channels\n"
|
||||
,name);
|
||||
|
||||
}
|
||||
|
||||
int main(int argc, char ** argv) {
|
||||
@ -109,9 +108,9 @@ int main(int argc, char ** argv) {
|
||||
case 'E':
|
||||
really_force_loop = 1;
|
||||
break;
|
||||
case 'L':
|
||||
lwav = 1;
|
||||
break;
|
||||
case 'L':
|
||||
lwav = 1;
|
||||
break;
|
||||
case 'r':
|
||||
reset_outfilename = optarg;
|
||||
break;
|
||||
@ -298,16 +297,16 @@ int main(int argc, char ** argv) {
|
||||
fwrite(buf,sizeof(sample)*s->channels,toget,outfile);
|
||||
}
|
||||
}
|
||||
|
||||
if (!play && lwav && s->loop_flag) { // Writing smpl chuck
|
||||
make_smpl_chunk((uint8_t*)buf, s->loop_start_sample, s->loop_end_sample);
|
||||
fwrite(buf,1,0x44,outfile);
|
||||
fseek(outfile, 4, SEEK_SET);
|
||||
|
||||
size_t bytecount = len*s->channels*sizeof(sample);
|
||||
put_32bitLE((uint8_t*)buf, (int32_t)(bytecount+0x2c+52));
|
||||
fwrite(buf,1,0x4,outfile);
|
||||
}
|
||||
|
||||
if (!play && lwav && s->loop_flag) { // Writing smpl chuck
|
||||
make_smpl_chunk((uint8_t*)buf, s->loop_start_sample, s->loop_end_sample);
|
||||
fwrite(buf,1,0x44,outfile);
|
||||
fseek(outfile, 4, SEEK_SET);
|
||||
|
||||
size_t bytecount = len*s->channels*sizeof(sample);
|
||||
put_32bitLE((uint8_t*)buf, (int32_t)(bytecount+0x2c+52));
|
||||
fwrite(buf,1,0x4,outfile);
|
||||
}
|
||||
fclose(outfile); outfile = NULL;
|
||||
|
||||
#ifdef PROFILE_STREAMFILE
|
||||
|
@ -35,7 +35,7 @@ libg7221_decode.a:
|
||||
libg719_decode.a:
|
||||
$(MAKE) -C ../ext_libs -f Makefile.mingw $@
|
||||
|
||||
at3plusdecoder.a:
|
||||
libat3plusdecoder.a:
|
||||
$(MAKE) -C ../ext_libs -f Makefile.mingw $@
|
||||
|
||||
clean:
|
||||
|
Loading…
Reference in New Issue
Block a user