Merge pull request #198 from bnnm/fadpcm-ima-adm

FADPCM, IMA, ADM
This commit is contained in:
Christopher Snowhill 2018-02-25 15:16:12 -08:00 committed by GitHub
commit a24b02f5cc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 643 additions and 493 deletions

View File

@ -136,6 +136,9 @@ void decode_mta2(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing,
/* mc3_decoder */ /* mc3_decoder */
void decode_mc3(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); void decode_mc3(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel);
/* fadpcm_decoder */
void decode_fadpcm(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
/* ea_mt_decoder*/ /* ea_mt_decoder*/
ea_mt_codec_data *init_ea_mt(int channel_count, int type); ea_mt_codec_data *init_ea_mt(int channel_count, int type);
void decode_ea_mt(VGMSTREAM * vgmstream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); void decode_ea_mt(VGMSTREAM * vgmstream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel);

View File

@ -0,0 +1,85 @@
#include "coding.h"
#include "../util.h"
/* FADPCM table */
static const int8_t fadpcm_coefs[8][2] = {
{ 0 , 0 },
{ 60 , 0 },
{ 122 , 60 },
{ 115 , 52 },
{ 98 , 55 },
{ 0 , 0 },
{ 0 , 0 },
{ 0 , 0 },
};
/* FMOD's FADPCM, basically XA/PSX ADPCM with a fancy header layout.
* Code/layout could be simplified but tries to emulate FMOD's code.
* Algorithm and tables debugged from their PC DLLs. */
void decode_fadpcm(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) {
off_t frame_offset;
int i, j, k;
int block_samples, num_frame, samples_done = 0, sample_count = 0;
int coef_index[8], shift_factor[8];
int32_t hist1; //= stream->adpcm_history1_32;
int32_t hist2; //= stream->adpcm_history2_32;
/* external interleave (fixed size), mono */
block_samples = (0x8c - 0xc) * 2;
num_frame = first_sample / block_samples;
first_sample = first_sample % block_samples;
frame_offset = stream->offset + 0x8c*num_frame;
/* parse 0xc header */
{
uint32_t coefs, shifts;
coefs = read_32bitLE(frame_offset + 0x00, stream->streamfile);
shifts = read_32bitLE(frame_offset + 0x04, stream->streamfile);
hist1 = read_16bitLE(frame_offset + 0x08, stream->streamfile);
hist2 = read_16bitLE(frame_offset + 0x0a, stream->streamfile);
for (i = 0; i < 8; i++) {
coef_index[i] = (coefs >> i*4) & 0x0f;
shift_factor[i] = (shifts >> i*4) & 0x0f;
}
/* header samples are not written to outbuf */
}
/* decode nibbles, grouped in 0x10 * 0x04 * 2 */
for (i = 0; i < 8; i++) {
off_t group_offset = frame_offset + 0x0c + 0x10*i;
int32_t coef1 = fadpcm_coefs[(coef_index[i] % 0x07)][0]; /* indexes > 7 are repeats (ex. 0x9 is 0x2) */
int32_t coef2 = fadpcm_coefs[(coef_index[i] % 0x07)][1];
int32_t shift = 0x16 - shift_factor[i];
for (j = 0; j < 4; j++) {
uint32_t nibbles = read_32bitLE(group_offset + 0x04*j, stream->streamfile);
for (k = 0; k < 8; k++) {
int32_t new_sample;
new_sample = (nibbles >> k*4) & 0x0f;
new_sample = (new_sample << 28) >> shift; /* sign extend + scale */
new_sample = (new_sample - hist2*coef2 + hist1*coef1);
new_sample = new_sample >> 6; /* (new_sample / 64) has minor rounding differences */
new_sample = clamp16(new_sample);
if (sample_count >= first_sample && samples_done < samples_to_do) {
outbuf[samples_done * channelspacing] = new_sample;
samples_done++;
}
sample_count++;
hist2 = hist1;
hist1 = new_sample;
}
}
}
//stream->adpcm_history1_32 = hist1;
//stream->adpcm_history2_32 = hist2;
}

View File

@ -8,14 +8,7 @@
* All IMAs are mostly the same with these variations: * All IMAs are mostly the same with these variations:
* - interleave: blocks and channels are handled externally (layouts) or internally (mixed channels) * - interleave: blocks and channels are handled externally (layouts) or internally (mixed channels)
* - block header: none (external), normal (4 bytes of history 16b + step 8b + reserved 8b) or others; per channel/global * - block header: none (external), normal (4 bytes of history 16b + step 8b + reserved 8b) or others; per channel/global
* - expand type: ms-ima style or others; low or high nibble first * - expand type: IMA style or variations; low or high nibble first
*
* todo:
* MS IMAs have the last sample of the prev block in the block header. In Microsoft's implementation, the header sample
* is written first and last sample is skipped (since they match). vgmstream ignores the header sample and
* writes the last one instead. This means the very first sample in the first header in a stream is incorrectly skipped.
* Header step should be 8 bit.
* Officially defined in "Microsoft Multimedia Standards Update" doc (RIFFNEW.pdf).
*/ */
static const int ADPCMTable[89] = { static const int ADPCMTable[89] = {
@ -39,15 +32,15 @@ static const int IMA_IndexTable[16] = {
}; };
/* Standard IMA (most common) */ /* Original IMA expansion, using shift+ADDs to avoid MULs (slow back then) */
static void std_ima_expand_nibble(VGMSTREAMCHANNEL * stream, off_t byte_offset, int nibble_shift, int32_t * hist1, int32_t * step_index) { static void std_ima_expand_nibble(VGMSTREAMCHANNEL * stream, off_t byte_offset, int nibble_shift, int32_t * hist1, int32_t * step_index) {
int sample_nibble, sample_decoded, step, delta; int sample_nibble, sample_decoded, step, delta;
/* calculate diff = [signed] (step / 8) + (step / 4) + (step / 2) + (step) [when code = 4+2+1] /* simplified through math from:
* simplified through math, using bitwise ops to avoid rounding: * - diff = (code + 1/2) * (step / 4)
* diff = (code + 1/2) * (step / 4) * > diff = ((step * nibble) + (step / 2)) / 4
* > diff = (step * nibble / 4) + (step / 8) * > diff = (step * nibble / 4) + (step / 8)
* > diff = (((step * nibble) + (step / 2)) / 4) */ * final diff = [signed] (step / 8) + (step / 4) + (step / 2) + (step) [when code = 4+2+1] */
sample_nibble = (read_8bit(byte_offset,stream->streamfile) >> nibble_shift)&0xf; /* ADPCM code */ sample_nibble = (read_8bit(byte_offset,stream->streamfile) >> nibble_shift)&0xf; /* ADPCM code */
sample_decoded = *hist1; /* predictor value */ sample_decoded = *hist1; /* predictor value */
@ -81,7 +74,33 @@ static void std_ima_expand_nibble_16(VGMSTREAMCHANNEL * stream, off_t byte_offse
if (sample_nibble & 8) delta = -delta; if (sample_nibble & 8) delta = -delta;
sample_decoded += delta; sample_decoded += delta;
*hist1 = clamp16(sample_decoded); //no need for this actually *hist1 = clamp16(sample_decoded); /* no need for this, actually */
*step_index += IMA_IndexTable[sample_nibble];
if (*step_index < 0) *step_index=0;
if (*step_index > 88) *step_index=88;
}
/* Original IMA expansion, but using MULs rather than shift+ADDs (faster for newer processors).
* There is minor rounding difference between ADD and MUL expansions, noticeable/propagated in non-headered IMAs. */
static void std_ima_expand_nibble_mul(VGMSTREAMCHANNEL * stream, off_t byte_offset, int nibble_shift, int32_t * hist1, int32_t * step_index) {
int sample_nibble, sample_decoded, step, delta;
/* simplified through math from:
* - diff = (code + 1/2) * (step / 4)
* > diff = (code + 1/2) * step) / 4) * (2 / 2)
* > diff = (code + 1/2) * 2 * step / 8
* final diff = [signed] ((code * 2 + 1) * step) / 8 */
sample_nibble = (read_8bit(byte_offset,stream->streamfile) >> nibble_shift)&0xf;
sample_decoded = *hist1;
step = ADPCMTable[*step_index];
delta = (sample_nibble & 0x7);
delta = ((delta * 2 + 1) * step) >> 3;
if (sample_nibble & 8) delta = -delta;
sample_decoded += delta;
*hist1 = clamp16(sample_decoded);
*step_index += IMA_IndexTable[sample_nibble]; *step_index += IMA_IndexTable[sample_nibble];
if (*step_index < 0) *step_index=0; if (*step_index < 0) *step_index=0;
if (*step_index > 88) *step_index=88; if (*step_index > 88) *step_index=88;
@ -96,7 +115,8 @@ static void n3ds_ima_expand_nibble(VGMSTREAMCHANNEL * stream, off_t byte_offset,
step = ADPCMTable[*step_index]; step = ADPCMTable[*step_index];
sample_decoded = sample_decoded << 3; sample_decoded = sample_decoded << 3;
delta = step * (sample_nibble & 7) * 2 + step; /* custom */ delta = (sample_nibble & 0x07);
delta = step * delta * 2 + step; /* custom */
if (sample_nibble & 8) delta = -delta; if (sample_nibble & 8) delta = -delta;
sample_decoded += delta; sample_decoded += delta;
sample_decoded = sample_decoded >> 3; sample_decoded = sample_decoded >> 3;
@ -149,24 +169,6 @@ static void otns_ima_expand_nibble(VGMSTREAMCHANNEL * stream, off_t byte_offset,
if (*step_index > 88) *step_index=88; if (*step_index > 88) *step_index=88;
} }
/* Ubisoft games, algorithm by Zench (https://bitbucket.org/Zenchreal/decubisnd) */
static void ubi_ima_expand_nibble(VGMSTREAMCHANNEL * stream, off_t byte_offset, int nibble_shift, int32_t * hist1, int32_t * step_index) {
int sample_nibble, sample_decoded, step, delta;
sample_nibble = (read_8bit(byte_offset,stream->streamfile) >> nibble_shift)&0xf;
sample_decoded = *hist1;
step = ADPCMTable[*step_index];
delta = (((sample_nibble & 7) * 2 + 1) * step) >> 3; /* custom */
if (sample_nibble & 8) delta = -delta;
sample_decoded += delta;
*hist1 = clamp16(sample_decoded);
*step_index += IMA_IndexTable[sample_nibble];
if (*step_index < 0) *step_index=0;
if (*step_index > 88) *step_index=88;
}
/* ************************************ */ /* ************************************ */
/* DVI/IMA */ /* DVI/IMA */
/* ************************************ */ /* ************************************ */
@ -176,7 +178,6 @@ static void ubi_ima_expand_nibble(VGMSTREAMCHANNEL * stream, off_t byte_offset,
* For vgmstream, low nibble is called "IMA ADPCM" and high nibble is "DVI IMA ADPCM" (same thing though). */ * For vgmstream, low nibble is called "IMA ADPCM" and high nibble is "DVI IMA ADPCM" (same thing though). */
void decode_standard_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, int is_stereo, int is_high_first) { void decode_standard_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, int is_stereo, int is_high_first) {
int i, sample_count = 0; int i, sample_count = 0;
int32_t hist1 = stream->adpcm_history1_32; int32_t hist1 = stream->adpcm_history1_32;
int step_index = stream->adpcm_step_index; int step_index = stream->adpcm_step_index;
@ -186,7 +187,7 @@ void decode_standard_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channel
if (step_index < 0) step_index=0; if (step_index < 0) step_index=0;
if (step_index > 88) step_index=88; if (step_index > 88) step_index=88;
/* decode nibbles */ /* decode nibbles (layout: varies) */
for (i = first_sample; i < first_sample + samples_to_do; i++, sample_count += channelspacing) { for (i = first_sample; i < first_sample + samples_to_do; i++, sample_count += channelspacing) {
off_t byte_offset = is_stereo ? off_t byte_offset = is_stereo ?
stream->offset + i : /* stereo: one nibble per channel */ stream->offset + i : /* stereo: one nibble per channel */
@ -205,7 +206,6 @@ void decode_standard_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channel
void decode_3ds_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) { void decode_3ds_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) {
int i, sample_count; int i, sample_count;
int32_t hist1 = stream->adpcm_history1_32; int32_t hist1 = stream->adpcm_history1_32;
int step_index = stream->adpcm_step_index; int step_index = stream->adpcm_step_index;
@ -227,7 +227,6 @@ void decode_3ds_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspaci
void decode_snds_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { void decode_snds_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) {
int i, sample_count; int i, sample_count;
int32_t hist1 = stream->adpcm_history1_32; int32_t hist1 = stream->adpcm_history1_32;
int step_index = stream->adpcm_step_index; int step_index = stream->adpcm_step_index;
@ -249,7 +248,6 @@ void decode_snds_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspac
void decode_otns_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { void decode_otns_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) {
int i, sample_count; int i, sample_count;
int32_t hist1 = stream->adpcm_history1_32; int32_t hist1 = stream->adpcm_history1_32;
int step_index = stream->adpcm_step_index; int step_index = stream->adpcm_step_index;
@ -275,20 +273,20 @@ void decode_otns_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample *
/* MS-IMA */ /* MS-IMA */
/* ************************************ */ /* ************************************ */
/* IMA with variable-sized frames, header and custom nibble layout (outputs non-aligned number of samples). /* IMA with custom frame sizes, header and nibble layout. Outputs an odd number of samples per frame,
* so to simplify calcs this decodes full frames, thus hist doesn't need to be mantained.
* Officially defined in "Microsoft Multimedia Standards Update" doc (RIFFNEW.pdf). */ * Officially defined in "Microsoft Multimedia Standards Update" doc (RIFFNEW.pdf). */
void decode_ms_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { void decode_ms_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) {
int i, samples_read = 0, samples_done = 0, max_samples; int i, samples_read = 0, samples_done = 0, max_samples;
int32_t hist1;// = stream->adpcm_history1_32; int32_t hist1;// = stream->adpcm_history1_32;
int step_index;// = stream->adpcm_step_index; int step_index;// = stream->adpcm_step_index;
/* internal interleave (configurable size), mixed channels (4 byte per ch) */ /* internal interleave (configurable size), mixed channels */
int block_samples = ((vgmstream->interleave_block_size - 0x04*vgmstream->channels) * 2 / vgmstream->channels) + 1; int block_samples = ((vgmstream->interleave_block_size - 0x04*vgmstream->channels) * 2 / vgmstream->channels) + 1;
first_sample = first_sample % block_samples; first_sample = first_sample % block_samples;
/* normal header (hist+step+reserved per channel) */ /* normal header (hist+step+reserved), per channel */
{ { //if (first_sample == 0) {
off_t header_offset = stream->offset + 0x04*channel; off_t header_offset = stream->offset + 0x04*channel;
hist1 = read_16bitLE(header_offset+0x00,stream->streamfile); hist1 = read_16bitLE(header_offset+0x00,stream->streamfile);
@ -296,7 +294,7 @@ void decode_ms_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * ou
if (step_index < 0) step_index = 0; if (step_index < 0) step_index = 0;
if (step_index > 88) step_index = 88; if (step_index > 88) step_index = 88;
/* write header sample */ /* write header sample (odd samples per block) */
if (samples_read >= first_sample && samples_done < samples_to_do) { if (samples_read >= first_sample && samples_done < samples_to_do) {
outbuf[samples_done * channelspacing] = (short)hist1; outbuf[samples_done * channelspacing] = (short)hist1;
samples_done++; samples_done++;
@ -308,12 +306,12 @@ void decode_ms_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * ou
if (max_samples > samples_to_do + first_sample - samples_done) if (max_samples > samples_to_do + first_sample - samples_done)
max_samples = samples_to_do + first_sample - samples_done; /* for smaller last block */ max_samples = samples_to_do + first_sample - samples_done; /* for smaller last block */
/* decode nibbles (layout: alternates 4*2 nibbles per channel) */ /* decode nibbles (layout: alternates 4 bytes/4*2 nibbles per channel) */
for (i = 0; i < max_samples; i++) { for (i = 0; i < max_samples; i++) {
off_t byte_offset = stream->offset + 0x04*vgmstream->channels + 0x04*channel + 0x04*vgmstream->channels*(i/8) + (i%8)/2; off_t byte_offset = stream->offset + 0x04*vgmstream->channels + 0x04*channel + 0x04*vgmstream->channels*(i/8) + (i%8)/2;
int nibble_shift = (i&1?4:0); /* low nibble first */ int nibble_shift = (i&1?4:0); /* low nibble first */
std_ima_expand_nibble(stream, byte_offset,nibble_shift, &hist1, &step_index); std_ima_expand_nibble(stream, byte_offset,nibble_shift, &hist1, &step_index); /* original expand */
if (samples_read >= first_sample && samples_done < samples_to_do) { if (samples_read >= first_sample && samples_done < samples_to_do) {
outbuf[samples_done * channelspacing] = (short)(hist1); outbuf[samples_done * channelspacing] = (short)(hist1);
@ -331,24 +329,23 @@ void decode_ms_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * ou
//stream->adpcm_step_index = step_index; //stream->adpcm_step_index = step_index;
} }
/* Reflection's MS-IMA (some layout info from XA2WAV by Deniz Oezmen) */ /* Reflection's MS-IMA with custom nibble layout (some info from XA2WAV by Deniz Oezmen) */
void decode_ref_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { void decode_ref_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) {
int i, samples_read = 0, samples_done = 0, max_samples; int i, samples_read = 0, samples_done = 0, max_samples;
int32_t hist1;// = stream->adpcm_history1_32; int32_t hist1;// = stream->adpcm_history1_32;
int step_index;// = stream->adpcm_step_index; int step_index;// = stream->adpcm_step_index;
/* internal interleave (configurable size), mixed channels (4 byte per ch) */ /* internal interleave (configurable size), mixed channels */
int block_channel_size = (vgmstream->interleave_block_size - 0x04*vgmstream->channels) / vgmstream->channels; int block_channel_size = (vgmstream->interleave_block_size - 0x04*vgmstream->channels) / vgmstream->channels;
int block_samples = ((vgmstream->interleave_block_size - 0x04*vgmstream->channels) * 2 / vgmstream->channels) + 1; int block_samples = ((vgmstream->interleave_block_size - 0x04*vgmstream->channels) * 2 / vgmstream->channels) + 1;
first_sample = first_sample % block_samples; first_sample = first_sample % block_samples;
/* normal header (hist+step+reserved per channel) */ /* normal header (hist+step+reserved), per channel */
{ { //if (first_sample == 0) {
off_t header_offset = stream->offset + 0x04*channel; off_t header_offset = stream->offset + 0x04*channel;
hist1 = read_16bitLE(header_offset+0x00,stream->streamfile); hist1 = read_16bitLE(header_offset+0x00,stream->streamfile);
step_index = read_8bit(header_offset+0x02,stream->streamfile); /* 0x03: reserved */ step_index = read_8bit(header_offset+0x02,stream->streamfile);
if (step_index < 0) step_index = 0; if (step_index < 0) step_index = 0;
if (step_index > 88) step_index = 88; if (step_index > 88) step_index = 88;
@ -391,90 +388,92 @@ void decode_ref_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * o
/* XBOX-IMA */ /* XBOX-IMA */
/* ************************************ */ /* ************************************ */
/* MS-IMA with fixed frame size, skips last sample per channel (for aligment) and custom multichannel nibble layout. /* MS-IMA with fixed frame size, and outputs an even number of samples per frame (skips last nibble).
* For multichannel the layout is (I think) mixed stereo channels (ex. 6ch: 2ch + 2ch + 2ch) */ * Defined in Xbox's SDK. Multichannel interleaves 2ch*N/2, or 1ch*N with odd num_channels. */
void decode_xbox_ima(VGMSTREAM * vgmstream,VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do,int channel) { void decode_xbox_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) {
int i, sample_count; int i, sample_count = 0;
int32_t hist1 = stream->adpcm_history1_32; int32_t hist1 = stream->adpcm_history1_32;
int step_index = stream->adpcm_step_index; int step_index = stream->adpcm_step_index;
off_t offset = stream->offset; /* internal interleave (fixed size), mixed channels */
int block_samples = (0x24-0x4) * 2;
//internal interleave (0x20+4 size), mixed channels (4 byte per ch, mixed stereo)
int block_samples = (vgmstream->channels==1) ?
32 :
32*(vgmstream->channels&2);//todo this can be zero in 4/5/8ch = SEGFAULT using % below
first_sample = first_sample % block_samples; first_sample = first_sample % block_samples;
//normal header (per channel) /* normal header (hist+step+reserved), per stereo/mono channel in blocks */
if (first_sample == 0) { if (first_sample == 0) {
off_t header_offset; off_t header_offset = (channelspacing & 1) ?
header_offset = stream->offset + 4*(channel%2); stream->offset + 0x24*(channel) + 0x00:
stream->offset + 0x48*(channel/2) + 0x04*(channel%2);
hist1 = read_16bitLE(header_offset,stream->streamfile); hist1 = read_16bitLE(header_offset+0x00,stream->streamfile);
step_index = read_16bitLE(header_offset+2,stream->streamfile); step_index = read_8bit(header_offset+0x02,stream->streamfile);
if (step_index < 0) step_index=0;
if (step_index > 88) step_index=88;
}
for (i=first_sample,sample_count=0; i<first_sample+samples_to_do; i++,sample_count+=channelspacing) {
int nibble_shift;
offset = (channelspacing==1) ?
stream->offset + 4*(channel%2) + 4 + i/8*4 + (i%8)/2 :
stream->offset + 4*(channel%2) + 4*2 + i/8*4*2 + (i%8)/2;
nibble_shift = (i&1?4:0); //low nibble first
std_ima_expand_nibble(stream, offset,nibble_shift, &hist1, &step_index);
outbuf[sample_count] = (short)(hist1);
}
//internal interleave: increment offset on complete frame
if (channelspacing==1) { /* mono */
if (offset-stream->offset == 32+3) // ??
stream->offset += 0x24;
} else {
if (offset-stream->offset == 64+(4*(channel%2))+3) // ??
stream->offset += 0x24*channelspacing;
}
stream->adpcm_history1_32 = hist1;
stream->adpcm_step_index = step_index;
}
/* mono XBOX-IMA ADPCM for interleave */
void decode_xbox_ima_int(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) {
int i, sample_count = 0, num_frame;
int32_t hist1 = stream->adpcm_history1_32;
int step_index = stream->adpcm_step_index;
//external interleave
int block_samples = (0x24 - 0x4) * 2; /* block size - header, 2 samples per byte */
num_frame = first_sample / block_samples;
first_sample = first_sample % block_samples;
//normal header
if (first_sample == 0) {
off_t header_offset = stream->offset + 0x24*num_frame;
hist1 = read_16bitLE(header_offset,stream->streamfile);
step_index = read_8bit(header_offset+2,stream->streamfile);
if (step_index < 0) step_index=0; if (step_index < 0) step_index=0;
if (step_index > 88) step_index=88; if (step_index > 88) step_index=88;
//must write history from header as last nibble/sample in block is almost always 0 / not encoded /* write header sample (even samples per block, skips last nibble) */
outbuf[sample_count] = (short)(hist1); outbuf[sample_count] = (short)(hist1);
sample_count += channelspacing; sample_count += channelspacing;
first_sample += 1; first_sample += 1;
samples_to_do -= 1; samples_to_do -= 1;
} }
for (i=first_sample; i < first_sample + samples_to_do; i++) { /* first_sample + samples_to_do should be block_samples at most */ /* decode nibbles (layout: alternates 4 bytes/4*2 nibbles per channel, in stereo blocks) */
off_t byte_offset = (stream->offset + 0x24*num_frame + 0x4) + (i-1)/2; for (i = first_sample; i < first_sample + samples_to_do; i++) {
int nibble_shift = ((i-1)&1?4:0); //low nibble first off_t byte_offset = (channelspacing & 1) ?
(stream->offset + 0x24*(channel) + 0x04) + (i-1)/2:
(stream->offset + 0x48*(channel/2) + 0x04*2) + 0x04*(channel%2) + 0x04*2*((i-1)/8) + ((i-1)%8)/2;
int nibble_shift = ((i-1)&1?4:0); /* low nibble first */
//last nibble/sample in block is ignored (next header sample contains it) /* must skip last nibble per official decoder, probably not needed though */
if (i < block_samples) {
std_ima_expand_nibble(stream, byte_offset,nibble_shift, &hist1, &step_index);
outbuf[sample_count] = (short)(hist1);
sample_count += channelspacing;
}
}
/* internal interleave: increment offset on complete frame */
if (i == block_samples) {
stream->offset += 0x24*vgmstream->channels;
}
stream->adpcm_history1_32 = hist1;
stream->adpcm_step_index = step_index;
}
/* Mono XBOX-IMA ADPCM, used for interleave. Also defined in Xbox's SDK. */
void decode_xbox_ima_int(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) {
int i, sample_count = 0, num_frame;
int32_t hist1 = stream->adpcm_history1_32;
int step_index = stream->adpcm_step_index;
/* external interleave (fixed size), mono */
int block_samples = (0x24 - 0x4) * 2;
num_frame = first_sample / block_samples;
first_sample = first_sample % block_samples;
/* normal header (hist+step+reserved), single channel */
if (first_sample == 0) {
off_t header_offset = stream->offset + 0x24*num_frame;
hist1 = read_16bitLE(header_offset+0x00,stream->streamfile);
step_index = read_8bit(header_offset+0x02,stream->streamfile);
if (step_index < 0) step_index=0;
if (step_index > 88) step_index=88;
/* write header sample (even samples per block, skips last nibble) */
outbuf[sample_count] = (short)(hist1);
sample_count += channelspacing;
first_sample += 1;
samples_to_do -= 1;
}
/* decode nibbles (layout: all nibbles from one channel) */
for (i = first_sample; i < first_sample + samples_to_do; i++) {
off_t byte_offset = (stream->offset + 0x24*num_frame + 0x4) + (i-1)/2;
int nibble_shift = ((i-1)&1?4:0); /* low nibble first */
/* must skip last nibble per spec, rarely needed though (ex. Gauntlet Dark Legacy) */
if (i < block_samples) { if (i < block_samples) {
std_ima_expand_nibble(stream, byte_offset,nibble_shift, &hist1, &step_index); std_ima_expand_nibble(stream, byte_offset,nibble_shift, &hist1, &step_index);
outbuf[sample_count] = (short)(hist1); outbuf[sample_count] = (short)(hist1);
@ -486,39 +485,42 @@ void decode_xbox_ima_int(VGMSTREAMCHANNEL * stream, sample * outbuf, int channel
stream->adpcm_step_index = step_index; stream->adpcm_step_index = step_index;
} }
/* Similar to MS-IMA with even number of samples, header sample is not written (setup only).
* Apparently clamps to -32767 unlike standard's -32768 (probably not noticeable).
* Info here: http://problemkaputt.de/gbatek.htm#dssoundnotes */
void decode_nds_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) { void decode_nds_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) {
int i, sample_count; int i, sample_count;
int32_t hist1 = stream->adpcm_history1_32;
int32_t hist1 = stream->adpcm_history1_16;//todo unneeded 16?
int step_index = stream->adpcm_step_index; int step_index = stream->adpcm_step_index;
//external interleave /* external interleave (configurable size), mono */
//normal header /* normal header (hist+step+reserved), single channel */
if (first_sample == 0) { if (first_sample == 0) {
off_t header_offset = stream->offset; off_t header_offset = stream->offset;
hist1 = read_16bitLE(header_offset,stream->streamfile); hist1 = read_16bitLE(header_offset,stream->streamfile);
step_index = read_16bitLE(header_offset+2,stream->streamfile); step_index = read_16bitLE(header_offset+2,stream->streamfile);
if (step_index < 0) step_index=0; /* probably pre-adjusted */
//todo clip step_index? if (step_index > 88) step_index=88;
} }
/* decode nibbles (layout: all nibbles from the channel) */
for (i=first_sample,sample_count=0; i<first_sample+samples_to_do; i++,sample_count+=channelspacing) { for (i=first_sample,sample_count=0; i<first_sample+samples_to_do; i++,sample_count+=channelspacing) {
off_t byte_offset = stream->offset + 4 + i/2; off_t byte_offset = stream->offset + 0x04 + i/2;
int nibble_shift = (i&1?4:0); //low nibble first int nibble_shift = (i&1?4:0); /* low nibble first */
//todo waveform has minor deviations using known expands
std_ima_expand_nibble(stream, byte_offset,nibble_shift, &hist1, &step_index); std_ima_expand_nibble(stream, byte_offset,nibble_shift, &hist1, &step_index);
outbuf[sample_count] = (short)(hist1); outbuf[sample_count] = (short)(hist1);
} }
stream->adpcm_history1_16 = hist1; stream->adpcm_history1_32 = hist1;
stream->adpcm_step_index = step_index; stream->adpcm_step_index = step_index;
} }
void decode_dat4_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) { void decode_dat4_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) {
int i, sample_count; int i, sample_count;
int32_t hist1 = stream->adpcm_history1_16;//todo unneeded 16? int32_t hist1 = stream->adpcm_history1_16;//todo unneeded 16?
int step_index = stream->adpcm_step_index; int step_index = stream->adpcm_step_index;
@ -548,7 +550,6 @@ void decode_dat4_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspac
void decode_rad_ima(VGMSTREAM * vgmstream,VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do,int channel) { void decode_rad_ima(VGMSTREAM * vgmstream,VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do,int channel) {
int i, sample_count; int i, sample_count;
int32_t hist1 = stream->adpcm_history1_32; int32_t hist1 = stream->adpcm_history1_32;
int step_index = stream->adpcm_step_index; int step_index = stream->adpcm_step_index;
@ -583,7 +584,6 @@ void decode_rad_ima(VGMSTREAM * vgmstream,VGMSTREAMCHANNEL * stream, sample * ou
void decode_rad_ima_mono(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) { void decode_rad_ima_mono(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) {
int i, sample_count; int i, sample_count;
int32_t hist1 = stream->adpcm_history1_32; int32_t hist1 = stream->adpcm_history1_32;
int step_index = stream->adpcm_step_index; int step_index = stream->adpcm_step_index;
@ -647,91 +647,99 @@ void decode_apple_ima4(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelsp
/* XBOX-IMA with modified data layout */ /* XBOX-IMA with modified data layout */
void decode_fsb_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do,int channel) { void decode_fsb_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do,int channel) {
int i, sample_count; int i, sample_count = 0;
int32_t hist1 = stream->adpcm_history1_32; int32_t hist1 = stream->adpcm_history1_32;
int step_index = stream->adpcm_step_index; int step_index = stream->adpcm_step_index;
//internal interleave /* internal interleave (configurable size), mixed channels */
int block_samples = (0x24 - 4) * 2; /* block size - header, 2 samples per byte */ int block_samples = (0x24 - 0x4) * 2;
first_sample = first_sample % block_samples; first_sample = first_sample % block_samples;
//interleaved header (all hist per channel + all step_index per channel) /* interleaved header (all hist per channel + all step_index+reserved per channel) */
if (first_sample == 0) { if (first_sample == 0) {
off_t hist_offset = stream->offset + 2*channel; off_t hist_offset = stream->offset + 0x02*channel + 0x00;
off_t step_offset = stream->offset + 2*channel + 2*vgmstream->channels; off_t step_offset = stream->offset + 0x02*channel + 0x02*vgmstream->channels;
hist1 = read_16bitLE(hist_offset,stream->streamfile); hist1 = read_16bitLE(hist_offset,stream->streamfile);
step_index = read_8bit(step_offset,stream->streamfile); step_index = read_8bit(step_offset,stream->streamfile);
if (step_index < 0) step_index=0; if (step_index < 0) step_index=0;
if (step_index > 88) step_index=88; if (step_index > 88) step_index=88;
}
for (i=first_sample,sample_count=0; i<first_sample+samples_to_do; i++,sample_count+=channelspacing) { /* write header sample (even samples per block, skips last nibble) */
off_t byte_offset = stream->offset + 4*vgmstream->channels + 2*channel + i/4*2*vgmstream->channels + (i%4)/2;//2-byte per channel
int nibble_shift = (i&1?4:0); //low nibble first
std_ima_expand_nibble(stream, byte_offset,nibble_shift, &hist1, &step_index);
outbuf[sample_count] = (short)(hist1);
}
//internal interleave: increment offset on complete frame
if (i == block_samples) stream->offset += 36*vgmstream->channels;
stream->adpcm_history1_32 = hist1;
stream->adpcm_step_index = step_index;
}
/* XBOX-IMA with modified data layout */
void decode_wwise_ima(VGMSTREAM * vgmstream,VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) {
int i, sample_count = 0;
int32_t hist1 = stream->adpcm_history1_32;
int step_index = stream->adpcm_step_index;
//internal interleave (configurable size), block-interleave multichannel (ex. if block is 0xD8 in 6ch: 6 blocks of 4+0x20)
int block_samples = (vgmstream->interleave_block_size - 4*vgmstream->channels) * 2 / vgmstream->channels;
first_sample = first_sample % block_samples;
//block-interleaved header (1 header per channel block); can be LE or BE
if (first_sample == 0) {
int16_t (*read_16bit)(off_t,STREAMFILE*) = vgmstream->codec_endian ? read_16bitBE : read_16bitLE;
off_t header_offset = stream->offset + (vgmstream->interleave_block_size / vgmstream->channels)*channel;
hist1 = read_16bit(header_offset,stream->streamfile);
step_index = read_8bit(header_offset+2,stream->streamfile);
if (step_index < 0) step_index=0;
if (step_index > 88) step_index=88;
//must write history from header as last nibble/sample in block is almost always 0 / not encoded
outbuf[sample_count] = (short)(hist1); outbuf[sample_count] = (short)(hist1);
sample_count += channelspacing; sample_count += channelspacing;
first_sample += 1; first_sample += 1;
samples_to_do -= 1; samples_to_do -= 1;
} }
for (i=first_sample; i < first_sample + samples_to_do; i++) { /* first_sample + samples_to_do should be block_samples at most */ /* decode nibbles (layout: 2 bytes/2*2 nibbles per channel) */
off_t byte_offset = stream->offset + (vgmstream->interleave_block_size / vgmstream->channels)*channel + 4 + (i-1)/2; for (i = first_sample; i < first_sample + samples_to_do; i++) {
int nibble_shift = ((i-1)&1?4:0); //low nibble first off_t byte_offset = stream->offset + 0x04*vgmstream->channels + 0x02*channel + (i-1)/4*2*vgmstream->channels + ((i-1)%4)/2;
int nibble_shift = ((i-1)&1?4:0); /* low nibble first */
//last nibble/sample in block is ignored (next header sample contains it) /* must skip last nibble per official decoder, probably not needed though */
if (i < block_samples) { if (i < block_samples) {
std_ima_expand_nibble(stream, byte_offset,nibble_shift, &hist1, &step_index); std_ima_expand_nibble(stream, byte_offset,nibble_shift, &hist1, &step_index);
outbuf[sample_count] = (short)(hist1); outbuf[sample_count] = (short)(hist1);
sample_count+=channelspacing; sample_count += channelspacing;
} }
} }
//internal interleave: increment offset on complete frame /* internal interleave: increment offset on complete frame */
if (i == block_samples) stream->offset += vgmstream->interleave_block_size; if (i == block_samples) {
stream->offset += 0x24*vgmstream->channels;
}
stream->adpcm_history1_32 = hist1; stream->adpcm_history1_32 = hist1;
stream->adpcm_step_index = step_index; stream->adpcm_step_index = step_index;
} }
//todo atenuation: apparently from hcs's analysis Wwise IMA expands nibbles slightly different, reducing clipping/dbs
/* /* mono XBOX-IMA with header endianness and alt nibble expand (per hcs's decompilation) */
From Wwise_v2015.1.6_Build5553_SDK.Linux void decode_wwise_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) {
<_ZN13CAkADPCMCodec12DecodeSampleEiii>: int i, sample_count = 0, num_frame;
int32_t hist1 = stream->adpcm_history1_32;
int step_index = stream->adpcm_step_index;
/* external interleave (fixed size), mono */
int block_samples = (0x24 - 0x4) * 2;
num_frame = first_sample / block_samples;
first_sample = first_sample % block_samples;
/* normal header (hist+step+reserved), single channel */
if (first_sample == 0) {
int16_t (*read_16bit)(off_t,STREAMFILE*) = vgmstream->codec_endian ? read_16bitBE : read_16bitLE;
off_t header_offset = stream->offset + 0x24*num_frame;
hist1 = read_16bit(header_offset,stream->streamfile);
step_index = read_8bit(header_offset+2,stream->streamfile);
if (step_index < 0) step_index=0;
if (step_index > 88) step_index=88;
/* write header sample (even samples per block, skips last nibble) */
outbuf[sample_count] = (short)(hist1);
sample_count += channelspacing;
first_sample += 1;
samples_to_do -= 1;
}
/* decode nibbles (layout: all nibbles from one channel) */
for (i = first_sample; i < first_sample + samples_to_do; i++) {
off_t byte_offset = (stream->offset + 0x24*num_frame + 0x4) + (i-1)/2;
int nibble_shift = ((i-1)&1?4:0); /* low nibble first */
/* must skip last nibble like other XBOX-IMAs, often needed (ex. Bayonetta 2 sfx) */
if (i < block_samples) {
std_ima_expand_nibble_mul(stream, byte_offset,nibble_shift, &hist1, &step_index);
outbuf[sample_count] = (short)(hist1);
sample_count += channelspacing;
}
}
stream->adpcm_history1_32 = hist1;
stream->adpcm_step_index = step_index;
}
/* from hcs's analysis Wwise IMA expands nibbles slightly different, reducing dbs. Just "MUL" expand?
<_ZN13CAkADPCMCodec12DecodeSampleEiii>: //From Wwise_v2015.1.6_Build5553_SDK.Linux
10: 83 e0 07 and $0x7,%eax ; sample 10: 83 e0 07 and $0x7,%eax ; sample
13: 01 c0 add %eax,%eax ; sample*2 13: 01 c0 add %eax,%eax ; sample*2
15: 83 c0 01 add $0x1,%eax ; sample*2+1 15: 83 c0 01 add $0x1,%eax ; sample*2+1
@ -740,8 +748,6 @@ From Wwise_v2015.1.6_Build5553_SDK.Linux
1f: 85 c0 test %eax,%eax ; result negative? 1f: 85 c0 test %eax,%eax ; result negative?
21: 0f 48 c2 cmovs %edx,%eax ; adjust if negative to fix rounding for below division 21: 0f 48 c2 cmovs %edx,%eax ; adjust if negative to fix rounding for below division
24: c1 f8 03 sar $0x3,%eax ; (sample*2+1)*scale/8 24: c1 f8 03 sar $0x3,%eax ; (sample*2+1)*scale/8
Different rounding model vs IMA's shift-and-add (also "adjust" step may be unnecessary).
*/ */
/* MS-IMA with possibly the XBOX-IMA model of even number of samples per block (more tests are needed) */ /* MS-IMA with possibly the XBOX-IMA model of even number of samples per block (more tests are needed) */
@ -832,7 +838,7 @@ void decode_ubi_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspaci
(!(i%2) ? 4:0) : /* mono mode (high first) */ (!(i%2) ? 4:0) : /* mono mode (high first) */
(channel==0 ? 4:0); /* stereo mode (high=L,low=R) */ (channel==0 ? 4:0); /* stereo mode (high=L,low=R) */
ubi_ima_expand_nibble(stream, byte_offset,nibble_shift, &hist1, &step_index); std_ima_expand_nibble_mul(stream, byte_offset,nibble_shift, &hist1, &step_index);
outbuf[sample_count] = (short)(hist1); /* all samples are written */ outbuf[sample_count] = (short)(hist1); /* all samples are written */
} }
@ -849,7 +855,7 @@ size_t ima_bytes_to_samples(size_t bytes, int channels) {
} }
size_t ms_ima_bytes_to_samples(size_t bytes, int block_align, int channels) { size_t ms_ima_bytes_to_samples(size_t bytes, int block_align, int channels) {
/* MS IMA blocks have a 4 byte header per channel; 2 samples per byte (2 nibbles) */ /* MS-IMA blocks have a 4 byte header per channel; 2 samples per byte (2 nibbles) */
return (bytes / block_align) * ((block_align - 0x04*channels) * 2 / channels + 1) return (bytes / block_align) * ((block_align - 0x04*channels) * 2 / channels + 1)
+ ((bytes % block_align) ? (((bytes % block_align) - 0x04*channels) * 2 / channels + 1) : 0); + ((bytes % block_align) ? (((bytes % block_align) - 0x04*channels) * 2 / channels + 1) : 0);
} }
@ -858,7 +864,7 @@ size_t xbox_ima_bytes_to_samples(size_t bytes, int channels) {
int block_align = 0x24 * channels; int block_align = 0x24 * channels;
/* XBOX IMA blocks have a 4 byte header per channel; 2 samples per byte (2 nibbles) */ /* XBOX IMA blocks have a 4 byte header per channel; 2 samples per byte (2 nibbles) */
return (bytes / block_align) * (block_align - 4 * channels) * 2 / channels return (bytes / block_align) * (block_align - 4 * channels) * 2 / channels
+ ((bytes % block_align) ? ((bytes % block_align) - 4 * channels) * 2 / channels : 0); //todo probably not possible (aligned) + ((bytes % block_align) ? ((bytes % block_align) - 4 * channels) * 2 / channels : 0); /* unlikely (encoder aligns) */
} }
size_t ubi_ima_bytes_to_samples(size_t bytes, int channels, STREAMFILE *streamFile, off_t offset) { size_t ubi_ima_bytes_to_samples(size_t bytes, int channels, STREAMFILE *streamFile, off_t offset) {

View File

@ -105,6 +105,7 @@ static const char* extension_list[] = {
"dvi", "dvi",
"dxh", "dxh",
"e4x",
"eam", "eam",
"emff", "emff",
"enth", "enth",
@ -379,7 +380,7 @@ static const char* extension_list[] = {
"xss", "xss",
"xvag", "xvag",
"xvas", "xvas",
"xwav", "xwav",//fake, to be removed
"xwb", "xwb",
"xwc", "xwc",
"xwm", //FFmpeg, not parsed (XWMA) "xwm", //FFmpeg, not parsed (XWMA)
@ -495,6 +496,7 @@ static const coding_info coding_info_list[] = {
{coding_MTAF, "Konami MTAF 4-bit ADPCM"}, {coding_MTAF, "Konami MTAF 4-bit ADPCM"},
{coding_MTA2, "Konami MTA2 4-bit ADPCM"}, {coding_MTA2, "Konami MTA2 4-bit ADPCM"},
{coding_MC3, "Paradigm MC3 3-bit ADPCM"}, {coding_MC3, "Paradigm MC3 3-bit ADPCM"},
{coding_FADPCM, "FMOD FADPCM 4-bit ADCPM"},
{coding_SDX2, "Squareroot-delta-exact (SDX2) 8-bit DPCM"}, {coding_SDX2, "Squareroot-delta-exact (SDX2) 8-bit DPCM"},
{coding_SDX2_int, "Squareroot-delta-exact (SDX2) 8-bit DPCM with 1 byte interleave"}, {coding_SDX2_int, "Squareroot-delta-exact (SDX2) 8-bit DPCM with 1 byte interleave"},
@ -565,13 +567,13 @@ static const layout_info layout_info_list[] = {
{layout_thp_blocked, "THP Movie Audio blocked"}, {layout_thp_blocked, "THP Movie Audio blocked"},
{layout_filp_blocked, "FILp blocked"}, {layout_filp_blocked, "FILp blocked"},
{layout_blocked_ea_swvr, "blocked (EA SWVR)"}, {layout_blocked_ea_swvr, "blocked (EA SWVR)"},
{layout_ps2_adm_blocked, "ADM blocked"}, {layout_blocked_adm, "blocked (ADM)"},
{layout_dsp_bdsp_blocked, "DSP blocked"}, {layout_dsp_bdsp_blocked, "DSP blocked"},
{layout_blocked_ivaud, "blocked (IVAUD)"}, {layout_blocked_ivaud, "blocked (IVAUD)"},
{layout_ps2_iab_blocked, "IAB blocked"}, {layout_ps2_iab_blocked, "IAB blocked"},
{layout_ps2_strlr_blocked, "The Bouncer STR blocked"}, {layout_ps2_strlr_blocked, "The Bouncer STR blocked"},
{layout_rws_blocked, "RWS blocked"}, {layout_blocked_rws, "blocked (RWS)"},
{layout_hwas_blocked, "HWAS blocked"}, {layout_blocked_hwas, "blocked (HWAS)"},
{layout_tra_blocked, "TRA blocked"}, {layout_tra_blocked, "TRA blocked"},
{layout_acm, "ACM blocked"}, {layout_acm, "ACM blocked"},
{layout_mus_acm, "multiple ACM files, ACM blocked"}, {layout_mus_acm, "multiple ACM files, ACM blocked"},
@ -642,7 +644,6 @@ static const meta_info meta_info_list[] = {
{meta_PS2_ILD, "ILD header"}, {meta_PS2_ILD, "ILD header"},
{meta_PS2_PNB, "assumed PNB (PsychoNauts Bgm File) by .pnb extension"}, {meta_PS2_PNB, "assumed PNB (PsychoNauts Bgm File) by .pnb extension"},
{meta_XBOX_WAVM, "Xbox WAVM raw header"}, {meta_XBOX_WAVM, "Xbox WAVM raw header"},
{meta_XBOX_RIFF, "Microsoft XWAV RIFF header"},
{meta_DSP_STR, "assumed Conan Gamecube STR File by .str extension"}, {meta_DSP_STR, "assumed Conan Gamecube STR File by .str extension"},
{meta_EA_SCHL, "Electronic Arts SCHl header (variable)"}, {meta_EA_SCHL, "Electronic Arts SCHl header (variable)"},
{meta_EA_SCHL_fixed, "Electronic Arts SCHl header (fixed)"}, {meta_EA_SCHL_fixed, "Electronic Arts SCHl header (fixed)"},
@ -700,6 +701,7 @@ static const meta_info meta_info_list[] = {
{meta_PS2_PSH, "Dawn of Mana - Seiken Densetsu 4 PSH Header"}, {meta_PS2_PSH, "Dawn of Mana - Seiken Densetsu 4 PSH Header"},
{meta_RIFF_WAVE_labl, "RIFF WAVE header with loop markers"}, {meta_RIFF_WAVE_labl, "RIFF WAVE header with loop markers"},
{meta_RIFF_WAVE_smpl, "RIFF WAVE header with sample looping info"}, {meta_RIFF_WAVE_smpl, "RIFF WAVE header with sample looping info"},
{meta_RIFF_WAVE_wsmp, "RIFF WAVE header with wsmp looping info"},
{meta_RIFX_WAVE, "RIFX WAVE header"}, {meta_RIFX_WAVE, "RIFX WAVE header"},
{meta_RIFX_WAVE_smpl, "RIFX WAVE header with sample looping info"}, {meta_RIFX_WAVE_smpl, "RIFX WAVE header with sample looping info"},
{meta_XNB, "Microsoft XNA Game Studio 4.0 header"}, {meta_XNB, "Microsoft XNA Game Studio 4.0 header"},
@ -844,6 +846,7 @@ static const meta_info meta_info_list[] = {
{meta_DSP_XIII, "XIII dsp header"}, {meta_DSP_XIII, "XIII dsp header"},
{meta_NGC_DSP_STH_STR, "STH dsp header"}, {meta_NGC_DSP_STH_STR, "STH dsp header"},
{meta_DSP_CABELAS, "Cabelas games dsp header"}, {meta_DSP_CABELAS, "Cabelas games dsp header"},
{meta_PS2_ADM, "Dragon Quest V .ADM raw header"},
{meta_PS2_LPCM, "LPCM header"}, {meta_PS2_LPCM, "LPCM header"},
{meta_PS2_VMS, "VMS Header"}, {meta_PS2_VMS, "VMS Header"},
{meta_XAU, "XPEC XAU header"}, {meta_XAU, "XPEC XAU header"},

View File

@ -131,8 +131,8 @@ void render_vgmstream_blocked(sample * buffer, int32_t sample_count, VGMSTREAM *
case layout_blocked_ea_swvr: case layout_blocked_ea_swvr:
block_update_ea_swvr(vgmstream->next_block_offset,vgmstream); block_update_ea_swvr(vgmstream->next_block_offset,vgmstream);
break; break;
case layout_ps2_adm_blocked: case layout_blocked_adm:
ps2_adm_block_update(vgmstream->next_block_offset,vgmstream); block_update_adm(vgmstream->next_block_offset,vgmstream);
break; break;
case layout_dsp_bdsp_blocked: case layout_dsp_bdsp_blocked:
dsp_bdsp_block_update(vgmstream->next_block_offset,vgmstream); dsp_bdsp_block_update(vgmstream->next_block_offset,vgmstream);
@ -146,11 +146,11 @@ void render_vgmstream_blocked(sample * buffer, int32_t sample_count, VGMSTREAM *
case layout_ps2_strlr_blocked: case layout_ps2_strlr_blocked:
ps2_strlr_block_update(vgmstream->next_block_offset,vgmstream); ps2_strlr_block_update(vgmstream->next_block_offset,vgmstream);
break; break;
case layout_rws_blocked: case layout_blocked_rws:
rws_block_update(vgmstream->next_block_offset,vgmstream); block_update_rws(vgmstream->next_block_offset,vgmstream);
break; break;
case layout_hwas_blocked: case layout_blocked_hwas:
hwas_block_update(vgmstream->next_block_offset,vgmstream); block_update_hwas(vgmstream->next_block_offset,vgmstream);
break; break;
case layout_blocked_ea_sns: case layout_blocked_ea_sns:
block_update_ea_sns(vgmstream->next_block_offset,vgmstream); block_update_ea_sns(vgmstream->next_block_offset,vgmstream);

48
src/layout/blocked_adm.c Normal file
View File

@ -0,0 +1,48 @@
#include "layout.h"
#include "../vgmstream.h"
/* blocks of 0x1000 with interleave 0x400 but also smaller last interleave */
void block_update_adm(off_t block_offset, VGMSTREAM * vgmstream) {
STREAMFILE* streamFile = vgmstream->ch[0].streamfile;
int i, new_full_block;
size_t block_size, interleave_size, interleave_data;
/* no header */
interleave_size = 0x400;
interleave_data = 0x400;
block_size = interleave_size * vgmstream->channels;
/* every 0x1000 is a full block, signaled by PS-ADPCM flags */
new_full_block = (read_8bit(block_offset+0x01, streamFile) == 0x06);
/* try to autodetect usable interleave data size as can be smaller when a discrete block ends (ex. 0x10~0x50, varies with file) */
if (!new_full_block) {
off_t next_block_offset = block_offset + block_size;
while (next_block_offset > block_offset) {
next_block_offset -= 0x10;
/* check if unused line (all blocks should only use flags 0x06/0x03/0x02) */
if (read_32bitLE(next_block_offset, streamFile) == 0x00000000) {
interleave_data -= 0x10;
next_block_offset -= 0x10;
}
else {
break;
}
}
}
vgmstream->current_block_offset = block_offset;
vgmstream->next_block_offset = block_offset + block_size;
vgmstream->current_block_size = interleave_data;
for (i = 0; i < vgmstream->channels; i++) {
vgmstream->ch[i].offset = block_offset + interleave_size*i;
//if (new_full_block) { /* blocks are not discrete */
// vgmstream->ch[i].adpcm_history1_32 = 0;
// vgmstream->ch[i].adpcm_step_index = 0;
//}
}
}

View File

@ -2,7 +2,7 @@
#include "../vgmstream.h" #include "../vgmstream.h"
/* a simple headerless block with special adpcm history handling */ /* a simple headerless block with special adpcm history handling */
void hwas_block_update(off_t block_offset, VGMSTREAM * vgmstream) { void block_update_hwas(off_t block_offset, VGMSTREAM * vgmstream) {
int i; int i;
size_t block_size; size_t block_size;

View File

@ -2,7 +2,7 @@
#include "../vgmstream.h" #include "../vgmstream.h"
/* a simple headerless block with padding; configured in the main header */ /* a simple headerless block with padding; configured in the main header */
void rws_block_update(off_t block_offset, VGMSTREAM * vgmstream) { void block_update_rws(off_t block_offset, VGMSTREAM * vgmstream) {
int i; int i;
size_t block_size; size_t block_size;
size_t interleave; size_t interleave;

View File

@ -45,10 +45,8 @@ void thp_block_update(off_t block_offset, VGMSTREAM * vgmstream);
void filp_block_update(off_t block_offset, VGMSTREAM * vgmstream); void filp_block_update(off_t block_offset, VGMSTREAM * vgmstream);
void block_update_ivaud(off_t block_offset, VGMSTREAM * vgmstream); void block_update_ivaud(off_t block_offset, VGMSTREAM * vgmstream);
void block_update_ea_swvr(off_t block_offset, VGMSTREAM * vgmstream); void block_update_ea_swvr(off_t block_offset, VGMSTREAM * vgmstream);
void block_update_adm(off_t block_offset, VGMSTREAM * vgmstream);
void ps2_adm_block_update(off_t block_offset, VGMSTREAM * vgmstream);
void dsp_bdsp_block_update(off_t block_offset, VGMSTREAM * vgmstream); void dsp_bdsp_block_update(off_t block_offset, VGMSTREAM * vgmstream);
@ -58,10 +56,8 @@ void ps2_iab_block_update(off_t block_offset, VGMSTREAM * vgmstream);
void ps2_strlr_block_update(off_t block_offset, VGMSTREAM * vgmstream); void ps2_strlr_block_update(off_t block_offset, VGMSTREAM * vgmstream);
void rws_block_update(off_t block_offset, VGMSTREAM * vgmstream); void block_update_rws(off_t block_offset, VGMSTREAM * vgmstream);
void block_update_hwas(off_t block_offset, VGMSTREAM * vgmstream);
void hwas_block_update(off_t block_offset, VGMSTREAM * vgmstream);
void block_update_ea_sns(off_t block_offset, VGMSTREAM * vgmstream); void block_update_ea_sns(off_t block_offset, VGMSTREAM * vgmstream);
void block_update_awc(off_t block_offset, VGMSTREAM * vgmstream); void block_update_awc(off_t block_offset, VGMSTREAM * vgmstream);
void block_update_vgs(off_t block_offset, VGMSTREAM * vgmstream); void block_update_vgs(off_t block_offset, VGMSTREAM * vgmstream);

View File

@ -1,18 +0,0 @@
#include "layout.h"
#include "../vgmstream.h"
/* set up for the block at the given offset */
void ps2_adm_block_update(off_t block_offset, VGMSTREAM * vgmstream) {
int i;
vgmstream->current_block_offset = block_offset;
vgmstream->current_block_size = 0x1000; /*read_32bitLE(
vgmstream->current_block_offset+0x10,
vgmstream->ch[0].streamfile); */
vgmstream->next_block_offset = vgmstream->current_block_offset + vgmstream->current_block_size;
//vgmstream->current_block_size/=vgmstream->channels;
for (i=0;i<vgmstream->channels;i++) {
vgmstream->ch[i].offset = vgmstream->current_block_offset+(0x400*i);
}
}

View File

@ -1366,10 +1366,6 @@
RelativePath=".\meta\xbox_xvas.c" RelativePath=".\meta\xbox_xvas.c"
> >
</File> </File>
<File
RelativePath=".\meta\xbox_xwav.c"
>
</File>
<File <File
RelativePath=".\meta\xma.c" RelativePath=".\meta\xma.c"
> >
@ -1482,6 +1478,10 @@
RelativePath=".\coding\ea_xas_decoder.c" RelativePath=".\coding\ea_xas_decoder.c"
> >
</File> </File>
<File
RelativePath=".\coding\fadpcm_decoder.c"
>
</File>
<File <File
RelativePath=".\coding\ffmpeg_decoder.c" RelativePath=".\coding\ffmpeg_decoder.c"
> >
@ -1759,7 +1759,7 @@
> >
</File> </File>
<File <File
RelativePath=".\layout\ps2_adm_blocked.c" RelativePath=".\layout\blocked_adm.c"
> >
</File> </File>
<File <File
@ -1775,11 +1775,11 @@
> >
</File> </File>
<File <File
RelativePath=".\layout\rws_blocked.c" RelativePath=".\layout\blocked_rws.c"
> >
</File> </File>
<File <File
RelativePath=".\layout\hwas_blocked.c" RelativePath=".\layout\blocked_hwas.c"
> >
</File> </File>
<File <File

View File

@ -413,7 +413,6 @@
<ClCompile Include="meta\xbox_wavm.c" /> <ClCompile Include="meta\xbox_wavm.c" />
<ClCompile Include="meta\xbox_xmu.c" /> <ClCompile Include="meta\xbox_xmu.c" />
<ClCompile Include="meta\xbox_xvas.c" /> <ClCompile Include="meta\xbox_xvas.c" />
<ClCompile Include="meta\xbox_xwav.c" />
<ClCompile Include="meta\x360_pasx.c" /> <ClCompile Include="meta\x360_pasx.c" />
<ClCompile Include="meta\xma.c" /> <ClCompile Include="meta\xma.c" />
<ClCompile Include="meta\xnb.c" /> <ClCompile Include="meta\xnb.c" />
@ -430,6 +429,7 @@
<ClCompile Include="coding\ea_mt_decoder.c" /> <ClCompile Include="coding\ea_mt_decoder.c" />
<ClCompile Include="coding\ea_xa_decoder.c" /> <ClCompile Include="coding\ea_xa_decoder.c" />
<ClCompile Include="coding\ea_xas_decoder.c" /> <ClCompile Include="coding\ea_xas_decoder.c" />
<ClCompile Include="coding\fadpcm_decoder.c" />
<ClCompile Include="coding\g719_decoder.c" /> <ClCompile Include="coding\g719_decoder.c" />
<ClCompile Include="coding\g721_decoder.c" /> <ClCompile Include="coding\g721_decoder.c" />
<ClCompile Include="coding\g7221_decoder.c" /> <ClCompile Include="coding\g7221_decoder.c" />
@ -486,10 +486,10 @@
<ClCompile Include="layout\mus_acm_layout.c" /> <ClCompile Include="layout\mus_acm_layout.c" />
<ClCompile Include="layout\mxch_blocked.c" /> <ClCompile Include="layout\mxch_blocked.c" />
<ClCompile Include="layout\nolayout.c" /> <ClCompile Include="layout\nolayout.c" />
<ClCompile Include="layout\ps2_adm_blocked.c" /> <ClCompile Include="layout\blocked_adm.c" />
<ClCompile Include="layout\blocked_ea_swvr.c" /> <ClCompile Include="layout\blocked_ea_swvr.c" />
<ClCompile Include="layout\rws_blocked.c" /> <ClCompile Include="layout\blocked_rws.c" />
<ClCompile Include="layout\hwas_blocked.c" /> <ClCompile Include="layout\blocked_hwas.c" />
<ClCompile Include="layout\str_snds_blocked.c" /> <ClCompile Include="layout\str_snds_blocked.c" />
<ClCompile Include="layout\thp_blocked.c" /> <ClCompile Include="layout\thp_blocked.c" />
<ClCompile Include="layout\vs_blocked.c" /> <ClCompile Include="layout\vs_blocked.c" />

View File

@ -832,9 +832,6 @@
<ClCompile Include="meta\xbox_xvas.c"> <ClCompile Include="meta\xbox_xvas.c">
<Filter>meta\Source Files</Filter> <Filter>meta\Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="meta\xbox_xwav.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\xss.c"> <ClCompile Include="meta\xss.c">
<Filter>meta\Source Files</Filter> <Filter>meta\Source Files</Filter>
</ClCompile> </ClCompile>
@ -874,6 +871,9 @@
<ClCompile Include="coding\ea_xas_decoder.c"> <ClCompile Include="coding\ea_xas_decoder.c">
<Filter>coding\Source Files</Filter> <Filter>coding\Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="coding\fadpcm_decoder.c">
<Filter>coding\Source Files</Filter>
</ClCompile>
<ClCompile Include="coding\g721_decoder.c"> <ClCompile Include="coding\g721_decoder.c">
<Filter>coding\Source Files</Filter> <Filter>coding\Source Files</Filter>
</ClCompile> </ClCompile>
@ -1021,7 +1021,7 @@
<ClCompile Include="layout\halpst_blocked.c"> <ClCompile Include="layout\halpst_blocked.c">
<Filter>layout\Source Files</Filter> <Filter>layout\Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="layout\hwas_blocked.c"> <ClCompile Include="layout\blocked_hwas.c">
<Filter>layout\Source Files</Filter> <Filter>layout\Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="layout\ims_block.c"> <ClCompile Include="layout\ims_block.c">
@ -1042,13 +1042,13 @@
<ClCompile Include="layout\nolayout.c"> <ClCompile Include="layout\nolayout.c">
<Filter>layout\Source Files</Filter> <Filter>layout\Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="layout\ps2_adm_blocked.c"> <ClCompile Include="layout\blocked_adm.c">
<Filter>layout\Source Files</Filter> <Filter>layout\Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="layout\blocked_ea_swvr.c"> <ClCompile Include="layout\blocked_ea_swvr.c">
<Filter>layout\Source Files</Filter> <Filter>layout\Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="layout\rws_blocked.c"> <ClCompile Include="layout\blocked_rws.c">
<Filter>layout\Source Files</Filter> <Filter>layout\Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="layout\str_snds_blocked.c"> <ClCompile Include="layout\str_snds_blocked.c">

View File

@ -110,7 +110,7 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) {
break; break;
case 0x03: /* Loop Info */ case 0x03: /* Loop Info */
LoopStart = read_32bitLE(ExtraFlagStart+0x04,streamFile); LoopStart = read_32bitLE(ExtraFlagStart+0x04,streamFile);
if (ExtraFlagSize > 0x04) /* probably no needed */ if (ExtraFlagSize > 0x04) /* probably not needed */
LoopEnd = read_32bitLE(ExtraFlagStart+0x08,streamFile); LoopEnd = read_32bitLE(ExtraFlagStart+0x08,streamFile);
/* when start is 0 seems the song repeats with no real looping (ex. Sonic Boom Fire & Ice jingles) */ /* when start is 0 seems the song repeats with no real looping (ex. Sonic Boom Fire & Ice jingles) */
@ -118,9 +118,9 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) {
break; break;
case 0x04: /* free comment, or maybe SFX info */ case 0x04: /* free comment, or maybe SFX info */
break; break;
//case 0x05: /* Unknown (32b) */ //case 0x05: /* Unknown (32b) */
// /* found in Tearaway Vita, value 0, first stream only */ // /* found in Tearaway Vita, value 0, first stream only */
// break; // break;
case 0x06: /* XMA seek table */ case 0x06: /* XMA seek table */
/* no need for it */ /* no need for it */
break; break;
@ -142,11 +142,11 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) {
* (xN entries) * (xN entries)
*/ */
break; break;
//case 0x0d: /* Unknown (32b) */ //case 0x0d: /* Unknown (32b) */
// /* found in some XMA2 and Vorbis */ // /* found in some XMA2/Vorbis/FADPCM */
// break; // break;
default: default:
VGM_LOG("FSB5: unknown extra flag 0x%x at 0x%04x (size 0x%x)\n", ExtraFlagType, ExtraFlagStart, ExtraFlagSize); VGM_LOG("FSB5: unknown extra flag 0x%x at 0x%04x + 0x04 (size 0x%x)\n", ExtraFlagType, ExtraFlagStart, ExtraFlagSize);
break; break;
} }
@ -336,7 +336,10 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) {
#endif #endif
case 0x10: /* FMOD_SOUND_FORMAT_FADPCM */ case 0x10: /* FMOD_SOUND_FORMAT_FADPCM */
goto fail; vgmstream->coding_type = coding_FADPCM;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = 0x8c;
break;
default: default:
goto fail; goto fail;

View File

@ -63,6 +63,9 @@ static const uint8_t key_inv[] = { 0x6D,0x69,0x6E,0x74,0x37,0x38,0x72,0x75,0x6E,
/* Guitar Hero 3 */ //"5atu6w4zaw" /* Guitar Hero 3 */ //"5atu6w4zaw"
static const uint8_t key_gh3[] = { 0x35,0x61,0x74,0x75,0x36,0x77,0x34,0x7A,0x61,0x77 }; static const uint8_t key_gh3[] = { 0x35,0x61,0x74,0x75,0x36,0x77,0x34,0x7A,0x61,0x77 };
/* Supreme Commander 2 */ //"B2A7BB00"
static const uint8_t key_sc2[] = { 0x42,0x32,0x41,0x37,0x42,0x42,0x30,0x30 };
// Unknown: // Unknown:
// - Battle: Los Angeles // - Battle: Los Angeles
// - Guitar Hero: Warriors of Rock, DJ hero FSB // - Guitar Hero: Warriors of Rock, DJ hero FSB
@ -118,6 +121,10 @@ static const fsbkey_info fsbkey_list[] = {
{ 0,1, sizeof(key_gh3),key_gh3 },//untested { 0,1, sizeof(key_gh3),key_gh3 },//untested
{ 1,0, sizeof(key_gh3),key_gh3 },//untested { 1,0, sizeof(key_gh3),key_gh3 },//untested
{ 1,1, sizeof(key_gh3),key_gh3 },//untested { 1,1, sizeof(key_gh3),key_gh3 },//untested
{ 0,0, sizeof(key_sc2),key_sc2 },//untested
{ 0,1, sizeof(key_sc2),key_sc2 },//untested
{ 1,0, sizeof(key_sc2),key_sc2 },//untested
{ 1,1, sizeof(key_sc2),key_sc2 },//untested
}; };
static const int fsbkey_list_count = sizeof(fsbkey_list) / sizeof(fsbkey_list[0]); static const int fsbkey_list_count = sizeof(fsbkey_list) / sizeof(fsbkey_list[0]);

View File

@ -76,8 +76,6 @@ VGMSTREAM * init_vgmstream_ps2_pnb(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_xbox_wavm(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_xbox_wavm(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_xbox_xwav(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_ngc_str(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_ngc_str(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_caf(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_caf(STREAMFILE *streamFile);

View File

@ -18,7 +18,7 @@ VGMSTREAM * init_vgmstream_mss(STREAMFILE *streamFile) {
loop_flag = 0; loop_flag = 0;
channel_count = read_16bitLE(0x16,streamFile); channel_count = read_16bitLE(0x16,streamFile);
if (read_32bitLE(0x18,streamFile) == 0x4800 && vgmstream->channels > 2) if (read_32bitLE(0x18,streamFile) == 0x4800 && channel_count > 2)
channel_count = 2; //todo add support for interleave stereo streams channel_count = 2; //todo add support for interleave stereo streams
/* build the VGMSTREAM */ /* build the VGMSTREAM */

View File

@ -34,14 +34,14 @@ VGMSTREAM * init_vgmstream_nds_hwas(STREAMFILE *streamFile) {
vgmstream->meta_type = meta_NDS_HWAS; vgmstream->meta_type = meta_NDS_HWAS;
vgmstream->coding_type = coding_IMA_int; vgmstream->coding_type = coding_IMA_int;
vgmstream->layout_type = layout_hwas_blocked; vgmstream->layout_type = layout_blocked_hwas;
vgmstream->full_block_size = read_32bitLE(0x04,streamFile); /* usually 0x2000, 0x4000 or 0x8000 */ vgmstream->full_block_size = read_32bitLE(0x04,streamFile); /* usually 0x2000, 0x4000 or 0x8000 */
/* open the file for reading by each channel */ /* open the file for reading by each channel */
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
goto fail; goto fail;
hwas_block_update(start_offset, vgmstream); block_update_hwas(start_offset, vgmstream);
return vgmstream; return vgmstream;

View File

@ -1,37 +1,36 @@
#include "meta.h" #include "meta.h"
#include "../coding/coding.h" #include "../coding/coding.h"
/* XA30 - found in Driver: Parallel Lines (PC) */ /* XA30 - found in Reflections games [Driver: Parallel Lines (PC), Driver 3 (PC)] */
VGMSTREAM * init_vgmstream_pc_xa30(STREAMFILE *streamFile) { VGMSTREAM * init_vgmstream_pc_xa30(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL; VGMSTREAM * vgmstream = NULL;
off_t start_offset; off_t start_offset;
int loop_flag, channel_count, codec; int loop_flag, channel_count, codec;
size_t file_size, data_size; size_t stream_size;
int total_subsongs, target_subsong = streamFile->stream_index;
/* check extension, case insensitive */ /* check extension, case insensitive */
/* ".xa30" is just the ID, the real filename should be .XA */ /* ".xa30/e4x" is just the ID, the real filename should be .XA */
if (!check_extensions(streamFile,"xa,xa30")) if (!check_extensions(streamFile,"xa,xa30,e4x"))
goto fail; goto fail;
/* check header */ /* check header */
if (read_32bitBE(0x00,streamFile) != 0x58413330) /* "XA30" */ if (read_32bitBE(0x00,streamFile) != 0x58413330 && /* "XA30" [Driver: Parallel Lines (PC)]*/
read_32bitBE(0x00,streamFile) != 0x65347892) /* "e4x\92" [Driver 3 (PC)]*/
goto fail;
if (read_32bitLE(0x04,streamFile) != 2) /* channels?, also extra check to avoid PS2/PC XA30 mixup */
goto fail; goto fail;
if (read_32bitLE(0x04,streamFile) > 2) goto fail; /* extra check to avoid PS2/PC XA30 mixup */
total_subsongs = read_32bitLE(0x14,streamFile) != 0 ? 2 : 1; /* second stream offset (only in Driver 3) */
/* reportedly from XA2WAV those are offset+data from a second stream (not seen) */ if (target_subsong == 0) target_subsong = 1;
if (read_32bitLE(0x14,streamFile) != 0 || read_32bitLE(0x1c,streamFile) != 0) goto fail; if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail;
loop_flag = 0; loop_flag = 0;
channel_count = 2; /* 0x04: channels? (always 2 in practice) */ channel_count = 2; /* 0x04: channels? (always 2 in practice) */
codec = read_32bitLE(0x0c,streamFile);
codec = read_32bitLE(0x0c,streamFile); /* reportedly from XA2WAV (not seen) */ start_offset = read_32bitLE(0x10 + 0x04*(target_subsong-1),streamFile);
start_offset = read_32bitLE(0x10,streamFile); stream_size = read_32bitLE(0x18 + 0x04*(target_subsong-1),streamFile);
file_size = get_streamfile_size(streamFile);
data_size = read_32bitLE(0x18,streamFile);
if (data_size+start_offset != file_size) goto fail;
/* build the VGMSTREAM */ /* build the VGMSTREAM */
@ -39,19 +38,26 @@ VGMSTREAM * init_vgmstream_pc_xa30(STREAMFILE *streamFile) {
if (!vgmstream) goto fail; if (!vgmstream) goto fail;
vgmstream->sample_rate = read_32bitLE(0x08,streamFile); vgmstream->sample_rate = read_32bitLE(0x08,streamFile);
/* 0x20: always 00016000?, rest of the header is null */ /* 0x20: always IMA=00016000, PCM=00056000 PCM?, rest of the header is null */
vgmstream->num_streams = total_subsongs;
vgmstream->stream_size = stream_size;
vgmstream->meta_type = meta_PC_XA30; vgmstream->meta_type = meta_PC_XA30;
switch(codec) { switch(codec) {
case 0x00: /* PCM (rare, seen in Driver 3) */
vgmstream->coding_type = coding_PCM16LE;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = read_32bitLE(0x24,streamFile) / 2;
vgmstream->num_samples = pcm_bytes_to_samples(stream_size, channel_count, 16);
break;
case 0x01: /* MS-IMA variation */ case 0x01: /* MS-IMA variation */
vgmstream->coding_type = coding_REF_IMA; vgmstream->coding_type = coding_REF_IMA;
vgmstream->layout_type = layout_none; vgmstream->layout_type = layout_none;
vgmstream->interleave_block_size = read_32bitLE(0x24,streamFile); vgmstream->interleave_block_size = read_32bitLE(0x24,streamFile);
vgmstream->num_samples = ms_ima_bytes_to_samples(data_size, vgmstream->interleave_block_size, channel_count); vgmstream->num_samples = ms_ima_bytes_to_samples(stream_size, vgmstream->interleave_block_size, channel_count);
break; break;
case 0x00: /* PCM? */
default: default:
goto fail; goto fail;
} }

View File

@ -1,75 +1,109 @@
#include "meta.h" #include "meta.h"
#include "../layout/layout.h" #include "../layout/layout.h"
#include "../util.h" #include "../coding/coding.h"
#include <string.h>
/* WAD (from The golden Compass) */ static int get_adm_loop_info(STREAMFILE *streamFile, off_t *loop_start_offset);
/* .adm - from Dragon Quest V (PS2) */
VGMSTREAM * init_vgmstream_ps2_adm(STREAMFILE *streamFile) { VGMSTREAM * init_vgmstream_ps2_adm(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL; VGMSTREAM * vgmstream = NULL;
char filename[PATH_LIMIT]; int channel_count, loop_flag = 0;
int loop_flag = 0; off_t start_offset, loop_start_offset = 0;
int channel_count;
int i;
off_t start_offset;
/* check extension, case insensitive */ /* check extension, case insensitive */
streamFile->get_name(streamFile,filename,sizeof(filename)); if (!check_extensions(streamFile,"adm"))
if (strcasecmp("adm",filename_extension(filename))) goto fail; goto fail;
loop_flag = 0; /* raw data, but test some .ADM blocks as they always start with PS-ADPCM flag 0x06 every 0x1000 */
channel_count = 2;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail;
/* fill in the vital statistics */
start_offset = 0x0;
vgmstream->channels = channel_count;
vgmstream->sample_rate = 44100;
vgmstream->coding_type = coding_PSX;
#if 0
vgmstream->num_samples = read_32bitLE(0x0,streamFile)/channel_count/16*28;
if (loop_flag) {
vgmstream->loop_start_sample = 0;
vgmstream->loop_end_sample = read_32bitLE(0x0,streamFile)/channel_count/16*28;
}
#endif
vgmstream->layout_type = layout_ps2_adm_blocked;
vgmstream->interleave_block_size = 0x400;
vgmstream->meta_type = meta_PS2_ADM;
/* open the file for reading by each channel */
{ {
for (i=0;i<channel_count;i++) { int i;
vgmstream->ch[i].streamfile = streamFile->open(streamFile,filename,vgmstream->interleave_block_size); for (i = 0; i < 10; i++) {
if (read_8bit(0x1000*i + 0x01, streamFile) != 0x06)
if (!vgmstream->ch[i].streamfile) goto fail; goto fail;
vgmstream->ch[i].channel_start_offset=
vgmstream->ch[i].offset=i*vgmstream->interleave_block_size;
} }
} }
start_offset = 0x00;
loop_flag = get_adm_loop_info(streamFile, &loop_start_offset);
channel_count = 2;
/* Calc num_samples */ /* build the VGMSTREAM */
ps2_adm_block_update(start_offset,vgmstream); vgmstream = allocate_vgmstream(channel_count,loop_flag);
vgmstream->num_samples=0; //(get_streamfile_size(streamFile)/0x1000*0xFE0)/32*28; if (!vgmstream) goto fail;
do { vgmstream->sample_rate = 44100;
vgmstream->meta_type = meta_PS2_ADM;
vgmstream->num_samples += 0xFE0*14/16;
ps2_adm_block_update(vgmstream->next_block_offset,vgmstream);
} while (vgmstream->next_block_offset<get_streamfile_size(streamFile));
ps2_adm_block_update(start_offset,vgmstream); vgmstream->coding_type = coding_PSX;
vgmstream->layout_type = layout_blocked_adm;
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
goto fail;
/* calc num_samples as playable data size varies between files/blocks */
vgmstream->num_samples = 0; //ps_bytes_to_samples(get_streamfile_size(streamFile), channel_count);
block_update_adm(start_offset,vgmstream);
while (vgmstream->next_block_offset < get_streamfile_size(streamFile)) {
if (loop_flag && vgmstream->current_block_offset == loop_start_offset)
vgmstream->loop_start_sample = vgmstream->num_samples;
vgmstream->num_samples += ps_bytes_to_samples(vgmstream->current_block_size * channel_count, channel_count);
block_update_adm(vgmstream->next_block_offset,vgmstream);
}
if (loop_flag)
vgmstream->loop_end_sample = vgmstream->num_samples;
block_update_adm(start_offset,vgmstream);
return vgmstream; return vgmstream;
/* clean up anything we may have opened */
fail: fail:
if (vgmstream) close_vgmstream(vgmstream); close_vgmstream(vgmstream);
return NULL; return NULL;
} }
/* loops are not in the .ADM or .DAT bigfile containing them but in the exe; manually get them (a bit meh but whatevs) */
static int get_adm_loop_info(STREAMFILE *streamFile, off_t *loop_start_offset) {
char file_name[PATH_LIMIT];
char index_name[PATH_LIMIT];
STREAMFILE *streamExe = NULL;
int i, name_index = -1, loop_flag;
off_t offset;
streamExe = open_stream_name(streamFile, "SLPM_655.55");
if (!streamExe) goto fail;
get_streamfile_filename(streamFile, file_name, PATH_LIMIT);
/* get file index from name list (file_name == index_name = index number */
offset = 0x23B3c0;
for (i = 0; i < 51; i++) {
read_string(index_name,0x20+1, offset,streamExe);
if (strcmp(index_name, file_name)==0) {
name_index = i;
break;
}
offset += 0x20;
}
if (name_index < 0)
goto fail;
/* get file info using index */
offset = 0x23BAEC + 0x1c*name_index;
loop_flag = (read_32bitLE(offset + 0x10, streamExe) == 0); /* 1: don't loop, 0: loop */
if (loop_flag) { /* loop flag */
*loop_start_offset = read_32bitLE(offset + 0x04, streamExe);
}
/* 0x08: num_samples/loop_end, 0x0c: sample rate (always 44100), 0x14/18: some size? */
close_streamfile(streamExe);
return loop_flag;
fail:
close_streamfile(streamExe);
return 0;
}

View File

@ -131,7 +131,7 @@ static int read_fmt(int big_endian, STREAMFILE * streamFile, off_t current_chunk
fmt->coding_type = coding_MS_IMA; fmt->coding_type = coding_MS_IMA;
break; break;
case 0x69: /* XBOX IMA ADPCM [Rayman Raving Rabbids 2 (PC) --maybe waa/wac/wam/wad?] */ case 0x69: /* XBOX IMA ADPCM [Dynasty Warriors 5 (Xbox), Rayman Raving Rabbids 2 (PC) --maybe waa/wac/wam/wad?] */
if (fmt->bps != 4) goto fail; if (fmt->bps != 4) goto fail;
fmt->coding_type = coding_XBOX_IMA; fmt->coding_type = coding_XBOX_IMA;
break; break;
@ -232,10 +232,9 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) {
int fact_sample_skip = 0; int fact_sample_skip = 0;
int loop_flag = 0; int loop_flag = 0;
long loop_start_ms = -1; int loop_start_sample = 0, loop_end_sample = 0;
long loop_end_ms = -1; long loop_start_ms = -1, loop_end_ms = -1;
off_t loop_start_offset = -1; off_t loop_start_offset = -1, loop_end_offset = -1;
off_t loop_end_offset = -1;
int FormatChunkFound = 0, DataChunkFound = 0, JunkFound = 0; int FormatChunkFound = 0, DataChunkFound = 0, JunkFound = 0;
@ -248,8 +247,9 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) {
/* check extension */ /* check extension */
/* .lwav: to avoid hijacking .wav, .xwav: fake for Xbox games (unneded anymore) */
/* .da: The Great Battle VI (PS), .cd: Exector (PS), .med: Psi Ops (PC), .snd: Layton Brothers (iOS/Android) */ /* .da: The Great Battle VI (PS), .cd: Exector (PS), .med: Psi Ops (PC), .snd: Layton Brothers (iOS/Android) */
if ( check_extensions(streamFile, "wav,lwav,da,cd,med,snd") ) { if ( check_extensions(streamFile, "wav,lwav,xwav,da,cd,med,snd") ) {
; ;
} }
else if ( check_extensions(streamFile, "mwv") ) { else if ( check_extensions(streamFile, "mwv") ) {
@ -282,6 +282,10 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) {
if (riff_size+0x08+0x01 == file_size) if (riff_size+0x08+0x01 == file_size)
riff_size += 0x01; riff_size += 0x01;
/* some Xbox games do this [Dynasty Warriors 3 (Xbox), BloodRayne (Xbox)] */
if (riff_size == file_size && read_16bitLE(0x14,streamFile)==0x0069)
riff_size -= 0x08;
/* check for truncated RIFF */ /* check for truncated RIFF */
if (file_size < riff_size+0x08) goto fail; if (file_size < riff_size+0x08) goto fail;
@ -296,12 +300,11 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) {
if (fmt.codec == 0x6771 && chunk_type == 0x64617461) /* Liar-soft again */ if (fmt.codec == 0x6771 && chunk_type == 0x64617461) /* Liar-soft again */
chunk_size += (chunk_size%2) ? 0x01 : 0x00; chunk_size += (chunk_size%2) ? 0x01 : 0x00;
if (current_chunk+8+chunk_size > file_size) goto fail; if (current_chunk+0x08+chunk_size > file_size) goto fail;
switch(chunk_type) { switch(chunk_type) {
case 0x666d7420: /* "fmt " */ case 0x666d7420: /* "fmt " */
/* only one per file */ if (FormatChunkFound) goto fail; /* only one per file */
if (FormatChunkFound) goto fail;
FormatChunkFound = 1; FormatChunkFound = 1;
if (-1 == read_fmt(0, /* big endian == false*/ if (-1 == read_fmt(0, /* big endian == false*/
@ -311,20 +314,20 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) {
sns, sns,
mwv)) mwv))
goto fail; goto fail;
break; break;
case 0x64617461: /* data */
/* at most one per file */ case 0x64617461: /* "data" */
if (DataChunkFound) goto fail; if (DataChunkFound) goto fail; /* only one per file */
DataChunkFound = 1; DataChunkFound = 1;
start_offset = current_chunk + 8; start_offset = current_chunk + 0x08;
data_size = chunk_size; data_size = chunk_size;
break; break;
case 0x4C495354: /* LIST */
case 0x4C495354: /* "LIST" */
/* what lurks within?? */ /* what lurks within?? */
switch (read_32bitBE(current_chunk + 8, streamFile)) { switch (read_32bitBE(current_chunk+0x08, streamFile)) {
case 0x6164746C: /* adtl */ case 0x6164746C: /* "adtl" */
/* yay, atdl is its own little world */ /* yay, atdl is its own little world */
parse_adtl(current_chunk + 8, chunk_size, parse_adtl(current_chunk + 8, chunk_size,
streamFile, streamFile,
@ -334,28 +337,40 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) {
break; break;
} }
break; break;
case 0x736D706C: /* smpl */
/* check loop count and loop info */ case 0x736D706C: /* "smpl" (RIFFMIDISample + MIDILoop chunk) */
if (read_32bitLE(current_chunk+0x24, streamFile)==1) { /* check loop count/loop info (most common) *///todo double check values
if (read_32bitLE(current_chunk+0x2c+4, streamFile)==0) { /* 0x00: manufacturer id, 0x04: product id, 0x08: sample period, 0x0c: unity node,
* 0x10: pitch fraction, 0x14: SMPTE format, 0x18: SMPTE offset, 0x1c: loop count, 0x20: sampler data */
if (read_32bitLE(current_chunk+0x08+0x1c, streamFile)==1) {
/* 0x24: cue point id, 0x28: type (0=forward, 1=alternating, 2=backward)
* 0x2c: start, 0x30: end, 0x34: fraction, 0x38: play count */
if (read_32bitLE(current_chunk+0x08+0x28, streamFile)==0) {
loop_flag = 1; loop_flag = 1;
loop_start_offset = read_32bitLE(current_chunk+0x2c+8, streamFile); loop_start_offset = read_32bitLE(current_chunk+0x08+0x2c, streamFile);
loop_end_offset = read_32bitLE(current_chunk+0x2c+0xc,streamFile); loop_end_offset = read_32bitLE(current_chunk+0x08+0x30, streamFile);
} }
} }
break; break;
case 0x70666c74: /* pflt */
if (!mwv) break; /* ignore if not in an mwv */
mwv_pflt_offset = current_chunk; /* predictor filters */ case 0x77736D70: /* "wsmp" (RIFFDLSSample + DLSLoop chunk) */
/* check loop count/info (found in some Xbox games: Halo (non-looping), Dynasty Warriors 3, Crimson Sea) */
/* 0x00: size, 0x04: unity note, 0x06: fine tune, 0x08: gain, 0x10: loop count */
if (chunk_size >= 0x24
&& read_32bitLE(current_chunk+0x08+0x00, streamFile) == 0x14
&& read_32bitLE(current_chunk+0x08+0x10, streamFile) > 0
&& read_32bitLE(current_chunk+0x08+0x14, streamFile) == 0x10) {
/* 0x14: size, 0x18: loop type (0=forward, 1=release), 0x1c: loop start, 0x20: loop length */
if (read_32bitLE(current_chunk+0x08+0x18, streamFile)==0) {
loop_flag = 1;
loop_start_sample = read_32bitLE(current_chunk+0x08+0x1c, streamFile);
loop_end_sample = read_32bitLE(current_chunk+0x08+0x20, streamFile);
loop_end_sample += loop_start_sample;
}
}
break; break;
case 0x6374726c: /* ctrl */
if (!mwv) break;
loop_flag = read_32bitLE(current_chunk+0x08, streamFile); case 0x66616374: /* "fact" */
mwv_ctrl_offset = current_chunk;
break;
case 0x66616374: /* fact */
if (chunk_size == 0x04) { /* standard, usually found with ADPCM */ if (chunk_size == 0x04) { /* standard, usually found with ADPCM */
fact_sample_count = read_32bitLE(current_chunk+0x08, streamFile); fact_sample_count = read_32bitLE(current_chunk+0x08, streamFile);
} else if (sns && chunk_size == 0x10) { } else if (sns && chunk_size == 0x10) {
@ -367,11 +382,22 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) {
fact_sample_count = read_32bitLE(current_chunk+0x08, streamFile); fact_sample_count = read_32bitLE(current_chunk+0x08, streamFile);
fact_sample_skip = read_32bitLE(current_chunk+0x10, streamFile); fact_sample_skip = read_32bitLE(current_chunk+0x10, streamFile);
} }
break; break;
case 0x4A554E4B: /* JUNK */
case 0x70666c74: /* "pflt" (.mwv extension) */
if (!mwv) break; /* ignore if not in an mwv */
mwv_pflt_offset = current_chunk; /* predictor filters */
break;
case 0x6374726c: /* "ctrl" (.mwv extension) */
if (!mwv) break;
loop_flag = read_32bitLE(current_chunk+0x08, streamFile);
mwv_ctrl_offset = current_chunk;
break;
case 0x4A554E4B: /* "JUNK" */
JunkFound = 1; JunkFound = 1;
break; break;
default: default:
/* ignorance is bliss */ /* ignorance is bliss */
break; break;
@ -383,7 +409,7 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) {
if (!FormatChunkFound || !DataChunkFound) goto fail; if (!FormatChunkFound || !DataChunkFound) goto fail;
//todo improve detection using fmt sizes/values as Wwise's don't match the RIFF standard //todo improve detection using fmt sizes/values as Wwise's don't match the RIFF standard
/* JUNK is an optional Wwise chunk, and Wwise hijacks the MSADPCM/MS_IMA/XBOX IMA ids (how nice). /* JUNK is an optional Wwise chunk, and Wwise hijacks the MSADPCM/MS_IMA/XBOX IMA ids (how nice).
* To ensure their stuff is parsed in wwise.c we reject their JUNK, which they put almost always. * To ensure their stuff is parsed in wwise.c we reject their JUNK, which they put almost always.
* As JUNK is legal (if unusual) we only reject those codecs. * As JUNK is legal (if unusual) we only reject those codecs.
@ -445,18 +471,21 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) {
break; break;
case coding_MSADPCM: case coding_MSADPCM:
vgmstream->num_samples = fact_sample_count ? fact_sample_count : vgmstream->num_samples = msadpcm_bytes_to_samples(data_size, fmt.block_size, fmt.channel_count);
msadpcm_bytes_to_samples(data_size, fmt.block_size, fmt.channel_count); if (fact_sample_count && fact_sample_count < vgmstream->num_samples)
vgmstream->num_samples = fact_sample_count;
break; break;
case coding_MS_IMA: case coding_MS_IMA:
vgmstream->num_samples = fact_sample_count ? fact_sample_count : vgmstream->num_samples = ms_ima_bytes_to_samples(data_size, fmt.block_size, fmt.channel_count);
ms_ima_bytes_to_samples(data_size, fmt.block_size, fmt.channel_count); if (fact_sample_count && fact_sample_count < vgmstream->num_samples)
vgmstream->num_samples = fact_sample_count;
break; break;
case coding_XBOX_IMA: case coding_XBOX_IMA:
vgmstream->num_samples = fact_sample_count ? fact_sample_count : vgmstream->num_samples = xbox_ima_bytes_to_samples(data_size, fmt.channel_count);
xbox_ima_bytes_to_samples(data_size, fmt.channel_count); if (fact_sample_count && fact_sample_count < vgmstream->num_samples)
vgmstream->num_samples = fact_sample_count; /* some (converted?) Xbox games have bigger fact_samples */
break; break;
case coding_NGC_DSP: case coding_NGC_DSP:
@ -579,6 +608,11 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) {
vgmstream->loop_end_sample = loop_end_offset; vgmstream->loop_end_sample = loop_end_offset;
vgmstream->meta_type = meta_RIFF_WAVE_smpl; vgmstream->meta_type = meta_RIFF_WAVE_smpl;
} }
else if (loop_start_sample >= 0) {
vgmstream->loop_start_sample = loop_start_sample;
vgmstream->loop_end_sample = loop_end_sample;
vgmstream->meta_type = meta_RIFF_WAVE_wsmp;
}
else if (mwv && mwv_ctrl_offset != -1) { else if (mwv && mwv_ctrl_offset != -1) {
vgmstream->loop_start_sample = read_32bitLE(mwv_ctrl_offset+12, streamFile); vgmstream->loop_start_sample = read_32bitLE(mwv_ctrl_offset+12, streamFile);
vgmstream->loop_end_sample = vgmstream->num_samples; vgmstream->loop_end_sample = vgmstream->num_samples;

View File

@ -155,7 +155,7 @@ VGMSTREAM * init_vgmstream_rws(STREAMFILE *streamFile) {
if (name_offset) if (name_offset)
read_string(vgmstream->stream_name,STREAM_NAME_SIZE, name_offset,streamFile); read_string(vgmstream->stream_name,STREAM_NAME_SIZE, name_offset,streamFile);
vgmstream->layout_type = layout_rws_blocked; vgmstream->layout_type = layout_blocked_rws;
vgmstream->current_block_size = block_size / vgmstream->channels; vgmstream->current_block_size = block_size / vgmstream->channels;
vgmstream->full_block_size = block_size_total; vgmstream->full_block_size = block_size_total;
@ -207,7 +207,7 @@ VGMSTREAM * init_vgmstream_rws(STREAMFILE *streamFile) {
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
goto fail; goto fail;
rws_block_update(start_offset, vgmstream); /* block init */ block_update_rws(start_offset, vgmstream); /* block init */
return vgmstream; return vgmstream;

View File

@ -123,9 +123,9 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) {
//todo fix repeat looping //todo fix repeat looping
} }
} }
else if (find_chunk(streamFile, 0x4C495354,first_offset,0, &loop_offset,&loop_size, ww.big_endian, 0)) { /*"LIST", common */ //else if (find_chunk(streamFile, 0x4C495354,first_offset,0, &loop_offset,&loop_size, ww.big_endian, 0)) { /*"LIST", common */
//todo parse "adtl" (does it ever contain loop info in Wwise?) // /* usually contains "cue"s with sample positions for events (ex. Platinum Games) but no real looping info */
} //}
/* other Wwise specific: */ /* other Wwise specific: */
//"JUNK": optional padding for usually aligment (0-size JUNK exists too) //"JUNK": optional padding for usually aligment (0-size JUNK exists too)
@ -194,15 +194,14 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) {
break; break;
case IMA: /* common */ case IMA: /* common */
/* slightly modified XBOX-IMA with interleaved sub-blocks and LE/BE header */ /* slightly modified XBOX-IMA */
/* Wwise reuses common codec ids (ex. 0x0002 MSADPCM) for IMA so this parser should go AFTER riff.c avoid misdetection */
/* Wwise uses common codecs (ex. 0x0002 MSADPCM) so this parser should go AFTER riff.c avoid misdetection */
if (ww.bits_per_sample != 4) goto fail; if (ww.bits_per_sample != 4) goto fail;
if (ww.block_align != 0x24 * ww.channels) goto fail; if (ww.block_align != 0x24 * ww.channels) goto fail;
vgmstream->coding_type = coding_WWISE_IMA; vgmstream->coding_type = coding_WWISE_IMA;
vgmstream->layout_type = layout_none; vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = ww.block_align; vgmstream->interleave_block_size = ww.block_align / ww.channels;
vgmstream->codec_endian = ww.big_endian; vgmstream->codec_endian = ww.big_endian;
if (ww.truncated) /* enough to get real samples */ if (ww.truncated) /* enough to get real samples */

View File

@ -1,91 +0,0 @@
#include "meta.h"
#include "../coding/coding.h"
/* XWAV - renamed WAV with XBOX-IMA
* (could be parsed as RIFF/.lwav but has a custom loop chunk and multichannel) */
VGMSTREAM * init_vgmstream_xbox_xwav(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
int loop_flag, channel_count;
off_t start_offset;
/* check extension */
if (!check_extensions(streamFile,"xwav"))
goto fail;
/* check for headers */
if(!((read_32bitBE(0x00,streamFile) == 0x52494646) && /* "RIFF" */
(read_32bitBE(0x08,streamFile) == 0x57415645) && /* "WAVE" */
(read_32bitBE(0x0C,streamFile) == 0x666D7420) && /* "fmt " */
(read_16bitLE(0x14,streamFile) == 0x0069))) /* codec */
goto fail;
/* loop chunk found on Koei/Omega Force games [Crimson Sea, Dynasty Warriors 5] */
loop_flag = (read_32bitBE(0x28,streamFile) == 0x77736D70); /* "wsmp" */
channel_count = read_16bitLE(0x16,streamFile);
/* search for "data" */
start_offset = 0x1C;
do {
if (read_32bitBE(start_offset,streamFile)==0x64617461)
break;
start_offset += 0x04;
} while (start_offset < (off_t)get_streamfile_size(streamFile));
if (start_offset >= (off_t)get_streamfile_size(streamFile))
goto fail;
start_offset += 0x04;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail;
vgmstream->num_samples = xbox_ima_bytes_to_samples(read_32bitLE(start_offset,streamFile), vgmstream->channels);
vgmstream->sample_rate = read_32bitLE(0x18,streamFile);
if (loop_flag) {
vgmstream->loop_start_sample = read_32bitLE(0x4C,streamFile);
vgmstream->loop_end_sample = vgmstream->loop_start_sample + read_32bitLE(0x50,streamFile);
}
vgmstream->coding_type = coding_XBOX_IMA;
vgmstream->layout_type = layout_none;
vgmstream->meta_type = meta_XBOX_RIFF;
//if (!vgmstream_open_stream(vgmstream, streamFile, start_offset))
// goto fail;
//custom init
{
int i, ch;
char filename[PATH_LIMIT];
streamFile->get_name(streamFile,filename,sizeof(filename));
if (channel_count > 2) { /* multichannel interleaved init */
for (i=0, ch=0;i<channel_count;i++,ch++) {
if ((ch&2) && (i!=0)) {
ch = 0;
start_offset += 0x24*2;
}
vgmstream->ch[i].streamfile = streamFile->open(streamFile,filename,0x24);
vgmstream->ch[i].offset = start_offset + 0x04;
if (!vgmstream->ch[i].streamfile) goto fail;
}
}
else {
for (i=0; i < channel_count; i++) {
vgmstream->ch[i].streamfile = streamFile->open(streamFile,filename,0x24);
vgmstream->ch[i].offset = start_offset + 0x04;
if (!vgmstream->ch[i].streamfile) goto fail;
}
}
}
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;
}

View File

@ -993,6 +993,27 @@ int get_streamfile_name(STREAMFILE *streamFile, char * buffer, size_t size) {
streamFile->get_name(streamFile,buffer,size); streamFile->get_name(streamFile,buffer,size);
return 1; return 1;
} }
int get_streamfile_filename(STREAMFILE *streamFile, char * buffer, size_t size) {
char foldername[PATH_LIMIT];
const char *path;
streamFile->get_name(streamFile,foldername,sizeof(foldername));
//todo Windows CMD accepts both \\ and /, better way to handle this?
path = strrchr(foldername,'\\');
if (!path)
path = strrchr(foldername,'/');
if (path != NULL)
path = path+1;
//todo validate sizes and copy sensible max
if (path) {
strcpy(buffer, path);
} else {
strcpy(buffer, foldername);
}
return 1;
}
int get_streamfile_path(STREAMFILE *streamFile, char * buffer, size_t size) { int get_streamfile_path(STREAMFILE *streamFile, char * buffer, size_t size) {
const char *path; const char *path;

View File

@ -179,6 +179,7 @@ int find_chunk_le(STREAMFILE *streamFile, uint32_t chunk_id, off_t start_offset,
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 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_name(STREAMFILE *streamFile, char * buffer, size_t size);
int get_streamfile_filename(STREAMFILE *streamFile, char * buffer, size_t size);
int get_streamfile_path(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); int get_streamfile_ext(STREAMFILE *streamFile, char * filename, size_t size);
#endif #endif

View File

@ -55,7 +55,6 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = {
init_vgmstream_ps2_ild, init_vgmstream_ps2_ild,
init_vgmstream_ps2_pnb, init_vgmstream_ps2_pnb,
init_vgmstream_xbox_wavm, init_vgmstream_xbox_wavm,
init_vgmstream_xbox_xwav,
init_vgmstream_ngc_str, init_vgmstream_ngc_str,
init_vgmstream_ea_schl, init_vgmstream_ea_schl,
init_vgmstream_caf, init_vgmstream_caf,
@ -628,7 +627,9 @@ VGMSTREAM * allocate_vgmstream(int channel_count, int looped) {
VGMSTREAMCHANNEL * start_channels; VGMSTREAMCHANNEL * start_channels;
VGMSTREAMCHANNEL * loop_channels; VGMSTREAMCHANNEL * loop_channels;
if (channel_count <= 0) return NULL; /* up to ~16 aren't too rare for multilayered files, more is probably a bug */
if (channel_count <= 0 || channel_count > 64)
return NULL;
vgmstream = calloc(1,sizeof(VGMSTREAM)); vgmstream = calloc(1,sizeof(VGMSTREAM));
if (!vgmstream) return NULL; if (!vgmstream) return NULL;
@ -969,13 +970,13 @@ void render_vgmstream(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstre
case layout_filp_blocked: case layout_filp_blocked:
case layout_blocked_ivaud: case layout_blocked_ivaud:
case layout_blocked_ea_swvr: case layout_blocked_ea_swvr:
case layout_ps2_adm_blocked: case layout_blocked_adm:
case layout_dsp_bdsp_blocked: case layout_dsp_bdsp_blocked:
case layout_tra_blocked: case layout_tra_blocked:
case layout_ps2_iab_blocked: case layout_ps2_iab_blocked:
case layout_ps2_strlr_blocked: case layout_ps2_strlr_blocked:
case layout_rws_blocked: case layout_blocked_rws:
case layout_hwas_blocked: case layout_blocked_hwas:
case layout_blocked_ea_sns: case layout_blocked_ea_sns:
case layout_blocked_awc: case layout_blocked_awc:
case layout_blocked_vgs: case layout_blocked_vgs:
@ -1072,6 +1073,7 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) {
case coding_XBOX_IMA: case coding_XBOX_IMA:
case coding_XBOX_IMA_int: case coding_XBOX_IMA_int:
case coding_FSB_IMA: case coding_FSB_IMA:
case coding_WWISE_IMA:
return 64; return 64;
case coding_APPLE_IMA4: case coding_APPLE_IMA4:
return 64; return 64;
@ -1079,7 +1081,6 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) {
case coding_REF_IMA: case coding_REF_IMA:
return ((vgmstream->interleave_block_size - 0x04*vgmstream->channels) * 2 / vgmstream->channels) + 1; return ((vgmstream->interleave_block_size - 0x04*vgmstream->channels) * 2 / vgmstream->channels) + 1;
case coding_RAD_IMA: case coding_RAD_IMA:
case coding_WWISE_IMA:
return (vgmstream->interleave_block_size - 0x04*vgmstream->channels) * 2 / vgmstream->channels; return (vgmstream->interleave_block_size - 0x04*vgmstream->channels) * 2 / vgmstream->channels;
case coding_NDS_IMA: case coding_NDS_IMA:
case coding_DAT4_IMA: case coding_DAT4_IMA:
@ -1146,6 +1147,8 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) {
return 128*2; return 128*2;
case coding_MC3: case coding_MC3:
return 10; return 10;
case coding_FADPCM:
return 256; /* (0x8c - 0xc) * 2 */
case coding_EA_MT: case coding_EA_MT:
return 432; return 432;
case coding_CRI_HCA: case coding_CRI_HCA:
@ -1227,7 +1230,6 @@ int get_vgmstream_frame_size(VGMSTREAM * vgmstream) {
case coding_RAD_IMA: case coding_RAD_IMA:
case coding_NDS_IMA: case coding_NDS_IMA:
case coding_DAT4_IMA: case coding_DAT4_IMA:
case coding_WWISE_IMA:
case coding_REF_IMA: case coding_REF_IMA:
return vgmstream->interleave_block_size; return vgmstream->interleave_block_size;
case coding_AWC_IMA: case coding_AWC_IMA:
@ -1240,8 +1242,11 @@ int get_vgmstream_frame_size(VGMSTREAM * vgmstream) {
case coding_UBI_IMA: /* variable (PCM then IMA) */ case coding_UBI_IMA: /* variable (PCM then IMA) */
return 0; return 0;
case coding_XBOX_IMA: case coding_XBOX_IMA:
//todo should be 0x48 when stereo, but blocked/interleave layout don't understand stereo codecs
return 0x24; //vgmstream->channels==1 ? 0x24 : 0x48;
case coding_XBOX_IMA_int: case coding_XBOX_IMA_int:
case coding_FSB_IMA: case coding_FSB_IMA:
case coding_WWISE_IMA:
return 0x24; return 0x24;
case coding_APPLE_IMA4: case coding_APPLE_IMA4:
return 0x22; return 0x22;
@ -1299,6 +1304,8 @@ int get_vgmstream_frame_size(VGMSTREAM * vgmstream) {
return 0x90; return 0x90;
case coding_MC3: case coding_MC3:
return 0x04; return 0x04;
case coding_FADPCM:
return 0x8c;
case coding_EA_MT: case coding_EA_MT:
return 0; /* variable (frames of bit counts or PCM frames) */ return 0; /* variable (frames of bit counts or PCM frames) */
#ifdef VGM_USE_ATRAC9 #ifdef VGM_USE_ATRAC9
@ -1916,6 +1923,13 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to
chan); chan);
} }
break; break;
case coding_FADPCM:
for (chan=0;chan<vgmstream->channels;chan++) {
decode_fadpcm(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan,
vgmstream->channels,vgmstream->samples_into_block,
samples_to_do);
}
break;
case coding_EA_MT: case coding_EA_MT:
for (chan=0;chan<vgmstream->channels;chan++) { for (chan=0;chan<vgmstream->channels;chan++) {
decode_ea_mt(vgmstream, buffer+samples_written*vgmstream->channels+chan, decode_ea_mt(vgmstream, buffer+samples_written*vgmstream->channels+chan,
@ -2532,7 +2546,7 @@ int get_vgmstream_average_bitrate(VGMSTREAM * vgmstream) {
* Inits vgmstreams' channels doing two things: * Inits vgmstreams' channels doing two things:
* - sets the starting offset per channel (depending on the layout) * - sets the starting offset per channel (depending on the layout)
* - opens its own streamfile from on a base one. One streamfile per channel may be open (to improve read/seeks). * - opens its own streamfile from on a base one. One streamfile per channel may be open (to improve read/seeks).
* Should be called in metas before returning the VGMSTREAM.. * Should be called in metas before returning the VGMSTREAM.
*/ */
int vgmstream_open_stream(VGMSTREAM * vgmstream, STREAMFILE *streamFile, off_t start_offset) { int vgmstream_open_stream(VGMSTREAM * vgmstream, STREAMFILE *streamFile, off_t start_offset) {
STREAMFILE * file; STREAMFILE * file;

View File

@ -152,6 +152,7 @@ typedef enum {
coding_MTAF, /* Konami MTAF ADPCM */ coding_MTAF, /* Konami MTAF ADPCM */
coding_MTA2, /* Konami MTA2 ADPCM */ coding_MTA2, /* Konami MTA2 ADPCM */
coding_MC3, /* Paradigm MC3 3-bit ADPCM */ coding_MC3, /* Paradigm MC3 3-bit ADPCM */
coding_FADPCM, /* FMOD FADPCM 4-bit ADCPM */
/* others */ /* others */
coding_SDX2, /* SDX2 2:1 Squareroot-Delta-Exact compression DPCM */ coding_SDX2, /* SDX2 2:1 Squareroot-Delta-Exact compression DPCM */
@ -238,15 +239,15 @@ typedef enum {
layout_thp_blocked, layout_thp_blocked,
layout_filp_blocked, layout_filp_blocked,
layout_blocked_ea_swvr, layout_blocked_ea_swvr,
layout_ps2_adm_blocked, layout_blocked_adm,
layout_dsp_bdsp_blocked, layout_dsp_bdsp_blocked,
layout_mxch_blocked, layout_mxch_blocked,
layout_blocked_ivaud, /* GTA IV .ivaud blocks */ layout_blocked_ivaud, /* GTA IV .ivaud blocks */
layout_tra_blocked, /* DefJam Rapstar .tra blocks */ layout_tra_blocked, /* DefJam Rapstar .tra blocks */
layout_ps2_iab_blocked, layout_ps2_iab_blocked,
layout_ps2_strlr_blocked, layout_ps2_strlr_blocked,
layout_rws_blocked, layout_blocked_rws,
layout_hwas_blocked, layout_blocked_hwas,
layout_blocked_ea_sns, /* newest Electronic Arts blocks, found in SNS/SNU/SPS/etc formats */ layout_blocked_ea_sns, /* newest Electronic Arts blocks, found in SNS/SNU/SPS/etc formats */
layout_blocked_awc, /* Rockstar AWC */ layout_blocked_awc, /* Rockstar AWC */
layout_blocked_vgs, /* Guitar Hero II (PS2) */ layout_blocked_vgs, /* Guitar Hero II (PS2) */
@ -456,7 +457,6 @@ typedef enum {
meta_UBI_CKD, /* Ubisoft CKD RIFF header (Rayman Origins Wii) */ meta_UBI_CKD, /* Ubisoft CKD RIFF header (Rayman Origins Wii) */
meta_XBOX_WAVM, /* XBOX WAVM File */ meta_XBOX_WAVM, /* XBOX WAVM File */
meta_XBOX_RIFF, /* XBOX RIFF/WAVE File */
meta_XBOX_WVS, /* XBOX WVS */ meta_XBOX_WVS, /* XBOX WVS */
meta_NGC_WVS, /* Metal Arms - Glitch in the System */ meta_NGC_WVS, /* Metal Arms - Glitch in the System */
meta_XBOX_MATX, /* XBOX MATX */ meta_XBOX_MATX, /* XBOX MATX */
@ -481,6 +481,7 @@ typedef enum {
meta_RIFF_WAVE_POS, /* .wav + .pos for looping (Ys Complete PC) */ meta_RIFF_WAVE_POS, /* .wav + .pos for looping (Ys Complete PC) */
meta_RIFF_WAVE_labl, /* RIFF w/ loop Markers in LIST-adtl-labl */ meta_RIFF_WAVE_labl, /* RIFF w/ loop Markers in LIST-adtl-labl */
meta_RIFF_WAVE_smpl, /* RIFF w/ loop data in smpl chunk */ meta_RIFF_WAVE_smpl, /* RIFF w/ loop data in smpl chunk */
meta_RIFF_WAVE_wsmp, /* RIFF w/ loop data in wsmp chunk */
meta_RIFF_WAVE_MWV, /* .mwv RIFF w/ loop data in ctrl chunk pflt */ meta_RIFF_WAVE_MWV, /* .mwv RIFF w/ loop data in ctrl chunk pflt */
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 */
@ -550,7 +551,7 @@ typedef enum {
meta_PS2_WAD, /* The golden Compass */ meta_PS2_WAD, /* The golden Compass */
meta_DSP_XIII, /* XIII, possibly more (Ubisoft header???) */ meta_DSP_XIII, /* XIII, possibly more (Ubisoft header???) */
meta_DSP_CABELAS, /* Cabelas games */ meta_DSP_CABELAS, /* Cabelas games */
meta_PS2_ADM, /* Dragon Quest 5 */ meta_PS2_ADM, /* Dragon Quest V (PS2) */
meta_PS2_LPCM, /* Ah! My Goddess */ meta_PS2_LPCM, /* Ah! My Goddess */
meta_DSP_BDSP, /* Ah! My Goddess */ meta_DSP_BDSP, /* Ah! My Goddess */
meta_PS2_VMS, /* Autobahn Raser - Police Madness */ meta_PS2_VMS, /* Autobahn Raser - Police Madness */