XNBm support, share some functionality among RIFF-related formats

git-svn-id: https://vgmstream.svn.sourceforge.net/svnroot/vgmstream@987 51a99a44-fe44-0410-b1ba-c3e57ba2b86b
This commit is contained in:
halleyscometsw 2012-08-20 05:17:52 +00:00
parent 4320444a9d
commit a4561ef328
7 changed files with 368 additions and 127 deletions

View File

@ -520,6 +520,7 @@ bool input_vgmstream::g_is_our_path(const char * p_path,const char * p_extension
if(!stricmp_utf8(p_extension,"xa30")) return 1; if(!stricmp_utf8(p_extension,"xa30")) return 1;
if(!stricmp_utf8(p_extension,"xau")) return 1; if(!stricmp_utf8(p_extension,"xau")) return 1;
if(!stricmp_utf8(p_extension,"xmu")) return 1; if(!stricmp_utf8(p_extension,"xmu")) return 1;
if(!stricmp_utf8(p_extension,"xnb")) return 1;
if(!stricmp_utf8(p_extension,"xsf")) return 1; if(!stricmp_utf8(p_extension,"xsf")) return 1;
if(!stricmp_utf8(p_extension,"xss")) return 1; if(!stricmp_utf8(p_extension,"xss")) return 1;
if(!stricmp_utf8(p_extension,"xvag")) return 1; if(!stricmp_utf8(p_extension,"xvag")) return 1;
@ -832,6 +833,7 @@ DECLARE_MULTIPLE_FILE_TYPE("XA2 Audio File (*.XA2)", xa2);
DECLARE_MULTIPLE_FILE_TYPE("XA30 Audio File (*.XA30)", xa30); DECLARE_MULTIPLE_FILE_TYPE("XA30 Audio File (*.XA30)", xa30);
DECLARE_MULTIPLE_FILE_TYPE("XAU Audio File (*.XAU)", xau); DECLARE_MULTIPLE_FILE_TYPE("XAU Audio File (*.XAU)", xau);
DECLARE_MULTIPLE_FILE_TYPE("XMU Audio File (*.XMU)", xmu); DECLARE_MULTIPLE_FILE_TYPE("XMU Audio File (*.XMU)", xmu);
DECLARE_MULTIPLE_FILE_TYPE("XNB Audio File (*.XNB)", xnb);
DECLARE_MULTIPLE_FILE_TYPE("XSF Audio File (*.XSF)", xsf); DECLARE_MULTIPLE_FILE_TYPE("XSF Audio File (*.XSF)", xsf);
DECLARE_MULTIPLE_FILE_TYPE("XSS Audio File (*.XSS)", xss); DECLARE_MULTIPLE_FILE_TYPE("XSS Audio File (*.XSS)", xss);
DECLARE_MULTIPLE_FILE_TYPE("XVAG Audio File (*.XVAG)", xvag); DECLARE_MULTIPLE_FILE_TYPE("XVAG Audio File (*.XVAG)", xvag);

View File

@ -134,6 +134,8 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_rifx(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_rifx(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_xnbm(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_pos(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_pos(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_nwa(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_nwa(STREAMFILE * streamFile);

View File

@ -81,18 +81,110 @@ void parse_adtl(off_t adtl_offset, off_t adtl_length, STREAMFILE *streamFile,
} }
} }
struct riff_fmt_chunk {
int sample_rate;
int channel_count;
uint32_t block_size;
int coding_type;
int interleave;
};
int read_fmt(int big_endian,
STREAMFILE * streamFile,
off_t current_chunk,
struct riff_fmt_chunk * fmt,
int sns,
int mwv) {
int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL;
int16_t (*read_16bit)(off_t,STREAMFILE*) = NULL;
if (big_endian) {
read_32bit = read_32bitBE;
read_16bit = read_16bitBE;
} else {
read_32bit = read_32bitLE;
read_16bit = read_16bitLE;
}
fmt->sample_rate = read_32bit(current_chunk+0x0c,streamFile);
fmt->channel_count = read_16bit(current_chunk+0x0a,streamFile);
fmt->block_size = read_16bit(current_chunk+0x14,streamFile);
switch ((uint16_t)read_16bit(current_chunk+0x8,streamFile)) {
case 1: /* PCM */
switch (read_16bit(current_chunk+0x16,streamFile)) {
case 16:
if (big_endian) {
fmt->coding_type = coding_PCM16LE;
} else {
fmt->coding_type = coding_PCM16BE;
}
fmt->interleave = 2;
break;
case 8:
fmt->coding_type = coding_PCM8_U_int;
fmt->interleave = 1;
break;
default:
goto fail;
}
break;
case 2: /* MS ADPCM */
/* ensure 4bps */
if (read_16bit(current_chunk+0x16,streamFile)!=4)
goto fail;
fmt->coding_type = coding_MSADPCM;
fmt->interleave = 0;
break;
case 0x11: /* MS IMA ADCM */
/* ensure 4bps */
if (read_16bit(current_chunk+0x16,streamFile)!=4)
goto fail;
fmt->coding_type = coding_MS_IMA;
fmt->interleave = 0;
break;
case 0x69: /* MS IMA ADCM - Rayman Raving Rabbids 2 (PC) */
/* ensure 4bps */
if (read_16bit(current_chunk+0x16,streamFile)!=4)
goto fail;
fmt->coding_type = coding_MS_IMA;
fmt->interleave = 0;
break;
case 0x555: /* Level-5 0x555 ADPCM */
if (!mwv) goto fail;
fmt->coding_type = coding_L5_555;
fmt->interleave = 0x12;
break;
case 0x5050: /* Ubisoft .sns uses this for DSP */
if (!sns) goto fail;
fmt->coding_type = coding_NGC_DSP;
fmt->interleave = 8;
break;
case 0xFFF0: /* */
fmt->coding_type = coding_NGC_DSP;
fmt->interleave = 8;
break;
default:
goto fail;
}
return 0;
fail:
return -1;
}
VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL; VGMSTREAM * vgmstream = NULL;
char filename[260]; char filename[260];
struct riff_fmt_chunk fmt;
off_t file_size = -1; off_t file_size = -1;
int channel_count = 0;
int sample_count = 0; int sample_count = 0;
int fact_sample_count = -1; int fact_sample_count = -1;
int sample_rate = 0;
int coding_type = -1;
off_t start_offset = -1; off_t start_offset = -1;
int interleave = -1;
int loop_flag = 0; int loop_flag = 0;
long loop_start_ms = -1; long loop_start_ms = -1;
@ -101,7 +193,6 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) {
off_t loop_end_offset = -1; off_t loop_end_offset = -1;
uint32_t riff_size; uint32_t riff_size;
uint32_t data_size = 0; uint32_t data_size = 0;
uint32_t block_size = 0;
int FormatChunkFound = 0; int FormatChunkFound = 0;
int DataChunkFound = 0; int DataChunkFound = 0;
@ -156,59 +247,14 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) {
if (FormatChunkFound) goto fail; if (FormatChunkFound) goto fail;
FormatChunkFound = 1; FormatChunkFound = 1;
sample_rate = read_32bitLE(current_chunk+0x0c,streamFile); if (-1 == read_fmt(0, /* big endian == false*/
channel_count = read_16bitLE(current_chunk+0x0a,streamFile); streamFile,
block_size = read_16bitLE(current_chunk+0x14,streamFile); current_chunk,
&fmt,
sns,
mwv))
goto fail;
switch (read_16bitLE(current_chunk+0x8,streamFile)) {
case 1: /* PCM */
switch (read_16bitLE(current_chunk+0x16,streamFile)) {
case 16:
coding_type = coding_PCM16LE;
interleave = 2;
break;
case 8:
coding_type = coding_PCM8_U_int;
interleave = 1;
break;
default:
goto fail;
}
break;
case 2: /* MS ADPCM */
/* ensure 4bps */
if (read_16bitLE(current_chunk+0x16,streamFile)!=4)
goto fail;
coding_type = coding_MSADPCM;
interleave = 0;
break;
case 0x11: /* MS IMA ADCM */
/* ensure 4bps */
if (read_16bitLE(current_chunk+0x16,streamFile)!=4)
goto fail;
coding_type = coding_MS_IMA;
interleave = 0;
break;
case 0x69: /* MS IMA ADCM - Rayman Raving Rabbids 2 (PC) */
/* ensure 4bps */
if (read_16bitLE(current_chunk+0x16,streamFile)!=4)
goto fail;
coding_type = coding_MS_IMA;
interleave = 0;
break;
case 0x555: /* Level-5 0x555 ADPCM */
if (!mwv) goto fail;
coding_type = coding_L5_555;
interleave = 0x12;
break;
case 0x5050: /* Ubisoft .sns uses this for DSP */
if (!sns) goto fail;
coding_type = coding_NGC_DSP;
interleave = 8;
break;
default:
goto fail;
}
break; break;
case 0x64617461: /* data */ case 0x64617461: /* data */
/* at most one per file */ /* at most one per file */
@ -276,23 +322,25 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) {
if (!FormatChunkFound || !DataChunkFound) goto fail; if (!FormatChunkFound || !DataChunkFound) goto fail;
switch (coding_type) { switch (fmt.coding_type) {
case coding_PCM16LE: case coding_PCM16LE:
sample_count = data_size/2/channel_count; sample_count = data_size/2/fmt.channel_count;
break; break;
case coding_PCM8_U_int: case coding_PCM8_U_int:
sample_count = data_size/channel_count; sample_count = data_size/fmt.channel_count;
break; break;
case coding_L5_555: case coding_L5_555:
sample_count = data_size/0x12/channel_count*32; sample_count = data_size/0x12/fmt.channel_count*32;
break; break;
case coding_MSADPCM: case coding_MSADPCM:
sample_count = msadpcm_bytes_to_samples(data_size, block_size, channel_count); sample_count = msadpcm_bytes_to_samples(data_size, fmt.block_size, fmt.channel_count);
break; break;
case coding_MS_IMA: case coding_MS_IMA:
sample_count = (data_size / block_size) * (block_size - 4 * channel_count) * 2 / channel_count + sample_count = (data_size / fmt.block_size) * (fmt.block_size - 4 * fmt.channel_count) * 2 / fmt.channel_count +
((data_size % block_size) ? (data_size % block_size - 4 * channel_count) * 2 / channel_count : 0); ((data_size % fmt.block_size) ? (data_size % fmt.block_size - 4 * fmt.channel_count) * 2 / fmt.channel_count : 0);
break; break;
default:
goto fail;
} }
/* .sns uses fact chunk */ /* .sns uses fact chunk */
@ -304,18 +352,18 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) {
/* build the VGMSTREAM */ /* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag); vgmstream = allocate_vgmstream(fmt.channel_count,loop_flag);
if (!vgmstream) goto fail; if (!vgmstream) goto fail;
/* fill in the vital statistics */ /* fill in the vital statistics */
vgmstream->num_samples = sample_count; vgmstream->num_samples = sample_count;
vgmstream->sample_rate = sample_rate; vgmstream->sample_rate = fmt.sample_rate;
vgmstream->coding_type = coding_type; vgmstream->coding_type = fmt.coding_type;
vgmstream->layout_type = layout_none; vgmstream->layout_type = layout_none;
if (channel_count > 1) { if (fmt.channel_count > 1) {
switch (coding_type) { switch (fmt.coding_type) {
case coding_PCM8_U_int: case coding_PCM8_U_int:
case coding_MS_IMA: case coding_MS_IMA:
case coding_MSADPCM: case coding_MSADPCM:
@ -327,12 +375,12 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) {
} }
} }
vgmstream->interleave_block_size = interleave; vgmstream->interleave_block_size = fmt.interleave;
switch (coding_type) { switch (fmt.coding_type) {
case coding_MSADPCM: case coding_MSADPCM:
case coding_MS_IMA: case coding_MS_IMA:
// override interleave_block_size with frame size // override interleave_block_size with frame size
vgmstream->interleave_block_size = block_size; vgmstream->interleave_block_size = fmt.block_size;
break; break;
default: default:
// use interleave from above // use interleave from above
@ -343,9 +391,9 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) {
if (loop_start_ms >= 0) if (loop_start_ms >= 0)
{ {
vgmstream->loop_start_sample = vgmstream->loop_start_sample =
(long long)loop_start_ms*sample_rate/1000; (long long)loop_start_ms*fmt.sample_rate/1000;
vgmstream->loop_end_sample = vgmstream->loop_end_sample =
(long long)loop_end_ms*sample_rate/1000; (long long)loop_end_ms*fmt.sample_rate/1000;
vgmstream->meta_type = meta_RIFF_WAVE_labl_Marker; vgmstream->meta_type = meta_RIFF_WAVE_labl_Marker;
} }
else if (loop_start_offset >= 0) else if (loop_start_offset >= 0)
@ -369,7 +417,7 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) {
if (mwv) if (mwv)
{ {
int i, c; int i, c;
if (coding_type == coding_L5_555) if (fmt.coding_type == coding_L5_555)
{ {
const int filter_order = 3; const int filter_order = 3;
int filter_count = read_32bitLE(mwv_pflt_offset+12, streamFile); int filter_count = read_32bitLE(mwv_pflt_offset+12, streamFile);
@ -379,7 +427,7 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) {
read_32bitLE(mwv_pflt_offset+4, streamFile) < 8 + filter_count * 4 * filter_order) read_32bitLE(mwv_pflt_offset+4, streamFile) < 8 + filter_count * 4 * filter_order)
goto fail; goto fail;
if (filter_count > 0x20) goto fail; if (filter_count > 0x20) goto fail;
for (c = 0; c < channel_count; c++) for (c = 0; c < fmt.channel_count; c++)
{ {
for (i = 0; i < filter_count * filter_order; i++) for (i = 0; i < filter_count * filter_order; i++)
{ {
@ -400,7 +448,7 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) {
{0x04ab,0xfced,0x0789,0xfedf,0x09a2,0xfae5,0x0c90,0xfac1, {0x04ab,0xfced,0x0789,0xfedf,0x09a2,0xfae5,0x0c90,0xfac1,
0x084d,0xfaa4,0x0982,0xfdf7,0x0af6,0xfafa,0x0be6,0xfbf5}; 0x084d,0xfaa4,0x0982,0xfdf7,0x0af6,0xfafa,0x0be6,0xfbf5};
for (c = 0; c < channel_count; c++) for (c = 0; c < fmt.channel_count; c++)
{ {
int i; int i;
for (i = 0; i < 16; i++) for (i = 0; i < 16; i++)
@ -419,10 +467,10 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) {
STREAMFILE_DEFAULT_BUFFER_SIZE); STREAMFILE_DEFAULT_BUFFER_SIZE);
if (!vgmstream->ch[0].streamfile) goto fail; if (!vgmstream->ch[0].streamfile) goto fail;
for (i=0;i<channel_count;i++) { for (i=0;i<fmt.channel_count;i++) {
vgmstream->ch[i].streamfile = vgmstream->ch[0].streamfile; vgmstream->ch[i].streamfile = vgmstream->ch[0].streamfile;
vgmstream->ch[i].offset = vgmstream->ch[i].channel_start_offset = vgmstream->ch[i].offset = vgmstream->ch[i].channel_start_offset =
start_offset+i*interleave; start_offset+i*fmt.interleave;
} }
} }
@ -438,14 +486,12 @@ VGMSTREAM * init_vgmstream_rifx(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL; VGMSTREAM * vgmstream = NULL;
char filename[260]; char filename[260];
struct riff_fmt_chunk fmt;
off_t file_size = -1; off_t file_size = -1;
int channel_count = 0;
int sample_count = 0; int sample_count = 0;
int fact_sample_count = -1; int fact_sample_count = -1;
int sample_rate = 0;
int coding_type = -1;
off_t start_offset = -1; off_t start_offset = -1;
int interleave = -1;
off_t wiih_offset = -1; off_t wiih_offset = -1;
uint32_t wiih_size = 0; uint32_t wiih_size = 0;
@ -454,7 +500,6 @@ VGMSTREAM * init_vgmstream_rifx(STREAMFILE *streamFile) {
off_t loop_end_offset = -1; off_t loop_end_offset = -1;
uint32_t riff_size; uint32_t riff_size;
uint32_t data_size = 0; uint32_t data_size = 0;
uint32_t block_size = 0;
int FormatChunkFound = 0; int FormatChunkFound = 0;
int DataChunkFound = 0; int DataChunkFound = 0;
@ -496,32 +541,14 @@ VGMSTREAM * init_vgmstream_rifx(STREAMFILE *streamFile) {
if (FormatChunkFound) goto fail; if (FormatChunkFound) goto fail;
FormatChunkFound = 1; FormatChunkFound = 1;
sample_rate = read_32bitBE(current_chunk+0x0c,streamFile); if (-1 == read_fmt(1, /* big endian == true */
channel_count = read_16bitBE(current_chunk+0x0a,streamFile); streamFile,
block_size = read_16bitBE(current_chunk+0x14,streamFile); current_chunk,
&fmt,
0, /* sns == false */
0)) /* mwv == false */
goto fail;
switch ((uint16_t)read_16bitBE(current_chunk+0x8,streamFile)) {
case 1: /* PCM */
switch (read_16bitBE(current_chunk+0x16,streamFile)) {
case 16:
coding_type = coding_PCM16BE;
interleave = 2;
break;
case 8:
coding_type = coding_PCM8_U_int;
interleave = 1;
break;
default:
goto fail;
}
break;
case 0xFFF0:
coding_type = coding_NGC_DSP;
interleave = 8;
break;
default:
goto fail;
}
break; break;
case 0x64617461: /* data */ case 0x64617461: /* data */
/* at most one per file */ /* at most one per file */
@ -565,38 +592,67 @@ VGMSTREAM * init_vgmstream_rifx(STREAMFILE *streamFile) {
if (!FormatChunkFound || !DataChunkFound) goto fail; if (!FormatChunkFound || !DataChunkFound) goto fail;
switch (coding_type) { switch (fmt.coding_type) {
case coding_PCM16BE: case coding_PCM16BE:
sample_count = data_size/2/channel_count; sample_count = data_size/2/fmt.channel_count;
break; break;
case coding_PCM8_U_int: case coding_PCM8_U_int:
sample_count = data_size/channel_count; sample_count = data_size/fmt.channel_count;
break; break;
case coding_NGC_DSP: case coding_NGC_DSP:
/* the only way of getting DSP info right now */ /* the only way of getting DSP info right now */
if (wiih_offset < 0 || wiih_size != 0x2e*channel_count) goto fail; if (wiih_offset < 0 || wiih_size != 0x2e*fmt.channel_count) goto fail;
sample_count = data_size/8/channel_count*14; sample_count = data_size/8/fmt.channel_count*14;
break; break;
#if 0
/* found in RE:ORC, looks like it should be MS_IMA instead */
case coding_MSADPCM:
sample_count = msadpcm_bytes_to_samples(data_size, fmt.block_size, fmt.channel_count);
break;
#endif
default:
goto fail;
} }
/* build the VGMSTREAM */ /* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag); vgmstream = allocate_vgmstream(fmt.channel_count,loop_flag);
if (!vgmstream) goto fail; if (!vgmstream) goto fail;
/* fill in the vital statistics */ /* fill in the vital statistics */
vgmstream->num_samples = sample_count; vgmstream->num_samples = sample_count;
vgmstream->sample_rate = sample_rate; vgmstream->sample_rate = fmt.sample_rate;
vgmstream->coding_type = fmt.coding_type;
vgmstream->coding_type = coding_type;
if (channel_count > 1 && coding_type != coding_PCM8_U_int)
vgmstream->layout_type = layout_interleave;
else
vgmstream->layout_type = layout_none; vgmstream->layout_type = layout_none;
vgmstream->interleave_block_size = interleave; if (fmt.channel_count > 1) {
switch (fmt.coding_type) {
case coding_PCM8_U_int:
case coding_MS_IMA:
case coding_MSADPCM:
// use layout_none from above
break;
default:
vgmstream->layout_type = layout_interleave;
break;
}
}
if (coding_type == coding_MS_IMA) vgmstream->interleave_block_size = fmt.interleave;
vgmstream->interleave_block_size = block_size; switch (fmt.coding_type) {
case coding_MSADPCM:
case coding_MS_IMA:
// override interleave_block_size with frame size
vgmstream->interleave_block_size = fmt.block_size;
break;
default:
// use interleave from above
break;
}
if (fmt.coding_type == coding_MS_IMA)
vgmstream->interleave_block_size = fmt.block_size;
if (loop_flag) { if (loop_flag) {
if (loop_start_offset >= 0) if (loop_start_offset >= 0)
@ -614,7 +670,7 @@ VGMSTREAM * init_vgmstream_rifx(STREAMFILE *streamFile) {
/* read from WiiH */ /* read from WiiH */
if (wiih_offset >= 0) { if (wiih_offset >= 0) {
int i,j; int i,j;
for (i=0;i<channel_count;i++) { for (i=0;i<fmt.channel_count;i++) {
for (j=0;j<16;j++) for (j=0;j<16;j++)
vgmstream->ch[i].adpcm_coef[j] = read_16bitBE(wiih_offset + i * 0x2e + j * 2,streamFile); vgmstream->ch[i].adpcm_coef[j] = read_16bitBE(wiih_offset + i * 0x2e + j * 2,streamFile);
vgmstream->ch[i].adpcm_history1_16 = read_16bitBE(wiih_offset + i * 0x2e + 0x24,streamFile); vgmstream->ch[i].adpcm_history1_16 = read_16bitBE(wiih_offset + i * 0x2e + 0x24,streamFile);
@ -630,10 +686,10 @@ VGMSTREAM * init_vgmstream_rifx(STREAMFILE *streamFile) {
STREAMFILE_DEFAULT_BUFFER_SIZE); STREAMFILE_DEFAULT_BUFFER_SIZE);
if (!vgmstream->ch[0].streamfile) goto fail; if (!vgmstream->ch[0].streamfile) goto fail;
for (i=0;i<channel_count;i++) { for (i=0;i<fmt.channel_count;i++) {
vgmstream->ch[i].streamfile = vgmstream->ch[0].streamfile; vgmstream->ch[i].streamfile = vgmstream->ch[0].streamfile;
vgmstream->ch[i].offset = vgmstream->ch[i].channel_start_offset = vgmstream->ch[i].offset = vgmstream->ch[i].channel_start_offset =
start_offset+i*interleave; start_offset+i*fmt.interleave;
} }
} }
@ -644,3 +700,177 @@ fail:
if (vgmstream) close_vgmstream(vgmstream); if (vgmstream) close_vgmstream(vgmstream);
return NULL; return NULL;
} }
/* XNBm (Windows 7 Phone) */
VGMSTREAM * init_vgmstream_xnbm(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
char filename[260];
struct riff_fmt_chunk fmt;
off_t file_size = -1;
int sample_count = 0;
off_t start_offset = -1;
int loop_flag = 0;
#if 0
long loop_start_ms = -1;
long loop_end_ms = -1;
off_t loop_start_offset = -1;
off_t loop_end_offset = -1;
#endif
uint32_t xnbm_size;
uint32_t data_size = 0;
/* check extension, case insensitive */
streamFile->get_name(streamFile,filename,sizeof(filename));
if (strcasecmp("xnb",filename_extension(filename)))
{
goto fail;
}
/* check header */
if ((uint32_t)read_32bitBE(0,streamFile)!=0x584E426d) /* "XNBm" */
goto fail;
/* check version? */
if ((uint32_t)read_16bitLE(4,streamFile)!=5)
goto fail;
xnbm_size = read_32bitLE(6,streamFile);
file_size = get_streamfile_size(streamFile);
/* check for tructated XNBm */
if (file_size < xnbm_size) goto fail;
/* read through chunks to verify format and find metadata */
{
off_t current_chunk = 0xa; /* start with first chunk */
int id_string_len;
uint32_t fmt_chunk_size;
/* flag? count of strings? */
if (read_8bit(current_chunk ++, streamFile) != 1)
goto fail;
/* string length */
id_string_len = read_8bit(current_chunk ++, streamFile);
/* skip string */
/* may want to check this ID "Microsoft.Xna.Framework.Content.SoundEffectReader" */
current_chunk += id_string_len;
/* ???? */
if (read_32bitLE(current_chunk, streamFile) != 0)
goto fail;
current_chunk += 4;
/* ???? */
if (read_8bit(current_chunk ++, streamFile) != 0)
goto fail;
/* flag? count of chunks? */
if (read_8bit(current_chunk++, streamFile) != 1)
goto fail;
/* fmt size */
fmt_chunk_size = read_32bitLE(current_chunk, streamFile);
current_chunk += 4;
if (-1 == read_fmt(0, /* big endian == false */
streamFile,
current_chunk-8, /* read_fmt() expects to skip "fmt "+size */
&fmt,
0, /* sns == false */
0)) /* mwv == false */
goto fail;
current_chunk += fmt_chunk_size;
/* data size! */
data_size = read_32bitLE(current_chunk, streamFile);
current_chunk += 4;
start_offset = current_chunk;
}
switch (fmt.coding_type) {
case coding_PCM16LE:
sample_count = data_size/2/fmt.channel_count;
break;
case coding_PCM8_U_int:
sample_count = data_size/fmt.channel_count;
break;
case coding_MSADPCM:
sample_count = msadpcm_bytes_to_samples(data_size, fmt.block_size, fmt.channel_count);
break;
case coding_MS_IMA:
sample_count = (data_size / fmt.block_size) * (fmt.block_size - 4 * fmt.channel_count) * 2 / fmt.channel_count +
((data_size % fmt.block_size) ? (data_size % fmt.block_size - 4 * fmt.channel_count) * 2 / fmt.channel_count : 0);
break;
default:
goto fail;
}
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(fmt.channel_count,loop_flag);
if (!vgmstream) goto fail;
/* fill in the vital statistics */
vgmstream->num_samples = sample_count;
vgmstream->sample_rate = fmt.sample_rate;
vgmstream->coding_type = fmt.coding_type;
vgmstream->layout_type = layout_none;
if (fmt.channel_count > 1) {
switch (fmt.coding_type) {
case coding_PCM8_U_int:
case coding_MS_IMA:
case coding_MSADPCM:
// use layout_none from above
break;
default:
vgmstream->layout_type = layout_interleave;
break;
}
}
vgmstream->interleave_block_size = fmt.interleave;
switch (fmt.coding_type) {
case coding_MSADPCM:
case coding_MS_IMA:
// override interleave_block_size with frame size
vgmstream->interleave_block_size = fmt.block_size;
break;
default:
// use interleave from above
break;
}
vgmstream->meta_type = meta_XNBm;
/* open the file, set up each channel */
{
int i;
vgmstream->ch[0].streamfile = streamFile->open(streamFile,filename,
STREAMFILE_DEFAULT_BUFFER_SIZE);
if (!vgmstream->ch[0].streamfile) goto fail;
for (i=0;i<fmt.channel_count;i++) {
vgmstream->ch[i].streamfile = vgmstream->ch[0].streamfile;
vgmstream->ch[i].offset = vgmstream->ch[i].channel_start_offset =
start_offset+i*fmt.interleave;
}
}
return vgmstream;
/* clean up anything we may have opened */
fail:
if (vgmstream) close_vgmstream(vgmstream);
return NULL;
}

View File

@ -318,6 +318,7 @@ VGMSTREAM * (*init_vgmstream_fcns[])(STREAMFILE *streamFile) = {
init_vgmstream_ps2_hsf, init_vgmstream_ps2_hsf,
init_vgmstream_ps3_ivag, init_vgmstream_ps3_ivag,
init_vgmstream_ps2_2pfs, init_vgmstream_ps2_2pfs,
init_vgmstream_xnbm,
}; };
#define INIT_VGMSTREAM_FCNS (sizeof(init_vgmstream_fcns)/sizeof(init_vgmstream_fcns[0])) #define INIT_VGMSTREAM_FCNS (sizeof(init_vgmstream_fcns)/sizeof(init_vgmstream_fcns[0]))
@ -2339,6 +2340,9 @@ void describe_vgmstream(VGMSTREAM * vgmstream, char * desc, int length) {
case meta_RIFX_WAVE_smpl: case meta_RIFX_WAVE_smpl:
snprintf(temp,TEMPSIZE,"RIFX WAVE header with sample looping info"); snprintf(temp,TEMPSIZE,"RIFX WAVE header with sample looping info");
break; break;
case meta_XNBm:
snprintf(temp,TEMPSIZE,"XNBm header");
break;
case meta_PCM_SCD: case meta_PCM_SCD:
snprintf(temp,TEMPSIZE,"PCM file with custom header (SCD)"); snprintf(temp,TEMPSIZE,"PCM file with custom header (SCD)");
break; break;

View File

@ -422,6 +422,7 @@ typedef enum {
meta_RIFF_WAVE_SNS, /* .sns RIFF */ meta_RIFF_WAVE_SNS, /* .sns RIFF */
meta_RIFX_WAVE, /* RIFX, for big-endian WAVs */ meta_RIFX_WAVE, /* RIFX, for big-endian WAVs */
meta_RIFX_WAVE_smpl, /* RIFX w/ loop data in smpl chunk */ meta_RIFX_WAVE_smpl, /* RIFX w/ loop data in smpl chunk */
meta_XNBm, /* XNBm, which has a RIFF fmt chunk */
meta_PC_MXST, /* Lego Island MxSt */ meta_PC_MXST, /* Lego Island MxSt */
meta_PC_SOB_SAB, /* Worms 4 Mayhem SOB+SAB file */ meta_PC_SOB_SAB, /* Worms 4 Mayhem SOB+SAB file */
meta_NWA, /* Visual Art's NWA */ meta_NWA, /* Visual Art's NWA */

View File

@ -265,6 +265,7 @@ gchar *vgmstream_exts [] = {
"xa30", "xa30",
"xau", "xau",
"xmu", "xmu",
"xnb",
"xsf", "xsf",
"xss", "xss",
"xvag", "xvag",

View File

@ -338,6 +338,7 @@ char * extension_list[] = {
"xa30\0XA30 Audio File (*.XA30)\0", "xa30\0XA30 Audio File (*.XA30)\0",
"xau\0XAU Audio File (*.XAU)\0", "xau\0XAU Audio File (*.XAU)\0",
"xmu\0XMU Audio File (*.XMU)\0", "xmu\0XMU Audio File (*.XMU)\0",
"xnb\0XNB Audio File (*.XNB)\0",
"xsf\0XSF Audio File (*.XSF)\0", "xsf\0XSF Audio File (*.XSF)\0",
"xss\0XSS Audio File (*.XSS)\0", "xss\0XSS Audio File (*.XSS)\0",
"xvag\0XVAG Audio File (*.XVAG)\0", "xvag\0XVAG Audio File (*.XVAG)\0",