Updated VGMStream to r1050-1391-g4ed16da3.

CQTexperiment
Christopher Snowhill 2018-07-14 18:36:08 -07:00
parent ccf4b1506b
commit a6efe31256
30 changed files with 708 additions and 473 deletions

View File

@ -1,70 +1,97 @@
#include "coding.h" #include "coding.h"
#include "../util.h" #include "../util.h"
const int SH = 4; // todo this is based on Kazzuya's old code; different emus (PCSX, Mame, Mednafen, etc) do
const int SHC = 10; // XA coefs int math in different ways (see comments below), so may not be 100% accurate.
// May be implemented like the SNES/SPC700 BRR (per-filter code?).
double K0[4] = { 0.0, 0.9375, 1.796875, 1.53125}; /* XA ADPCM gain values */
double K1[4] = { 0.0, 0.0, -0.8125,-0.859375}; static const double K0[4] = { 0.0, 0.9375, 1.796875, 1.53125 };
static const double K1[4] = { 0.0, 0.0, -0.8125,-0.859375};
static int IK0(int fid) static int IK0(int fid) { return ((int)((-K0[fid]) * (1 << 10))); } /* K0/1 floats to int, K*2^10 = K*1024 */
{ return ((int)((-K0[fid]) * (1 << SHC))); } static int IK1(int fid) { return ((int)((-K1[fid]) * (1 << 10))); }
static int IK1(int fid)
{ return ((int)((-K1[fid]) * (1 << SHC))); }
static int CLAMP(int value, int Minim, int Maxim)
{
if (value < Minim) value = Minim;
if (value > Maxim) value = Maxim;
return value;
}
/* Sony XA ADPCM, defined for CD-DA/CD-i in the "Red Book" (private) or "Green Book" (public) specs.
* The algorithm basically is BRR (Bit Rate Reduction) from the SNES SPC700, while the data layout is new.
*
* Decoding is defined in diagrams, roughly as:
* pcm = clamp( signed_nibble * 2^(12-range) + K0[index]*hist1 + K1[index]*hist2 )
* - Range (12-range=shift) and filter index are renewed every ~28 samples.
* - nibble is expanded to a signed 16b sample, reimplemented as:
* short sample = ((nibble << 12) & 0xf000) >> shift
* or: int sample = ((nibble << 28) & 0xf0000000) >> (shift + N)
* - K0/K1 are float coefs are typically redefined with int math in various ways, with non-equivalent rounding:
* (sample + K0*2^N*hist1 + K1*2^N*hist1 + [(2^N)/2]) / 2^N
* (sample + K0*2^N*hist1 + K1*2^N*hist1 + [(2^N)/2]) >> N
* sample + (K0<<N*hist1 + K1<<N*hist1)>>N
* sample + (K0*2^N*hist1)>>N + (K1*2^N*hist1)>>N
* etc
* (rounding differences should be inaudible, so public implementations may be approximations)
*
* Various XA descendants (PS-ADPCM, EA-XA, NGC DTK, FADPCM, etc) do filters/rounding slightly
* differently, using one of the above methods.
* int coef tables commonly use N = 6 or 8, so K0 0.9375*64 = 60 or 0.9375*256 = 240
* PS1 XA is apparently upsampled and interpolated to 44100, vgmstream doesn't simulate this.
*
* Info (Green Book): https://www.lscdweb.com/data/downloadables/2/8/cdi_may94_r2.pdf
*/
void decode_xa(VGMSTREAM * vgmstream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { void decode_xa(VGMSTREAM * vgmstream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) {
static int head_table[8] = {0,2,8,10};
VGMSTREAMCHANNEL * stream = &(vgmstream->ch[channel]); VGMSTREAMCHANNEL * stream = &(vgmstream->ch[channel]);
int predict_nr, shift_factor, sample; off_t sp_offset;
int32_t hist1=stream->adpcm_history1_32; int i;
int32_t hist2=stream->adpcm_history2_32; int frames_in, sample_count = 0;
int HeadTable[8]={0,2,8,10}; int32_t coef1, coef2, coef_index, shift_factor;
int32_t hist1 = stream->adpcm_history1_32;
int32_t hist2 = stream->adpcm_history2_32;
short scale; /* external interleave (fixed size), mono/stereo */
int i; frames_in = first_sample / (28*2 / channelspacing);
int32_t sample_count; first_sample = first_sample % 28;
int framesin = first_sample / (56 / channelspacing); /* hack for mono/stereo handling */
vgmstream->xa_get_high_nibble = !vgmstream->xa_get_high_nibble;
if (first_sample && channelspacing==1)
vgmstream->xa_get_high_nibble = !vgmstream->xa_get_high_nibble;
/* parse current sound unit (subframe) sound parameters */
sp_offset = stream->offset+head_table[frames_in]+vgmstream->xa_get_high_nibble;
coef_index = (read_8bit(sp_offset,stream->streamfile) >> 4) & 0xf;
shift_factor = (read_8bit(sp_offset,stream->streamfile) ) & 0xf;
VGM_ASSERT(coef_index > 4 || shift_factor > 12, "XA: incorrect coefs/shift at %lx\n", sp_offset);
if (coef_index > 4)
coef_index = 0; /* only 4 filters are used, rest is apparently 0 */
if (shift_factor > 12)
shift_factor = 9; /* supposedly, from Nocash PSX docs */
coef1 = IK0(coef_index);
coef2 = IK1(coef_index);
first_sample = first_sample % 28; /* decode nibbles */
for (i = first_sample; i < first_sample + samples_to_do; i++) {
vgmstream->xa_get_high_nibble=!vgmstream->xa_get_high_nibble; int32_t new_sample;
uint8_t nibbles = (uint8_t)read_8bit(stream->offset+0x10+frames_in+(i*0x04),stream->streamfile);
if((first_sample) && (channelspacing==1)) new_sample = vgmstream->xa_get_high_nibble ?
vgmstream->xa_get_high_nibble=!vgmstream->xa_get_high_nibble; (nibbles >> 4) & 0x0f :
(nibbles ) & 0x0f;
new_sample = (int16_t)((new_sample << 12) & 0xf000) >> shift_factor; /* 16b sign extend + scale */
new_sample = new_sample << 4;
new_sample = new_sample - ((coef1*hist1 + coef2*hist2) >> 10);
predict_nr = read_8bit(stream->offset+HeadTable[framesin]+vgmstream->xa_get_high_nibble,stream->streamfile) >> 4; hist2 = hist1;
shift_factor = read_8bit(stream->offset+HeadTable[framesin]+vgmstream->xa_get_high_nibble,stream->streamfile) & 0xf; hist1 = new_sample; /* must go before clamp, somehow */
new_sample = new_sample >> 4;
new_sample = clamp16(new_sample);
for (i=first_sample,sample_count=0; i<first_sample+samples_to_do; i++,sample_count+=channelspacing) { outbuf[sample_count] = new_sample;
short sample_byte = (short)read_8bit(stream->offset+16+framesin+(i*4),stream->streamfile); sample_count += channelspacing;
}
scale = ((vgmstream->xa_get_high_nibble ? stream->adpcm_history1_32 = hist1;
sample_byte >> 4 : stream->adpcm_history2_32 = hist2;
sample_byte & 0x0f)<<12);
sample = (short)(scale & 0xf000) >> shift_factor;
sample <<= SH;
sample -= (IK0(predict_nr) * hist1 + (IK1(predict_nr) * hist2)) >> SHC;
hist2=hist1;
hist1=sample;
sample = CLAMP(sample, -32768 << SH, 32767 << SH);
outbuf[sample_count] = (short)(sample >> SH);
}
stream->adpcm_history1_32=hist1;
stream->adpcm_history2_32=hist2;
} }
size_t xa_bytes_to_samples(size_t bytes, int channels, int is_blocked) { size_t xa_bytes_to_samples(size_t bytes, int channels, int is_blocked) {

View File

@ -1,13 +1,16 @@
#include "vgmstream.h" #include "vgmstream.h"
//#define VGM_REGISTER_TYPE(extension) ...
//#define VGM_REGISTER_TYPE_COMMON(extension) ... /* for common extensions like aiff */
/* defines the list of accepted extensions. vgmstream doesn't use it internally so it's here
* to inform plugins that need it. Common extensions are commented out to avoid stealing them. */
/* some extensions could be #ifdef but no really needed */ /* some extensions require external libraries and could be #ifdef, no really needed */
/* some formats marked as "not parsed" mean they'll go through FFmpeg, the header/extension is not parsed */ /* some formats marked as "not parsed" mean they'll go through FFmpeg, the header/extension is not parsed */
static const char* extension_list[] = { static const char* extension_list[] = {
//"", /* vgmstream can plays extensionless files too, but plugins must accept them manually */
"04sw", "04sw",
"2dx9", "2dx9",
"2pfs", "2pfs",
@ -38,6 +41,7 @@ static const char* extension_list[] = {
"al2", "al2",
"amts", //fake extension/header id for .stm (to be removed) "amts", //fake extension/header id for .stm (to be removed)
"ao", //txth/reserved [Cloudphobia (PC)] "ao", //txth/reserved [Cloudphobia (PC)]
"apc", //txth/reserved [MegaRace 3 (PC)]
"as4", "as4",
"asd", "asd",
"asf", "asf",
@ -343,6 +347,7 @@ static const char* extension_list[] = {
"stx", "stx",
"svag", "svag",
"svs", "svs",
"svg", //txth/reserved [Hunter: The Reckoning - Wayward (PS2)]
"swag", "swag",
"swav", "swav",
"swd", "swd",
@ -367,6 +372,7 @@ static const char* extension_list[] = {
"v0", "v0",
//"v1", //dual channel with v0 //"v1", //dual channel with v0
"vag", "vag",
"vai", //txth/reserved [Ratatouille (GC)]
"vas", "vas",
"vawx", "vawx",
"vb", "vb",
@ -435,7 +441,7 @@ static const char* extension_list[] = {
"zsd", "zsd",
"zwdsp", "zwdsp",
"vgmstream" "vgmstream" /* fake extension, catch-all for FFmpeg/txth/etc */
//, NULL //end mark //, NULL //end mark
}; };
@ -829,7 +835,7 @@ static const meta_info meta_info_list[] = {
{meta_PS2_MCG, "Gunvari MCG Header"}, {meta_PS2_MCG, "Gunvari MCG Header"},
{meta_ZSD, "ZSD Header"}, {meta_ZSD, "ZSD Header"},
{meta_RedSpark, "RedSpark Header"}, {meta_RedSpark, "RedSpark Header"},
{meta_PC_IVAUD, "assumed GTA IV Audio file by .ivaud extension"}, {meta_IVAUD, "Rockstar .ivaud header"},
{meta_DSP_WII_WSD, ".WSD header"}, {meta_DSP_WII_WSD, ".WSD header"},
{meta_WII_NDP, "Icon Games NDP header"}, {meta_WII_NDP, "Icon Games NDP header"},
{meta_PS2_SPS, "Ape Escape 2 SPS Header"}, {meta_PS2_SPS, "Ape Escape 2 SPS Header"},
@ -907,7 +913,7 @@ static const meta_info meta_info_list[] = {
{meta_X360_TRA, "Terminal Reality .TRA raw header"}, {meta_X360_TRA, "Terminal Reality .TRA raw header"},
{meta_PS2_VGS, "Princess Soft VGS header"}, {meta_PS2_VGS, "Princess Soft VGS header"},
{meta_PS2_IAB, "Runtime .IAB header"}, {meta_PS2_IAB, "Runtime .IAB header"},
{meta_PS2_STRLR, "STR L/R header"}, {meta_PS2_STRLR, "The Bouncer STR header"},
{meta_LSF_N1NJ4N, ".lsf !n1nj4n header"}, {meta_LSF_N1NJ4N, ".lsf !n1nj4n header"},
{meta_VAWX, "feelplus VAWX header"}, {meta_VAWX, "feelplus VAWX header"},
{meta_PC_SNDS, "assumed Heavy Iron IMA by .snds extension"}, {meta_PC_SNDS, "assumed Heavy Iron IMA by .snds extension"},
@ -1023,6 +1029,7 @@ static const meta_info meta_info_list[] = {
{meta_OGG_GWM, "Ogg Vorbis (GWM header)"}, {meta_OGG_GWM, "Ogg Vorbis (GWM header)"},
{meta_DSP_SADF, "Procyon Studio SADF header"}, {meta_DSP_SADF, "Procyon Studio SADF header"},
{meta_H4M, "Hudson HVQM4 header"}, {meta_H4M, "Hudson HVQM4 header"},
{meta_OGG_MUS, "Ogg Vorbis (MUS header)"},
#ifdef VGM_USE_FFMPEG #ifdef VGM_USE_FFMPEG
{meta_FFmpeg, "FFmpeg supported file format"}, {meta_FFmpeg, "FFmpeg supported file format"},

View File

@ -1,187 +1,192 @@
#include "layout.h" #include "layout.h"
#include "../coding/coding.h" #include "../coding/coding.h"
#include "../vgmstream.h" #include "../vgmstream.h"
/* parse EA style blocks, id+size+samples+data */ /* parse EA style blocks, id+size+samples+data */
void block_update_ea_schl(off_t block_offset, VGMSTREAM * vgmstream) { void block_update_ea_schl(off_t block_offset, VGMSTREAM * vgmstream) {
STREAMFILE* streamFile = vgmstream->ch[0].streamfile; STREAMFILE* streamFile = vgmstream->ch[0].streamfile;
int i; int i;
int new_schl = 0; int new_schl = 0;
size_t block_size, block_samples; size_t block_size, block_samples;
int32_t (*read_32bit)(off_t,STREAMFILE*) = vgmstream->codec_endian ? read_32bitBE : read_32bitLE; int32_t (*read_32bit)(off_t,STREAMFILE*) = vgmstream->codec_endian ? read_32bitBE : read_32bitLE;
/* EOF reads: signal we have nothing and let the layout fail */ /* EOF reads: signal we have nothing and let the layout fail */
if (block_offset >= get_streamfile_size(streamFile)) { if (block_offset >= get_streamfile_size(streamFile)) {
vgmstream->current_block_offset = block_offset; vgmstream->current_block_offset = block_offset;
vgmstream->next_block_offset = block_offset; vgmstream->next_block_offset = block_offset;
vgmstream->current_block_samples = -1; vgmstream->current_block_samples = -1;
return; return;
} }
/* read a single block */ /* read a single block */
{ {
uint32_t block_id = read_32bitBE(block_offset+0x00,streamFile); uint32_t block_id = read_32bitBE(block_offset+0x00,streamFile);
block_size = read_32bitLE(block_offset+0x04,streamFile); block_size = read_32bitLE(block_offset+0x04,streamFile);
if (block_size > 0x00F00000) /* size is always LE, except in early SAT/MAC */ if (block_size > 0x00F00000) /* size is always LE, except in early SAT/MAC */
block_size = read_32bitBE(block_offset+0x04,streamFile); block_size = read_32bitBE(block_offset+0x04,streamFile);
switch(block_id) { switch(block_id) {
case 0x5343446C: /* "SCDl" */ case 0x5343446C: /* "SCDl" */
case 0x5344454E: /* "SDEN" */ case 0x5344454E: /* "SDEN" */
case 0x53444652: /* "SDFR" */ case 0x53444652: /* "SDFR" */
case 0x53444745: /* "SDGE" */ case 0x53444745: /* "SDGE" */
case 0x53444954: /* "SDIT" */ case 0x53444954: /* "SDIT" */
case 0x53445350: /* "SDSP" */ case 0x53445350: /* "SDSP" */
case 0x53445255: /* "SDRU" */ case 0x53445255: /* "SDRU" */
case 0x53444A41: /* "SDJA" */ case 0x53444A41: /* "SDJA" */
/* audio chunk */ /* audio chunk */
if (vgmstream->coding_type == coding_PSX) if (vgmstream->coding_type == coding_PSX)
block_samples = ps_bytes_to_samples(block_size-0x10, vgmstream->channels); block_samples = ps_bytes_to_samples(block_size-0x10, vgmstream->channels);
else else
block_samples = read_32bit(block_offset+0x08,streamFile); block_samples = read_32bit(block_offset+0x08,streamFile);
break; break;
default: default:
/* ignore other chunks (audio "SCHl/SCCl/...", video "pIQT/MADk/...", etc) */ /* ignore other chunks (audio "SCHl/SCCl/...", video "pIQT/MADk/...", etc) */
block_samples = 0; /* layout ignores this */ block_samples = 0; /* layout ignores this */
break; break;
} }
/* "SCHl" start block (movie "SHxx" shouldn't use multi files) */ /* "SCHl" start block (movie "SHxx" shouldn't use multi files) */
if (block_id == 0x5343486C) if (block_id == 0x5343486C)
new_schl = 1; new_schl = 1;
/* padding between "SCEl" and next "SCHl" (when subfiles exist) */ /* padding between "SCEl" and next "SCHl" (when subfiles exist) */
if (block_id == 0x00000000) if (block_id == 0x00000000)
block_size = 0x04; block_size = 0x04;
/* guard against errors (happens in bad rips/endianness, observed max is vid ~0x20000) */ /* guard against errors (happens in bad rips/endianness, observed max is vid ~0x20000) */
if (block_size == 0x00 || block_size > 0xFFFFF || block_samples > 0xFFFF) { if (block_size == 0x00 || block_size > 0xFFFFF || block_samples > 0xFFFF) {
block_size = 0x04; block_size = 0x04;
block_samples = 0; block_samples = 0;
} }
/* "SCEl" end chunk should be 32b-aligned, fixes some multi-SCHl [ex. Need for Speed 2 (PC) .eam] */ /* "SCEl" end chunk should be 32b-aligned, fixes some multi-SCHl [ex. Need for Speed 2 (PC) .eam] */
if (((block_offset + block_size) % 0x04) && block_id == 0x5343456C) { if (((block_offset + block_size) % 0x04) && block_id == 0x5343456C) {
block_size += 0x04 - ((block_offset + block_size) % 0x04); block_size += 0x04 - ((block_offset + block_size) % 0x04);
} }
} }
/* set new channel offsets and ADPCM history */ vgmstream->current_block_offset = block_offset;
/* ADPCM hist could be considered part of the stream/decoder (some EAXA decoders call it "EAXA R1" when it has hist), and BNKs vgmstream->next_block_offset = block_offset + block_size;
* (with no blocks) may also have them in the first offset, but also may not. To simplify we just read them here. */ vgmstream->current_block_samples = block_samples;
switch(vgmstream->coding_type) { vgmstream->current_block_size = 0; /* uses current_block_samples instead */
/* id, size, unk1, unk2, interleaved data */
case coding_PSX: /* no need to setup offsets (plus could read over filesize near EOF) */
for (i = 0; i < vgmstream->channels; i++) { if (block_samples == 0)
size_t interleave = (block_size-0x10) / vgmstream->channels; return;
vgmstream->ch[i].offset = block_offset + 0x10 + i*interleave;
}
/* 0x08/0x0c: unknown (doesn't look like hist or offsets, as 1ch files has them too) */ /* set new channel offsets and ADPCM history */
/* ADPCM hist could be considered part of the stream/decoder (some EAXA decoders call it "EAXA R1" when it has hist), and BNKs
break; * (with no blocks) may also have them in the first offset, but also may not. To simplify we just read them here. */
switch(vgmstream->coding_type) {
/* id, size, IMA hist, stereo/mono data */ /* id, size, unk1, unk2, interleaved data */
case coding_DVI_IMA: case coding_PSX:
for(i = 0; i < vgmstream->channels; i++) { for (i = 0; i < vgmstream->channels; i++) {
off_t header_offset = block_offset + 0xc + i*4; size_t interleave = (block_size-0x10) / vgmstream->channels;
vgmstream->ch[i].adpcm_history1_32 = read_16bitLE(header_offset+0x00, vgmstream->ch[i].streamfile); vgmstream->ch[i].offset = block_offset + 0x10 + i*interleave;
vgmstream->ch[i].adpcm_step_index = read_16bitLE(header_offset+0x02, vgmstream->ch[i].streamfile); }
vgmstream->ch[i].offset = block_offset + 0xc + (4*vgmstream->channels); /* 0x08/0x0c: unknown (doesn't look like hist or offsets, as 1ch files has them too) */
}
break;
break;
/* id, size, IMA hist, stereo/mono data */
/* id, size, samples */ case coding_DVI_IMA:
case coding_PCM16_int: for(i = 0; i < vgmstream->channels; i++) {
for (i = 0; i < vgmstream->channels; i++) { off_t header_offset = block_offset + 0xc + i*4;
vgmstream->ch[i].offset = block_offset + 0x0c + (i*0x02); vgmstream->ch[i].adpcm_history1_32 = read_16bitLE(header_offset+0x00, vgmstream->ch[i].streamfile);
} vgmstream->ch[i].adpcm_step_index = read_16bitLE(header_offset+0x02, vgmstream->ch[i].streamfile);
vgmstream->ch[i].offset = block_offset + 0xc + (4*vgmstream->channels);
break; }
/* id, size, samples, hists-per-channel, stereo/interleaved data */ break;
case coding_EA_XA:
//case coding_EA_XA_V2: /* handled in default */ /* id, size, samples */
case coding_EA_XA_int: case coding_PCM16_int:
for (i = 0; i < vgmstream->channels; i++) { for (i = 0; i < vgmstream->channels; i++) {
int is_interleaved = vgmstream->coding_type == coding_EA_XA_int; vgmstream->ch[i].offset = block_offset + 0x0c + (i*0x02);
size_t interleave; }
/* read ADPCM history from all channels before data (not actually read in sx.exe) */ break;
//vgmstream->ch[i].adpcm_history1_32 = read_16bit(block_offset + 0x0C + (i*0x04) + 0x00,streamFile);
//vgmstream->ch[i].adpcm_history2_32 = read_16bit(block_offset + 0x0C + (i*0x04) + 0x02,streamFile); /* id, size, samples, hists-per-channel, stereo/interleaved data */
case coding_EA_XA:
/* the block can have padding so find the channel size from num_samples */ //case coding_EA_XA_V2: /* handled in default */
interleave = is_interleaved ? (block_samples / 28 * 0x0f) : 0; case coding_EA_XA_int:
for (i = 0; i < vgmstream->channels; i++) {
/* NOT channels*0x04, as seen in Superbike 2000 (PC) EA-XA v1 mono vids */ int is_interleaved = vgmstream->coding_type == coding_EA_XA_int;
vgmstream->ch[i].offset = block_offset + 0x0c + 2*0x04 + i*interleave; size_t interleave;
}
/* read ADPCM history from all channels before data (not actually read in sx.exe) */
break; //vgmstream->ch[i].adpcm_history1_32 = read_16bit(block_offset + 0x0C + (i*0x04) + 0x00,streamFile);
//vgmstream->ch[i].adpcm_history2_32 = read_16bit(block_offset + 0x0C + (i*0x04) + 0x02,streamFile);
/* id, size, samples, offsets-per-channel, flag (0x01 = data start), data */
case coding_EA_MT: /* the block can have padding so find the channel size from num_samples */
for (i = 0; i < vgmstream->channels; i++) { interleave = is_interleaved ? (block_samples / 28 * 0x0f) : 0;
off_t channel_start = read_32bit(block_offset + 0x0C + (0x04*i),streamFile);
vgmstream->ch[i].offset = block_offset + 0x0C + (0x04*vgmstream->channels) + channel_start + 0x01; /* NOT channels*0x04, as seen in Superbike 2000 (PC) EA-XA v1 mono vids */
} vgmstream->ch[i].offset = block_offset + 0x0c + 2*0x04 + i*interleave;
}
/* flush decoder in every block change */
flush_ea_mt(vgmstream); break;
break;
/* id, size, samples, offsets-per-channel, flag (0x01 = data start), data */
#ifdef VGM_USE_MPEG case coding_EA_MT:
/* id, size, samples, offsets, unknown (null for MP2, some size/config for EALayer3; only if not >2ch) */ for (i = 0; i < vgmstream->channels; i++) {
case coding_MPEG_custom: off_t channel_start = read_32bit(block_offset + 0x0C + (0x04*i),streamFile);
case coding_MPEG_layer1: vgmstream->ch[i].offset = block_offset + 0x0C + (0x04*vgmstream->channels) + channel_start + 0x01;
case coding_MPEG_layer2: }
case coding_MPEG_layer3:
case coding_MPEG_ealayer3: /* flush decoder in every block change */
for (i = 0; i < vgmstream->channels; i++) { flush_ea_mt(vgmstream);
off_t channel_start; break;
/* EALayer3 6ch uses 1ch*6 with offsets, no flag in header [Medal of Honor 2010 (PC) movies] */ #ifdef VGM_USE_MPEG
if (vgmstream->channels > 2) { /* id, size, samples, offsets, unknown (null for MP2, some size/config for EALayer3; only if not >2ch) */
channel_start = read_32bit(block_offset + 0x0C + 0x04*i,streamFile); case coding_MPEG_custom:
} else { case coding_MPEG_layer1:
channel_start = read_32bit(block_offset + 0x0C,streamFile); case coding_MPEG_layer2:
} case coding_MPEG_layer3:
case coding_MPEG_ealayer3:
vgmstream->ch[i].offset = block_offset + 0x0C + (0x04*vgmstream->channels) + channel_start; for (i = 0; i < vgmstream->channels; i++) {
} off_t channel_start;
/* SCHl with multiple SCHl need to reset their MPEG decoder as there are trailing samples in the buffers */ /* EALayer3 6ch uses 1ch*6 with offsets, no flag in header [Medal of Honor 2010 (PC) movies] */
if (new_schl) { if (vgmstream->channels > 2) {
flush_mpeg(vgmstream->codec_data); channel_start = read_32bit(block_offset + 0x0C + 0x04*i,streamFile);
} } else {
channel_start = read_32bit(block_offset + 0x0C,streamFile);
break; }
#endif
/* id, size, samples, offsets-per-channel, interleaved data (w/ optional hist per channel) */ vgmstream->ch[i].offset = block_offset + 0x0C + (0x04*vgmstream->channels) + channel_start;
default: }
for (i = 0; i < vgmstream->channels; i++) {
off_t channel_start = read_32bit(block_offset + 0x0C + (0x04*i),streamFile); /* SCHl with multiple SCHl need to reset their MPEG decoder as there are trailing samples in the buffers */
vgmstream->ch[i].offset = block_offset + 0x0C + (0x04*vgmstream->channels) + channel_start; if (new_schl) {
} flush_mpeg(vgmstream->codec_data);
}
/* read ADPCM history before each channel if needed (not actually read in sx.exe) */
if (vgmstream->codec_version == 1) { break;
for (i = 0; i < vgmstream->channels; i++) { #endif
//vgmstream->ch[i].adpcm_history1_32 = read_16bit(vgmstream->ch[i].offset+0x00,streamFile); /* id, size, samples, offsets-per-channel, interleaved data (w/ optional hist per channel) */
//vgmstream->ch[i].adpcm_history3_32 = read_16bit(vgmstream->ch[i].offset+0x02,streamFile); default:
vgmstream->ch[i].offset += 4; for (i = 0; i < vgmstream->channels; i++) {
} off_t channel_start = read_32bit(block_offset + 0x0C + (0x04*i),streamFile);
} vgmstream->ch[i].offset = block_offset + 0x0C + (0x04*vgmstream->channels) + channel_start;
}
break;
} /* read ADPCM history before each channel if needed (not actually read in sx.exe) */
if (vgmstream->codec_version == 1) {
vgmstream->current_block_offset = block_offset; for (i = 0; i < vgmstream->channels; i++) {
vgmstream->next_block_offset = block_offset + block_size; //vgmstream->ch[i].adpcm_history1_32 = read_16bit(vgmstream->ch[i].offset+0x00,streamFile);
vgmstream->current_block_samples = block_samples; //vgmstream->ch[i].adpcm_history3_32 = read_16bit(vgmstream->ch[i].offset+0x02,streamFile);
vgmstream->current_block_size = 0; /* uses current_block_samples instead */ vgmstream->ch[i].offset += 4;
} }
}
break;
}
}

View File

@ -1,25 +1,53 @@
#include "layout.h" #include "layout.h"
#include "../vgmstream.h" #include "../vgmstream.h"
/* set up for the block at the given offset */ /* GTA IV blocks */
void block_update_ivaud(off_t block_offset, VGMSTREAM * vgmstream) { void block_update_ivaud(off_t block_offset, VGMSTREAM * vgmstream) {
STREAMFILE *streamFile = vgmstream->ch[0].streamfile;
size_t header_size, block_samples;
int i; int i;
off_t start_offset; off_t seek_info_offset;
off_t interleave_size;
int32_t nextFrame=0;
STREAMFILE *streamFile=vgmstream->ch[0].streamfile;
vgmstream->current_block_offset = block_offset; /* base header */
seek_info_offset = read_32bitLE(block_offset+0x00,streamFile); /*64b */
/* 0x08(8): seek table offset */
/* 0x10(8): seek table offset again? */
nextFrame=(read_32bitLE(vgmstream->current_block_offset+0x28,streamFile)<<12)+0x800; /* seek info (per channel) */
/* 0x00: start entry */
/* 0x04: number of entries */
/* 0x08: unknown */
/* 0x0c: data size */
vgmstream->next_block_offset = vgmstream->current_block_offset + nextFrame; /* seek table (per all entries) */
vgmstream->current_block_size=read_32bitLE(block_offset+0x24,streamFile)/2; /* 0x00: start? */
/* 0x04: end? */
start_offset=vgmstream->current_block_offset + 0x800;
interleave_size=(read_32bitLE(block_offset+0x28,streamFile)<<12)/2;
for(i=0;i<vgmstream->channels;i++) { /* find header size */
vgmstream->ch[i].offset = start_offset + (i*interleave_size); /* can't see a better way to calc, as there may be dummy entries after usable ones
} * (table is max 0x7b8 + seek table offset + 0x800-padded) */
if (vgmstream->channels > 3)
header_size = 0x1000;
else
header_size = 0x800;
/* get max data_size as channels may vary slightly (data is padded, hopefully won't create pops) */
block_samples = 0;
for(i = 0;i < vgmstream->channels; i++) {
size_t channel_samples = read_32bitLE(block_offset + seek_info_offset+0x0c + 0x10*i,streamFile);
if (block_samples < channel_samples)
block_samples = channel_samples;
}
vgmstream->current_block_offset = block_offset;
vgmstream->next_block_offset = block_offset + vgmstream->full_block_size;
vgmstream->current_block_samples = block_samples;
vgmstream->current_block_size = 0;
for(i = 0; i < vgmstream->channels; i++) {
/* use seek table's start entry to find channel offset */
size_t interleave_size = read_32bitLE(block_offset + seek_info_offset+0x00 + 0x10*i,streamFile) * 0x800;
vgmstream->ch[i].offset = block_offset + header_size + interleave_size;
}
} }

View File

@ -1,19 +1,17 @@
#include "layout.h" #include "layout.h"
#include "../vgmstream.h" #include "../vgmstream.h"
/* set up for the block at the given offset */ /* The Bouncer STRx blocks, one block per channel when stereo */
void block_update_ps2_strlr(off_t block_offset, VGMSTREAM * vgmstream) { void block_update_ps2_strlr(off_t block_offset, VGMSTREAM * vgmstream) {
STREAMFILE* streamFile = vgmstream->ch[0].streamfile;
int i; int i;
vgmstream->current_block_offset = block_offset; vgmstream->current_block_offset = block_offset;
vgmstream->current_block_size = read_32bitLE( vgmstream->current_block_size = read_32bitLE(block_offset+0x04,streamFile); /* can be smaller than 0x800 */
vgmstream->current_block_offset+0x4, vgmstream->next_block_offset = block_offset + 0x800*vgmstream->channels;
vgmstream->ch[0].streamfile)*2; /* 0x08: number of remaning blocks, 0x10: some id/size? (shared in all blocks) */
vgmstream->next_block_offset = vgmstream->current_block_offset+vgmstream->current_block_size+0x40;
//vgmstream->current_block_size/=vgmstream->channels;
for (i=0;i<vgmstream->channels;i++) { for (i = 0; i < vgmstream->channels; i++) {
vgmstream->ch[i].offset = vgmstream->current_block_offset+0x20+(0x800*i); vgmstream->ch[i].offset = block_offset + 0x20 + 0x800*i;
} }
} }

View File

@ -48,9 +48,8 @@ VGMSTREAM * init_vgmstream_ahx(STREAMFILE *streamFile) {
{ {
#ifdef VGM_USE_MPEG #ifdef VGM_USE_MPEG
mpeg_custom_config cfg; mpeg_custom_config cfg = {0};
memset(&cfg, 0, sizeof(mpeg_custom_config));
cfg.encryption = read_8bit(0x13,streamFile); /* 0x08 = keyword encryption */ cfg.encryption = read_8bit(0x13,streamFile); /* 0x08 = keyword encryption */
cfg.cri_type = type; cfg.cri_type = type;

View File

@ -28,7 +28,7 @@ static int parse_awc_header(STREAMFILE* streamFile, awc_header* awc);
/* AWC - from RAGE (Rockstar Advanced Game Engine) audio (Red Dead Redemption, Max Payne 3, GTA5) */ /* AWC - from RAGE (Rockstar Advanced Game Engine) audio (Red Dead Redemption, Max Payne 3, GTA5) */
VGMSTREAM * init_vgmstream_awc(STREAMFILE *streamFile) { VGMSTREAM * init_vgmstream_awc(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL; VGMSTREAM * vgmstream = NULL;
awc_header awc; awc_header awc = {0};
/* check extension */ /* check extension */
if (!check_extensions(streamFile,"awc")) if (!check_extensions(streamFile,"awc"))
@ -137,8 +137,7 @@ VGMSTREAM * init_vgmstream_awc(STREAMFILE *streamFile) {
#endif #endif
#ifdef VGM_USE_MPEG #ifdef VGM_USE_MPEG
case 0x07: { /* MPEG (PS3) */ case 0x07: { /* MPEG (PS3) */
mpeg_custom_config cfg; mpeg_custom_config cfg = {0};
memset(&cfg, 0, sizeof(mpeg_custom_config));
cfg.chunk_size = awc.block_chunk; cfg.chunk_size = awc.block_chunk;
cfg.big_endian = awc.big_endian; cfg.big_endian = awc.big_endian;
@ -183,8 +182,6 @@ static int parse_awc_header(STREAMFILE* streamFile, awc_header* awc) {
off_t off; off_t off;
int target_subsong = streamFile->stream_index; int target_subsong = streamFile->stream_index;
memset(awc,0,sizeof(awc_header));
/* check header */ /* check header */
if (read_32bitBE(0x00,streamFile) != 0x41444154 && /* "ADAT" (LE) */ if (read_32bitBE(0x00,streamFile) != 0x41444154 && /* "ADAT" (LE) */

View File

@ -36,8 +36,8 @@ VGMSTREAM * init_vgmstream_bik(STREAMFILE *streamFile) {
#ifdef VGM_USE_FFMPEG #ifdef VGM_USE_FFMPEG
{ {
ffmpeg_custom_config cfg; ffmpeg_custom_config cfg = {0};
memset(&cfg, 0, sizeof(ffmpeg_custom_config));
cfg.stream_index = stream_index; cfg.stream_index = stream_index;
vgmstream->codec_data = init_ffmpeg_config(streamFile, NULL,0, 0x0,get_streamfile_size(streamFile), &cfg); vgmstream->codec_data = init_ffmpeg_config(streamFile, NULL,0, 0x0,get_streamfile_size(streamFile), &cfg);

View File

@ -108,22 +108,26 @@ fail:
return NULL; return NULL;
} }
/* .SPS? - from Frostbite engine games? [Need for Speed Rivals (PS4)], v1 header */ /* .SPS - from Frostbite engine games, v1 header */
VGMSTREAM * init_vgmstream_ea_sps_fb(STREAMFILE *streamFile) { VGMSTREAM * init_vgmstream_ea_sps_fb(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL; VGMSTREAM * vgmstream = NULL;
off_t start_offset = 0, header_offset = 0, sps_offset, max_offset; off_t start_offset = 0, header_offset = 0, sps_offset, max_offset;
/* checks */ /* checks */
/* assumed to be .sps (no extensions in the archives) */ /* should be .sps once extracted (filenames are hashed) */
if (!check_extensions(streamFile,"sps")) if (!check_extensions(streamFile,"sps"))
goto fail; goto fail;
if (read_32bitBE(0x00,streamFile) != 0x011006C0) if (read_32bitBE(0x00,streamFile) != 0x011006C0 && /* Need for Speed: The Run (PS3), Need for Speed: Rivals (PS4) */
read_32bitBE(0x00,streamFile) != 0x01100180 && /* Need for Speed: The Run (X360) */
read_32bitBE(0x00,streamFile) != 0x01100000) /* Need for Speed: The Run (PC) */
goto fail; goto fail;
/* file has a Frostbite descriptor (SoundWaveAsset segments) data before actual .sps, exact size unknown.
* 0x00: segments/flags/sizes? 0x04: SegmentLength?, 0x08: SeekTableOffset?, 0x0c: mini SPS header
* rest: unknown fields? may be padded? (ex. 0x22 > 0x24, 0x1d > 0x20 */
/* file has some kind of data before .sps, exact offset unknown. /* actual offsets are probably somewhere but for now just manually search. */
* Actual offsets are probably somewhere but for now just manually search. */ sps_offset = read_32bitBE(0x08, streamFile); /* seek table, number of entries unknown */
sps_offset = read_32bitBE(0x08, streamFile); /* points to some kind of table, number of entries unknown */
max_offset = sps_offset + 0x3000; max_offset = sps_offset + 0x3000;
if (max_offset > get_streamfile_size(streamFile)) if (max_offset > get_streamfile_size(streamFile))
max_offset = get_streamfile_size(streamFile); max_offset = get_streamfile_size(streamFile);
@ -171,7 +175,7 @@ static VGMSTREAM * init_vgmstream_eaaudiocore_header(STREAMFILE * streamHead, ST
/* rest is optional, depends on flags header used (ex. SNU and SPS may have bigger headers): /* rest is optional, depends on flags header used (ex. SNU and SPS may have bigger headers):
* &0x20: 1 int (usually 0x00), &0x00/40: nothing, &0x60: 2 ints (usually 0x00 and 0x14) */ * &0x20: 1 int (usually 0x00), &0x00/40: nothing, &0x60: 2 ints (usually 0x00 and 0x14) */
/* V0: SNR+SNS, V1: SPR+SPS (not apparent differences, other than the block flags used) */ /* V0: SNR+SNS, V1: SPR+SPS (no apparent differences, other than the block flags used) */
if (version != 0 && version != 1) { if (version != 0 && version != 1) {
VGM_LOG("EA SNS/SPS: unknown version\n"); VGM_LOG("EA SNS/SPS: unknown version\n");
goto fail; goto fail;

View File

@ -39,7 +39,7 @@ static size_t eaac_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset,
off_t intrablock_offset, intradata_offset; off_t intrablock_offset, intradata_offset;
uint32_t block_flag, block_size, data_size, skip_size; uint32_t block_flag, block_size, data_size, skip_size;
block_flag = read_8bit(data->physical_offset+0x00,streamfile); block_flag = (uint8_t)read_8bit(data->physical_offset+0x00,streamfile);
block_size = read_32bitBE(data->physical_offset+0x00,streamfile) & 0x00FFFFFF; block_size = read_32bitBE(data->physical_offset+0x00,streamfile) & 0x00FFFFFF;
if (data->version == 1 && block_flag == 0x48) { if (data->version == 1 && block_flag == 0x48) {
@ -47,7 +47,7 @@ static size_t eaac_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset,
continue; /* skip header block */ continue; /* skip header block */
} }
if (data->version == 1 && block_flag == 0x45) if (data->version == 1 && block_flag == 0x45)
return total_read; /* stop on last block (always empty) */ break; /* stop on last block (always empty) */
switch(data->codec) { switch(data->codec) {
#if 0 #if 0
@ -92,13 +92,13 @@ static size_t eaac_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset,
if (to_read > length) if (to_read > length)
to_read = length; to_read = length;
if (to_read == 0) if (to_read == 0)
return total_read; /* should never happen... */ break; /* should never happen... */
/* finally read and move buffer/offsets */ /* finally read and move buffer/offsets */
bytes_read = read_streamfile(dest, data->physical_offset + intrablock_offset, to_read, streamfile); bytes_read = read_streamfile(dest, data->physical_offset + intrablock_offset, to_read, streamfile);
total_read += bytes_read; total_read += bytes_read;
if (bytes_read != to_read) if (bytes_read != to_read)
return total_read; /* couldn't read fully */ break; /* couldn't read fully */
dest += bytes_read; dest += bytes_read;
offset += bytes_read; offset += bytes_read;
@ -131,7 +131,7 @@ static size_t eaac_io_size(STREAMFILE *streamfile, eaac_io_data* data) {
while (physical_offset < max_physical_offset) { while (physical_offset < max_physical_offset) {
uint32_t block_flag, block_size, data_size; uint32_t block_flag, block_size, data_size;
block_flag = read_8bit(physical_offset+0x00,streamfile); block_flag = (uint8_t)read_8bit(physical_offset+0x00,streamfile);
block_size = read_32bitBE(physical_offset+0x00,streamfile) & 0x00FFFFFF; block_size = read_32bitBE(physical_offset+0x00,streamfile) & 0x00FFFFFF;
if (data->version == 0 && block_flag != 0x00 && block_flag != 0x80) if (data->version == 0 && block_flag != 0x00 && block_flag != 0x80)

View File

@ -10,7 +10,7 @@
#define EA_VERSION_V3 0x03 // ~PS2 era #define EA_VERSION_V3 0x03 // ~PS2 era
/* platform constants (unasigned values seem internal only) */ /* platform constants (unasigned values seem internal only) */
#define EA_PLATFORM_GENERIC -1 // typically Wii/X360/PS3 #define EA_PLATFORM_GENERIC -1 // typically Wii/X360/PS3/videos
#define EA_PLATFORM_PC 0x00 #define EA_PLATFORM_PC 0x00
#define EA_PLATFORM_PSX 0x01 #define EA_PLATFORM_PSX 0x01
#define EA_PLATFORM_N64 0x02 #define EA_PLATFORM_N64 0x02
@ -21,6 +21,7 @@
#define EA_PLATFORM_XBOX 0x07 #define EA_PLATFORM_XBOX 0x07
#define EA_PLATFORM_X360 0x09 // also "Xenon" #define EA_PLATFORM_X360 0x09 // also "Xenon"
#define EA_PLATFORM_PSP 0x0A #define EA_PLATFORM_PSP 0x0A
#define EA_PLATFORM_PS3 0x0E // very rare [Need for Speed: Carbon (PS3)]
#define EA_PLATFORM_3DS 0x14 #define EA_PLATFORM_3DS 0x14
/* codec constants (undefined are probably reserved, ie.- sx.exe encodes PCM24/DVI but no platform decodes them) */ /* codec constants (undefined are probably reserved, ie.- sx.exe encodes PCM24/DVI but no platform decodes them) */
@ -350,11 +351,19 @@ static VGMSTREAM * init_vgmstream_ea_variable_header(STREAMFILE *streamFile, ea_
#endif #endif
case EA_CODEC2_MT10: /* MicroTalk (10:1 compression) */ case EA_CODEC2_MT10: /* MicroTalk (10:1 compression) */
case EA_CODEC2_MT5: /* MicroTalk (5:1 compression) */ case EA_CODEC2_MT5: { /* MicroTalk (5:1 compression) */
int use_pcm_blocks = 0;
if (ea->version == EA_VERSION_V3 || (ea->version == EA_VERSION_V2 &&
(ea->platform == EA_PLATFORM_PC || ea->platform == EA_PLATFORM_MAC))) {
use_pcm_blocks = 1;
}
vgmstream->coding_type = coding_EA_MT; vgmstream->coding_type = coding_EA_MT;
vgmstream->codec_data = init_ea_mt(vgmstream->channels, ea->version == EA_VERSION_V3); vgmstream->codec_data = init_ea_mt(vgmstream->channels, use_pcm_blocks);
if (!vgmstream->codec_data) goto fail; if (!vgmstream->codec_data) goto fail;
break; break;
}
case EA_CODEC2_ATRAC3PLUS: /* regular ATRAC3plus chunked in SCxx blocks, including RIFF header */ case EA_CODEC2_ATRAC3PLUS: /* regular ATRAC3plus chunked in SCxx blocks, including RIFF header */
default: default:
@ -509,6 +518,7 @@ static int parse_variable_header(STREAMFILE* streamFile, ea_header* ea, off_t be
case 0x13: /* effect bus (0..127) */ case 0x13: /* effect bus (0..127) */
case 0x14: /* emdedded user data (free size/value) */ case 0x14: /* emdedded user data (free size/value) */
case 0x19: /* related to playback envelope (BNK only) */ case 0x19: /* related to playback envelope (BNK only) */
case 0x1A: /* unknown and very rare, size 0 (BNK only) [SSX3 (PS2)] */
case 0x1B: /* unknown (movie only?) */ case 0x1B: /* unknown (movie only?) */
case 0x1C: /* initial envelope volume (BNK only) */ case 0x1C: /* initial envelope volume (BNK only) */
case 0x24: /* master random detune range (BNK only) */ case 0x24: /* master random detune range (BNK only) */
@ -608,7 +618,7 @@ static int parse_variable_header(STREAMFILE* streamFile, ea_header* ea, off_t be
case 0x9F: /* azimuth ch4 */ case 0x9F: /* azimuth ch4 */
case 0xA6: /* azimuth ch5 */ case 0xA6: /* azimuth ch5 */
case 0xA7: /* azimuth ch6 */ case 0xA7: /* azimuth ch6 */
case 0xA1: /* unknown and very rare, always 0x02 (FIFA 2001 PS2) */ case 0xA1: /* unknown and very rare, always 0x02 [FIFA 2001 (PS2)] */
read_patch(streamFile, &offset); read_patch(streamFile, &offset);
break; break;
@ -636,6 +646,7 @@ static int parse_variable_header(STREAMFILE* streamFile, ea_header* ea, off_t be
|| ea->platform == EA_PLATFORM_SAT || ea->platform == EA_PLATFORM_SAT
|| ea->platform == EA_PLATFORM_GC_WII || ea->platform == EA_PLATFORM_GC_WII
|| ea->platform == EA_PLATFORM_X360 || ea->platform == EA_PLATFORM_X360
|| ea->platform == EA_PLATFORM_PS3
|| ea->platform == EA_PLATFORM_GENERIC) { || ea->platform == EA_PLATFORM_GENERIC) {
ea->big_endian = 1; ea->big_endian = 1;
} }
@ -658,6 +669,7 @@ static int parse_variable_header(STREAMFILE* streamFile, ea_header* ea, off_t be
case EA_PLATFORM_XBOX: ea->version = EA_VERSION_V2; break; case EA_PLATFORM_XBOX: ea->version = EA_VERSION_V2; break;
case EA_PLATFORM_X360: ea->version = EA_VERSION_V3; break; case EA_PLATFORM_X360: ea->version = EA_VERSION_V3; break;
case EA_PLATFORM_PSP: ea->version = EA_VERSION_V3; break; case EA_PLATFORM_PSP: ea->version = EA_VERSION_V3; break;
case EA_PLATFORM_PS3: ea->version = EA_VERSION_V3; break;
case EA_PLATFORM_3DS: ea->version = EA_VERSION_V3; break; case EA_PLATFORM_3DS: ea->version = EA_VERSION_V3; break;
case EA_PLATFORM_GENERIC: ea->version = EA_VERSION_V2; break; case EA_PLATFORM_GENERIC: ea->version = EA_VERSION_V2; break;
default: default:
@ -707,6 +719,7 @@ static int parse_variable_header(STREAMFILE* streamFile, ea_header* ea, off_t be
case EA_PLATFORM_XBOX: ea->codec2 = EA_CODEC2_S16LE; break; case EA_PLATFORM_XBOX: ea->codec2 = EA_CODEC2_S16LE; break;
case EA_PLATFORM_X360: ea->codec2 = EA_CODEC2_EAXA; break; case EA_PLATFORM_X360: ea->codec2 = EA_CODEC2_EAXA; break;
case EA_PLATFORM_PSP: ea->codec2 = EA_CODEC2_EAXA; break; case EA_PLATFORM_PSP: ea->codec2 = EA_CODEC2_EAXA; break;
case EA_PLATFORM_PS3: ea->codec2 = EA_CODEC2_EAXA; break;
case EA_PLATFORM_3DS: ea->codec2 = EA_CODEC2_GCADPCM; break; case EA_PLATFORM_3DS: ea->codec2 = EA_CODEC2_GCADPCM; break;
default: default:
VGM_LOG("EA SCHl: unknown default codec2 for platform 0x%02x\n", ea->platform); VGM_LOG("EA SCHl: unknown default codec2 for platform 0x%02x\n", ea->platform);
@ -728,6 +741,7 @@ static int parse_variable_header(STREAMFILE* streamFile, ea_header* ea, off_t be
case EA_PLATFORM_XBOX: ea->sample_rate = 24000; break; case EA_PLATFORM_XBOX: ea->sample_rate = 24000; break;
case EA_PLATFORM_X360: ea->sample_rate = 44100; break; case EA_PLATFORM_X360: ea->sample_rate = 44100; break;
case EA_PLATFORM_PSP: ea->sample_rate = 22050; break; case EA_PLATFORM_PSP: ea->sample_rate = 22050; break;
case EA_PLATFORM_PS3: ea->sample_rate = 44100; break;
//case EA_PLATFORM_3DS: ea->sample_rate = 44100; break;//todo (not 22050/16000) //case EA_PLATFORM_3DS: ea->sample_rate = 44100; break;//todo (not 22050/16000)
default: default:
VGM_LOG("EA SCHl: unknown default sample rate for platform 0x%02x\n", ea->platform); VGM_LOG("EA SCHl: unknown default sample rate for platform 0x%02x\n", ea->platform);

View File

@ -26,7 +26,7 @@ VGMSTREAM * init_vgmstream_ea_schl_fixed(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL; VGMSTREAM * vgmstream = NULL;
off_t start_offset; off_t start_offset;
size_t header_size; size_t header_size;
ea_header ea; ea_header ea = {0};
/* check extension */ /* check extension */
@ -94,8 +94,6 @@ fail:
static int parse_fixed_header(STREAMFILE* streamFile, ea_header* ea, off_t begin_offset) { static int parse_fixed_header(STREAMFILE* streamFile, ea_header* ea, off_t begin_offset) {
off_t offset = begin_offset; off_t offset = begin_offset;
memset(ea,0,sizeof(ea_header));
if (read_32bitBE(offset+0x00, streamFile) != 0x5041546C && /* "PATl" */ if (read_32bitBE(offset+0x00, streamFile) != 0x5041546C && /* "PATl" */
read_32bitBE(offset+0x38, streamFile) != 0x544D706C) /* "TMpl" */ read_32bitBE(offset+0x38, streamFile) != 0x544D706C) /* "TMpl" */
goto fail; goto fail;

View File

@ -34,8 +34,10 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) {
* type 0x00 has an extra field (always 0?) at 0x1c */ * type 0x00 has an extra field (always 0?) at 0x1c */
BaseHeaderLength = (Version==0x00) ? 0x40 : 0x3C; BaseHeaderLength = (Version==0x00) ? 0x40 : 0x3C;
if ((SampleHeaderLength + NameTableLength + SampleDataLength + BaseHeaderLength) != get_streamfile_size(streamFile)) if ((SampleHeaderLength + NameTableLength + SampleDataLength + BaseHeaderLength) != get_streamfile_size(streamFile)) {
VGM_LOG("FSB5: bad size (%x + %x + %x + %x != %x)\n", SampleHeaderLength, NameTableLength, SampleDataLength, BaseHeaderLength, get_streamfile_size(streamFile));
goto fail; goto fail;
}
if (TargetSubsong == 0) TargetSubsong = 1; /* default to 1 */ if (TargetSubsong == 0) TargetSubsong = 1; /* default to 1 */
if (TargetSubsong > TotalSubsongs || TotalSubsongs <= 0) goto fail; if (TargetSubsong > TotalSubsongs || TotalSubsongs <= 0) goto fail;

View File

@ -238,7 +238,13 @@ static const hcakey_info hcakey_list[] = {
{3201512}, // 000000000030D9E8 {3201512}, // 000000000030D9E8
// PriPara: All Idol Perfect Stage (Takara Tomy) [Switch] // PriPara: All Idol Perfect Stage (Takara Tomy) [Switch]
{217735759}, // 000000000CFA624F {217735759}, // 000000000CFA624F
// Space Invaders Extreme (Taito Corporation, Backbone Entertainment) [PC]
{91380310056}, // 0000001546B0E028
// CR Another God Hades Advent (Universal Entertainment Corporation) [iOS/Android]
{64813795}, // 0000000003DCFAE3
}; };

View File

@ -2,67 +2,195 @@
#include "../layout/layout.h" #include "../layout/layout.h"
#include "../util.h" #include "../util.h"
/* ivaud (from GTA IV (PC)) */ typedef struct {
int is_music;
int total_subsongs;
int channel_count;
int sample_rate;
int codec;
int num_samples;
size_t block_count;
size_t block_size;
off_t stream_offset;
size_t stream_size;
} ivaud_header;
static int parse_ivaud_header(STREAMFILE* streamFile, ivaud_header* ivaud);
/* .ivaud - from GTA IV (PC) */
VGMSTREAM * init_vgmstream_ivaud(STREAMFILE *streamFile) { VGMSTREAM * init_vgmstream_ivaud(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL; VGMSTREAM * vgmstream = NULL;
ivaud_header ivaud = {0};
int loop_flag;
char filename[PATH_LIMIT]; /* checks */
off_t start_offset; /* (hashed filenames are likely extensionless and .ivaud is added by tools) */
off_t block_table_offset; if (!check_extensions(streamFile, "ivaud,"))
int loop_flag = 0; goto fail;
int channel_count;
int i; /* check header */
if (!parse_ivaud_header(streamFile, &ivaud))
goto fail;
/* at this time, i only check for extension */
/* i'll make further checks later */
streamFile->get_name(streamFile,filename,sizeof(filename));
if (strcasecmp("ivaud",filename_extension(filename))) goto fail;
/* multiple sounds .ivaud files are not implemented */
/* only used for voices & sfx */
if(read_32bitLE(0x10,streamFile)!=0)
goto fail;
/* never looped and allways 2 channels */
loop_flag = 0; loop_flag = 0;
channel_count = 2;
/* build the VGMSTREAM */ /* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag); vgmstream = allocate_vgmstream(ivaud.channel_count,loop_flag);
if (!vgmstream) goto fail; if (!vgmstream) goto fail;
/* fill in the vital statistics */ vgmstream->sample_rate = ivaud.sample_rate;
block_table_offset = read_32bitLE(0,streamFile); vgmstream->num_samples = ivaud.num_samples;
vgmstream->channels = channel_count; vgmstream->num_streams = ivaud.total_subsongs;
vgmstream->sample_rate = read_32bitLE(block_table_offset + 0x04,streamFile); vgmstream->stream_size = ivaud.stream_size;
vgmstream->coding_type = coding_IMA_int; vgmstream->meta_type = meta_IVAUD;
vgmstream->layout_type = layout_blocked_ivaud; switch(ivaud.codec) {
vgmstream->meta_type = meta_PC_IVAUD; case 0x0001: /* common in sfx, uncommon in music (ex. EP2_SFX/MENU_MUSIC) */
vgmstream->coding_type = coding_PCM16LE;
vgmstream->layout_type = ivaud.is_music ? layout_blocked_ivaud : layout_none;
vgmstream->full_block_size = ivaud.block_size;
break;
/* open the file for reading */ case 0x0400:
{ vgmstream->coding_type = coding_IMA_int;
for (i=0;i<channel_count;i++) { vgmstream->layout_type = ivaud.is_music ? layout_blocked_ivaud : layout_none;
vgmstream->ch[i].streamfile = streamFile->open(streamFile,filename,0x2000); vgmstream->full_block_size = ivaud.block_size;
if (!vgmstream->ch[i].streamfile) goto fail; break;
}
default:
VGM_LOG("IVAUD: unknown codec 0x%x\n", ivaud.codec);
goto fail;
} }
/* Calc num_samples */
start_offset = read_32bitLE(0x2C,streamFile);
//block_count = read_32bitLE(0x08,streamFile);
vgmstream->next_block_offset = read_32bitLE(0x2C,streamFile);
// to avoid troubles with "extra" samples
vgmstream->num_samples=((read_32bitLE(0x60,streamFile)/2)*2);
block_update_ivaud(start_offset,vgmstream); if (!vgmstream_open_stream(vgmstream,streamFile,ivaud.stream_offset))
goto fail;
if (vgmstream->layout_type == layout_blocked_ivaud)
block_update_ivaud(ivaud.stream_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;
} }
/* Parse Rockstar's .ivaud header (much info from SparkIV). */
static int parse_ivaud_header(STREAMFILE* streamFile, ivaud_header* ivaud) {
int target_subsong = streamFile->stream_index;
/* use bank's stream count to detect */
ivaud->is_music = (read_32bitLE(0x10,streamFile) == 0);
if (ivaud->is_music) {
off_t block_table_offset, channel_table_offset, channel_info_offset;
/* music header */
block_table_offset = read_32bitLE(0x00,streamFile); /* 64b */
ivaud->block_count = read_32bitLE(0x08,streamFile);
ivaud->block_size = read_32bitLE(0x0c,streamFile); /* 64b, uses padded blocks */
channel_table_offset = read_32bitLE(0x14,streamFile); /* 64b */
/* 0x1c(8): block_table_offset again? */
ivaud->channel_count = read_32bitLE(0x24,streamFile);
/* 0x28(4): unknown entries? */
ivaud->stream_offset = read_32bitLE(0x2c,streamFile);
channel_info_offset = channel_table_offset + ivaud->channel_count*0x10;
if ((ivaud->block_count * ivaud->block_size) + ivaud->stream_offset != get_streamfile_size(streamFile)) {
VGM_LOG("IVAUD: bad file size\n");
goto fail;
}
/* channel table (one entry per channel, points to channel info) */
/* 0x00(8): offset within channel_info_offset */
/* 0x08(4): hash */
/* 0x0c(4): size */
/* channel info (one entry per channel) */
/* 0x00(8): offset within data (should be 0) */
/* 0x08(4): hash */
/* 0x0c(4): half num_samples? */
ivaud->num_samples = read_32bitLE(channel_info_offset+0x10,streamFile);
/* 0x14(4): unknown (-1) */
/* 0x18(2): sample rate */
/* 0x1a(2): unknown */
ivaud->codec = read_32bitLE(channel_info_offset+0x1c,streamFile);
/* (when codec is IMA) */
/* 0x20(8): adpcm states offset, 0x38: num states? (reference for seeks?) */
/* rest: unknown data */
/* block table (one entry per block) */
/* 0x00: data size processed up to this block (doesn't count block padding) */
ivaud->sample_rate = read_32bitLE(block_table_offset + 0x04,streamFile);
/* sample_rate should agree with each channel in the channel table */
ivaud->total_subsongs = 1;
ivaud->stream_size = get_streamfile_size(streamFile);
}
else {
off_t stream_table_offset, stream_info_offset, stream_entry_offset;
/* bank header */
stream_table_offset = read_32bitLE(0x00,streamFile); /* 64b */
/* 0x08(8): header size? start offset? */
ivaud->total_subsongs = read_32bitLE(0x10,streamFile);
/* 0x14(4): unknown */
ivaud->stream_offset = read_32bitLE(0x18,streamFile); /* base start_offset */
if (target_subsong == 0) target_subsong = 1;
if (target_subsong < 0 || target_subsong > ivaud->total_subsongs || ivaud->total_subsongs < 1) goto fail;
if (stream_table_offset != 0x1c)
goto fail;
stream_info_offset = stream_table_offset + 0x10*ivaud->total_subsongs;
/* stream table (one entry per stream, points to stream info) */
stream_entry_offset = read_32bitLE(stream_table_offset + 0x10*(target_subsong-1) + 0x00,streamFile); /* within stream info */
/* 0x00(8): offset within stream_info_offset */
/* 0x08(4): hash */
/* 0x0c(4): size */
/* stream info (one entry per stream) */
ivaud->stream_offset += read_32bitLE(stream_info_offset+stream_entry_offset+0x00,streamFile); /* 64b, within data */
/* 0x08(4): hash */
/* 0x0c(4): half num_samples? */
ivaud->num_samples = read_32bitLE(stream_info_offset+stream_entry_offset+0x10,streamFile);
/* 0x14(4): unknown (-1) */
ivaud->sample_rate = (uint16_t)read_16bitLE(stream_info_offset+stream_entry_offset+0x18,streamFile);
/* 0x1a(2): unknown */
ivaud->codec = read_32bitLE(stream_info_offset+stream_entry_offset+0x1c,streamFile);
/* (when codec is IMA) */
/* 0x20(8): adpcm states offset, 0x38: num states? (reference for seeks?) */
/* rest: unknown data */
ivaud->channel_count = 1;
/* ghetto size calculator (could substract offsets but streams are not ordered) */
switch(ivaud->codec) {
case 0x0001:
ivaud->stream_size = ivaud->num_samples * 2; /* double 16b PCM */
break;
case 0x0400:
ivaud->stream_size = ivaud->num_samples / 2; /* half nibbles */
break;
default:
break;
}
}
return 1;
fail:
return 0;
}

View File

@ -1,66 +1,52 @@
#include "meta.h" #include "meta.h"
#include "../util.h" #include "../coding/coding.h"
/* bxaimc - 2009-03-05
- RRDS - found in Ridge Racer DS */
/* RRDS - from (some) NST games [Ridge Racer (DS), Metroid Prime Hunters - First Hunt (DS)] */
VGMSTREAM * init_vgmstream_nds_rrds(STREAMFILE *streamFile) { VGMSTREAM * init_vgmstream_nds_rrds(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL; VGMSTREAM * vgmstream = NULL;
char filename[PATH_LIMIT];
int channel_count;
int loop_flag;
off_t start_offset; off_t start_offset;
int loop_flag, channel_count;
/* check extension, case insensitive (made-up extension) */ /* checks */
streamFile->get_name(streamFile,filename,sizeof(filename)); /* .rrds: made-up extension (files come from a bigfile and don't have filenames/extension) */
if (strcasecmp("rrds",filename_extension(filename))) goto fail; if (!check_extensions(streamFile, ",rrds"))
goto fail;
/* check size */ if ((read_32bitLE(0x00,streamFile)+0x18) != get_streamfile_size(streamFile))
if ((read_32bitLE(0x0,streamFile)+0x18) != get_streamfile_size(streamFile)) goto fail;
goto fail;
/* check type details */ loop_flag = (read_32bitLE(0x14,streamFile) != 0); //todo not correct for MPH: First Hunt?
loop_flag = (read_32bitLE(0x14,streamFile) != 0);
channel_count = 1; channel_count = 1;
start_offset = 0x1c;
/* build the VGMSTREAM */
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag); vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail; if (!vgmstream) goto fail;
/* fill in the vital statistics */
start_offset = 0x1c;
vgmstream->num_samples = (read_32bitLE(0x0,streamFile)-start_offset) / channel_count * 2;
vgmstream->sample_rate = read_32bitLE(0x8,streamFile);
if (loop_flag) { vgmstream->sample_rate = read_32bitLE(0x08,streamFile);
vgmstream->loop_start_sample = (read_32bitLE(0x14,streamFile)-start_offset) / channel_count * 2; vgmstream->num_samples = ima_bytes_to_samples(read_32bitLE(0x00,streamFile)-start_offset,channel_count);
vgmstream->loop_end_sample = vgmstream->num_samples; if (loop_flag) {
} vgmstream->loop_start_sample = ima_bytes_to_samples(read_32bitLE(0x14,streamFile)-start_offset,channel_count);
vgmstream->loop_end_sample = vgmstream->num_samples;
vgmstream->coding_type = coding_IMA_int; }
vgmstream->meta_type = meta_NDS_RRDS; vgmstream->meta_type = meta_NDS_RRDS;
vgmstream->coding_type = coding_IMA_int;
vgmstream->layout_type = layout_none; vgmstream->layout_type = layout_none;
/* open the file for reading */
{ {
STREAMFILE * file;
file = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE);
if (!file) goto fail;
vgmstream->ch[0].streamfile = file;
vgmstream->ch[0].channel_start_offset=
vgmstream->ch[0].offset=start_offset;
/* one NDS IMA header for whole stream */
vgmstream->ch[0].adpcm_history1_16 = read_16bitLE(0x18,streamFile); vgmstream->ch[0].adpcm_history1_16 = read_16bitLE(0x18,streamFile);
vgmstream->ch[0].adpcm_step_index = read_16bitLE(0x1a,streamFile); vgmstream->ch[0].adpcm_step_index = read_16bitLE(0x1a,streamFile);
if (vgmstream->ch[0].adpcm_step_index < 0 || vgmstream->ch[0].adpcm_step_index > 88) goto fail; if (vgmstream->ch[0].adpcm_step_index < 0 || vgmstream->ch[0].adpcm_step_index > 88)
goto fail;
} }
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
goto fail;
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;
} }

View File

@ -226,6 +226,28 @@ static void gwm_ogg_decryption_callback(void *ptr, size_t size, size_t nmemb, vo
} }
} }
static void mus_ogg_decryption_callback(void *ptr, size_t size, size_t nmemb, void *datasource) {
static const uint8_t key[16] = {
0x21,0x4D,0x6F,0x01,0x20,0x4C,0x6E,0x02,0x1F,0x4B,0x6D,0x03,0x20,0x4C,0x6E,0x02
};
size_t bytes_read = size*nmemb;
ogg_vorbis_streamfile * const ov_streamfile = datasource;
int i;
char *header_id = "OggS";
/* bytes are xor'd with key, first "OggS" is changed */
for (i = 0; i < bytes_read; i++) {
if (ov_streamfile->offset+i < 0x04) { /* if decrypted gives "Mus " */
((uint8_t*)ptr)[i] = (uint8_t)header_id[(ov_streamfile->offset + i) % 4];
}
else {
((uint8_t*)ptr)[i] ^= key[(ov_streamfile->offset + i) % sizeof(key)];
}
}
}
/* Ogg Vorbis, by way of libvorbisfile; may contain loop comments */ /* Ogg Vorbis, by way of libvorbisfile; may contain loop comments */
VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) { VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) {
ogg_vorbis_meta_info_t ovmi = {0}; ogg_vorbis_meta_info_t ovmi = {0};
@ -239,6 +261,7 @@ VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) {
int is_rpgmvo = 0; int is_rpgmvo = 0;
int is_eno = 0; int is_eno = 0;
int is_gwm = 0; int is_gwm = 0;
int is_mus = 0;
/* check extension */ /* check extension */
@ -262,6 +285,8 @@ VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) {
is_eno = 1; is_eno = 1;
} else if (check_extensions(streamFile,"gwm")) { /* .gwm: Adagio: Cloudburst (PC) */ } else if (check_extensions(streamFile,"gwm")) { /* .gwm: Adagio: Cloudburst (PC) */
is_gwm = 1; is_gwm = 1;
} else if (check_extensions(streamFile,"mus")) { /* .mus: Redux - Dark Matters (PC) */
is_mus = 1;
} else { } else {
goto fail; goto fail;
} }
@ -355,7 +380,6 @@ VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) {
start_offset = 0x01; start_offset = 0x01;
} }
/* check GWM [Adagio: Cloudburst (PC)], encrypted */ /* check GWM [Adagio: Cloudburst (PC)], encrypted */
if (is_gwm) { if (is_gwm) {
ovmi.xor_value = 0x5D; ovmi.xor_value = 0x5D;
@ -363,6 +387,12 @@ VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) {
ovmi.meta_type = meta_OGG_GWM; ovmi.meta_type = meta_OGG_GWM;
} }
/* check .mus [Redux - Dark Matters (PC)], encrypted */
if (is_mus) {
ovmi.decryption_callback = mus_ogg_decryption_callback;
ovmi.meta_type = meta_OGG_MUS;
}
return init_vgmstream_ogg_vorbis_callbacks(streamFile, NULL, start_offset, &ovmi); return init_vgmstream_ogg_vorbis_callbacks(streamFile, NULL, start_offset, &ovmi);
@ -399,8 +429,8 @@ VGMSTREAM * init_vgmstream_ogg_vorbis_callbacks(STREAMFILE *streamFile, ov_callb
/* test if this is a proper Ogg Vorbis file, with the current (from init_x) STREAMFILE */ /* test if this is a proper Ogg Vorbis file, with the current (from init_x) STREAMFILE */
{ {
OggVorbis_File temp_ovf; OggVorbis_File temp_ovf = {0};
ogg_vorbis_streamfile temp_streamfile; ogg_vorbis_streamfile temp_streamfile = {0};
temp_streamfile.streamfile = streamFile; temp_streamfile.streamfile = streamFile;
@ -414,7 +444,6 @@ VGMSTREAM * init_vgmstream_ogg_vorbis_callbacks(STREAMFILE *streamFile, ov_callb
temp_streamfile.xor_value = ovmi->xor_value; temp_streamfile.xor_value = ovmi->xor_value;
/* open the ogg vorbis file for testing */ /* open the ogg vorbis file for testing */
memset(&temp_ovf, 0, sizeof(temp_ovf));
if (ov_test_callbacks(&temp_streamfile, &temp_ovf, NULL, 0, *callbacks_p)) if (ov_test_callbacks(&temp_streamfile, &temp_ovf, NULL, 0, *callbacks_p))
goto fail; goto fail;

View File

@ -41,8 +41,7 @@ VGMSTREAM * init_vgmstream_ogl(STREAMFILE *streamFile) {
#ifdef VGM_USE_VORBIS #ifdef VGM_USE_VORBIS
{ {
vorbis_custom_config cfg; vorbis_custom_config cfg = {0};
memset(&cfg, 0, sizeof(vorbis_custom_config));
vgmstream->layout_type = layout_none; vgmstream->layout_type = layout_none;
vgmstream->coding_type = coding_VORBIS_custom; vgmstream->coding_type = coding_VORBIS_custom;

View File

@ -46,13 +46,12 @@ static VGMSTREAM * init_vgmstream_opus(STREAMFILE *streamFile, meta_t meta_type,
{ {
uint8_t buf[0x100]; uint8_t buf[0x100];
size_t bytes; size_t bytes;
ffmpeg_custom_config cfg; ffmpeg_custom_config cfg = {0};
ffmpeg_codec_data *ffmpeg_data; ffmpeg_codec_data *ffmpeg_data;
bytes = ffmpeg_make_opus_header(buf,0x100, vgmstream->channels, skip, vgmstream->sample_rate); bytes = ffmpeg_make_opus_header(buf,0x100, vgmstream->channels, skip, vgmstream->sample_rate);
if (bytes <= 0) goto fail; if (bytes <= 0) goto fail;
memset(&cfg, 0, sizeof(ffmpeg_custom_config));
cfg.type = FFMPEG_SWITCH_OPUS; cfg.type = FFMPEG_SWITCH_OPUS;
ffmpeg_data = init_ffmpeg_config(streamFile, buf,bytes, start_offset,data_size, &cfg); ffmpeg_data = init_ffmpeg_config(streamFile, buf,bytes, start_offset,data_size, &cfg);

View File

@ -5,17 +5,14 @@
VGMSTREAM * init_vgmstream_ps2_joe(STREAMFILE *streamFile) { VGMSTREAM * init_vgmstream_ps2_joe(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL; VGMSTREAM * vgmstream = NULL;
off_t start_offset; off_t start_offset;
int channel_count, loop_flag, sample_rate, num_samples;
size_t file_size, data_size, unknown1, unknown2, interleave; size_t file_size, data_size, unknown1, unknown2, interleave;
int loop_flag;
int channel_count;
/* checks */ /* checks */
if (!check_extensions(streamFile, "joe")) if (!check_extensions(streamFile, "joe"))
goto fail; goto fail;
loop_flag = 1;
channel_count = 2;
file_size = get_streamfile_size(streamFile); file_size = get_streamfile_size(streamFile);
data_size = read_32bitLE(0x04,streamFile); data_size = read_32bitLE(0x04,streamFile);
unknown1 = read_32bitLE(0x08,streamFile); unknown1 = read_32bitLE(0x08,streamFile);
@ -48,14 +45,20 @@ VGMSTREAM * init_vgmstream_ps2_joe(STREAMFILE *streamFile) {
start_offset = file_size - data_size; start_offset = file_size - data_size;
channel_count = 2;
sample_rate = read_32bitLE(0x00,streamFile);
num_samples = ps_bytes_to_samples(data_size, channel_count);
/* most songs simply repeat except a few jingles (PS-ADPCM flags are always set) */
loop_flag = (num_samples > 20*sample_rate); /* in seconds */
/* build the VGMSTREAM */ /* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag); vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail; if (!vgmstream) goto fail;
vgmstream->sample_rate = read_32bitLE(0x00,streamFile); vgmstream->sample_rate = sample_rate;
vgmstream->coding_type = coding_PSX; vgmstream->num_samples = num_samples;
vgmstream->num_samples = ps_bytes_to_samples(data_size, channel_count);
//todo improve, not working 100% with early .joe //todo improve, not working 100% with early .joe
{ {
@ -103,6 +106,7 @@ VGMSTREAM * init_vgmstream_ps2_joe(STREAMFILE *streamFile) {
} }
} }
vgmstream->coding_type = coding_PSX;
vgmstream->layout_type = layout_interleave; vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = interleave; vgmstream->interleave_block_size = interleave;
vgmstream->meta_type = meta_PS2_JOE; vgmstream->meta_type = meta_PS2_JOE;

View File

@ -1,80 +1,57 @@
#include "meta.h" #include "meta.h"
#include "../layout/layout.h" #include "../layout/layout.h"
#include "../util.h" #include "../coding/coding.h"
/* STR: The Bouncer (PS2) */ /* STR - The Bouncer (PS2) */
VGMSTREAM * init_vgmstream_ps2_strlr(STREAMFILE *streamFile) { VGMSTREAM * init_vgmstream_ps2_strlr(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL; VGMSTREAM * vgmstream = NULL;
char filename[PATH_LIMIT]; int channel_count, loop_flag;
int loop_flag = 0; off_t start_offset;
int channel_count;
int i;
off_t start_offset; /* checks */
/* .vs: real extension (from .nam container) , .str: partial header id */
/* check extension, case insensitive */ if (!check_extensions(streamFile, "vs,str"))
streamFile->get_name(streamFile,filename,sizeof(filename)); goto fail;
if (strcasecmp("str",filename_extension(filename))) goto fail;
#if 0
/* check header */ /* check header */
if (read_32bitBE(0x00,streamFile) != 0x5354524C) /* "STRL" */ if (!(read_32bitBE(0x000,streamFile) == 0x5354524C && /* "STRL" */
read_32bitBE(0x800,streamFile) == 0x53545252) && /* "STRR" */
read_32bitBE(0x00,streamFile) != 0x5354524D) /* "STRM" */
goto fail; goto fail;
if (read_32bitBE(0x00,streamFile) != 0x53545252) /* "STRR" */
goto fail;
#endif
/* don't hijack Sonic & Sega All Stars Racing X360 (xma) */
if (read_32bitBE(0x00,streamFile) == 0x52494646)
goto fail; /* "RIFF"*/
/* don't hijack Mad Dash Racing (Xbox) */
if (read_32bitLE(0x0c,streamFile) == 1
&& read_32bitLE(0x010,streamFile) == 0
&& read_32bitLE(0x400,streamFile) == 0
&& read_32bitLE(0x7f0,streamFile) == 0)
goto fail;
loop_flag = 0; loop_flag = 0;
channel_count = 2; channel_count = (read_32bitBE(0x00,streamFile) == 0x5354524D) ? 1 : 2; /* "STRM"=mono (voices) */
start_offset = 0x00;
/* build the VGMSTREAM */
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag); vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail; if (!vgmstream) goto fail;
/* fill in the vital statistics */ vgmstream->sample_rate = 44100;
start_offset = 0x0;
vgmstream->channels = channel_count;
vgmstream->sample_rate = 48000;
vgmstream->coding_type = coding_PSX; vgmstream->coding_type = coding_PSX;
vgmstream->layout_type = layout_blocked_ps2_strlr; vgmstream->layout_type = layout_blocked_ps2_strlr;
//vgmstream->interleave_block_size = read_32bitLE(0xC, streamFile);
vgmstream->meta_type = meta_PS2_STRLR; vgmstream->meta_type = meta_PS2_STRLR;
/* open the file for reading by each channel */ if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
goto fail;
/* calc num_samples */
{ {
for (i=0;i<channel_count;i++) vgmstream->next_block_offset = start_offset;
{ do {
vgmstream->ch[i].streamfile = streamFile->open(streamFile, filename, 0x8000); block_update_ps2_strlr(vgmstream->next_block_offset,vgmstream);
if (!vgmstream->ch[i].streamfile) goto fail; vgmstream->num_samples += ps_bytes_to_samples(vgmstream->current_block_size, 1);
} }
while (vgmstream->next_block_offset < get_streamfile_size(streamFile));
} }
/* Calc num_samples */
block_update_ps2_strlr(start_offset, vgmstream); block_update_ps2_strlr(start_offset, vgmstream);
vgmstream->num_samples=0;
do
{
vgmstream->num_samples += vgmstream->current_block_size * 14 / 16;
block_update_ps2_strlr(vgmstream->next_block_offset, vgmstream);
} while (vgmstream->next_block_offset < get_streamfile_size(streamFile));
block_update_ps2_strlr(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;
} }

View File

@ -1,5 +1,5 @@
#include "meta.h" #include "meta.h"
#include "../util.h" #include "../coding/coding.h"
/* VPK */ /* VPK */
@ -34,7 +34,7 @@ VGMSTREAM * init_vgmstream_ps2_vpk(STREAMFILE *streamFile) {
/* Check for Compression Scheme */ /* Check for Compression Scheme */
vgmstream->coding_type = coding_PSX; vgmstream->coding_type = coding_PSX;
vgmstream->num_samples = read_32bitLE(0x04,streamFile)/16*28; vgmstream->num_samples = ps_bytes_to_samples(read_32bitLE(0x04,streamFile),vgmstream->channels);
/* Get loop point values */ /* Get loop point values */
if(vgmstream->loop_flag) { if(vgmstream->loop_flag) {
@ -42,7 +42,7 @@ VGMSTREAM * init_vgmstream_ps2_vpk(STREAMFILE *streamFile) {
vgmstream->loop_end_sample = vgmstream->num_samples; vgmstream->loop_end_sample = vgmstream->num_samples;
} }
vgmstream->interleave_block_size = read_32bitLE(0x0C,streamFile)/2; vgmstream->interleave_block_size = read_32bitLE(0x0C,streamFile)/vgmstream->channels;
vgmstream->layout_type = layout_interleave; vgmstream->layout_type = layout_interleave;
vgmstream->meta_type = meta_PS2_VPK; vgmstream->meta_type = meta_PS2_VPK;

View File

@ -32,8 +32,7 @@ VGMSTREAM * init_vgmstream_sk_aud(STREAMFILE *streamFile) {
#ifdef VGM_USE_VORBIS #ifdef VGM_USE_VORBIS
{ {
vorbis_custom_config cfg; vorbis_custom_config cfg = {0};
memset(&cfg, 0, sizeof(vorbis_custom_config));
vgmstream->layout_type = layout_none; vgmstream->layout_type = layout_none;
vgmstream->coding_type = coding_VORBIS_custom; vgmstream->coding_type = coding_VORBIS_custom;

View File

@ -313,6 +313,7 @@ static int parse_sb_header(ubi_sb_header * sb, STREAMFILE *streamFile) {
/* ignore non-audio entry (other types seem to have config data) */ /* ignore non-audio entry (other types seem to have config data) */
if (read_32bit(offset + 0x04, streamFile) != 0x01) if (read_32bit(offset + 0x04, streamFile) != 0x01)
continue; continue;
//;VGM_LOG("SB at %lx\n", offset);
/* weird case when there is no internal substream ID and just seem to rotate every time type changes, joy */ /* weird case when there is no internal substream ID and just seem to rotate every time type changes, joy */
if (sb->has_rotating_ids) { /* assumes certain configs can't happen in this case */ if (sb->has_rotating_ids) { /* assumes certain configs can't happen in this case */
@ -779,6 +780,23 @@ static int config_sb_header_version(ubi_sb_header * sb, STREAMFILE *streamFile)
return 1; return 1;
} }
#if 0
/* Far cry: Instincts - Evolution (2006)(Xbox) */
if (sb->version == 0x00170000 && is_sb2) {
sb->section1_entry_size = 0x48;
sb->section2_entry_size = 0x6c;
sb->external_flag_offset = 0;
sb->num_samples_offset = 0x28;
sb->stream_id_offset = 0;
sb->sample_rate_offset = 0x3c;
sb->channels_offset = 0x44;
sb->stream_type_offset = 0x48;
sb->extra_name_offset = 0x58;
return 1;
}
#endif
/* Prince of Persia: Rival Swords (2007)(PSP) */ /* Prince of Persia: Rival Swords (2007)(PSP) */
if (sb->version == 0x00180005 && is_sb5) { if (sb->version == 0x00180005 && is_sb5) {

View File

@ -204,7 +204,7 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) {
switch(ww.codec) { switch(ww.codec) {
case PCM: /* common */ case PCM: /* common */
/* normally riff.c has priority but it's needed when .wem is used */ /* normally riff.c has priority but it's needed when .wem is used */
if (ww.fmt_size != 0x10 && ww.fmt_size != 0x18) goto fail; /* old, new */ if (ww.fmt_size != 0x10 && ww.fmt_size != 0x18 && ww.fmt_size != 0x28) goto fail; /* old, new/Limbo (PC) */
if (ww.bits_per_sample != 16) goto fail; if (ww.bits_per_sample != 16) goto fail;
vgmstream->coding_type = (ww.big_endian ? coding_PCM16BE : coding_PCM16LE); vgmstream->coding_type = (ww.big_endian ? coding_PCM16BE : coding_PCM16LE);

View File

@ -39,8 +39,7 @@ VGMSTREAM * init_vgmstream_x360_ast(STREAMFILE *streamFile) {
{ {
/* manually find sample offsets (XMA1 nonsense again) */ /* manually find sample offsets (XMA1 nonsense again) */
ms_sample_data msd; ms_sample_data msd = {0};
memset(&msd,0,sizeof(ms_sample_data));
msd.xma_version = 1; msd.xma_version = 1;
msd.channels = channel_count; msd.channels = channel_count;

View File

@ -68,8 +68,7 @@ VGMSTREAM * init_vgmstream_xma(STREAMFILE *streamFile) {
/* fix samples; for now only XMA1 is fixed, but XMA2 num_samples don't include skip samples and xmaencode.exe doesn't use it */ /* fix samples; for now only XMA1 is fixed, but XMA2 num_samples don't include skip samples and xmaencode.exe doesn't use it */
if (is_xma1) { if (is_xma1) {
ms_sample_data msd; ms_sample_data msd = {0};
memset(&msd,0,sizeof(ms_sample_data));
msd.xma_version = is_xma1 ? 1 : 2; msd.xma_version = is_xma1 ? 1 : 2;
msd.channels = channel_count; msd.channels = channel_count;

View File

@ -19,7 +19,7 @@ VGMSTREAM * init_vgmstream_xvag(STREAMFILE *streamFile) {
size_t chunk_size, stream_size; size_t chunk_size, stream_size;
/* check extension, case insensitive */ /* check extension, case insensitive */
if (!check_extensions(streamFile,"xvag")) if (!check_extensions(streamFile,"xvag,"))
goto fail; goto fail;
/* check header */ /* check header */

View File

@ -2,16 +2,28 @@
#include "util.h" #include "util.h"
#include "streamtypes.h" #include "streamtypes.h"
const char * filename_extension(const char * filename) { const char * filename_extension(const char * pathname) {
const char * ext; const char * filename;
const char * extension;
/* You know what would be nice? strrchrnul(). /* get basename + extension */
* Instead I have to do it myself. */ filename = pathname;
ext = strrchr(filename,'.'); #if 0
if (ext==NULL) ext=filename+strlen(filename); /* point to null, i.e. an empty string for the extension */ //must detect empty extensions in folders with . in the name; not too important and DIR_SEPARATOR could improved
else ext=ext+1; /* skip the dot */ filename = strrchr(pathname, DIR_SEPARATOR);
if (filename == NULL)
filename = pathname; /* pathname has no separators (single filename) */
else
filename++; /* skip the separator */
#endif
return ext; extension = strrchr(filename,'.');
if (extension==NULL)
extension = filename+strlen(filename); /* point to null, i.e. an empty string for the extension */
else
extension++; /* skip the dot */
return extension;
} }
/* unused */ /* unused */

View File

@ -510,7 +510,7 @@ typedef enum {
meta_PS2_MCG, /* Gunvari MCG Files (was name .GCM on disk) */ meta_PS2_MCG, /* Gunvari MCG Files (was name .GCM on disk) */
meta_ZSD, /* Dragon Booster ZSD */ meta_ZSD, /* Dragon Booster ZSD */
meta_RedSpark, /* "RedSpark" RSD (MadWorld) */ meta_RedSpark, /* "RedSpark" RSD (MadWorld) */
meta_PC_IVAUD, /* .ivaud GTA IV */ meta_IVAUD, /* .ivaud GTA IV */
meta_NDS_HWAS, /* Spider-Man 3, Tony Hawk's Downhill Jam, possibly more... */ meta_NDS_HWAS, /* Spider-Man 3, Tony Hawk's Downhill Jam, possibly more... */
meta_NGC_LPS, /* Rave Master (Groove Adventure Rave)(GC) */ meta_NGC_LPS, /* Rave Master (Groove Adventure Rave)(GC) */
meta_NAOMI_ADPCM, /* NAOMI/NAOMI2 ARcade games */ meta_NAOMI_ADPCM, /* NAOMI/NAOMI2 ARcade games */
@ -686,6 +686,7 @@ typedef enum {
meta_TA_AAC_VITA, /* tri-Ace AAC (Judas Code) */ meta_TA_AAC_VITA, /* tri-Ace AAC (Judas Code) */
meta_OGG_GWM, /* Ogg Vorbis with encryption [Metronomicon (PC)] */ meta_OGG_GWM, /* Ogg Vorbis with encryption [Metronomicon (PC)] */
meta_H4M, /* Hudson HVQM4 video [Resident Evil 0 (GC), Tales of Symphonia (GC)] */ meta_H4M, /* Hudson HVQM4 video [Resident Evil 0 (GC), Tales of Symphonia (GC)] */
meta_OGG_MUS, /* Ogg Vorbis with encryption [Redux - Dark Matters (PC)] */
#ifdef VGM_USE_FFMPEG #ifdef VGM_USE_FFMPEG
meta_FFmpeg, meta_FFmpeg,