Updated VGMStream to r1050-1391-g4ed16da3.
parent
ccf4b1506b
commit
a6efe31256
|
@ -1,70 +1,97 @@
|
|||
#include "coding.h"
|
||||
#include "../util.h"
|
||||
|
||||
const int SH = 4;
|
||||
const int SHC = 10;
|
||||
// todo this is based on Kazzuya's old code; different emus (PCSX, Mame, Mednafen, etc) do
|
||||
// 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};
|
||||
double K1[4] = { 0.0, 0.0, -0.8125,-0.859375};
|
||||
|
||||
static int IK0(int fid)
|
||||
{ return ((int)((-K0[fid]) * (1 << SHC))); }
|
||||
|
||||
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;
|
||||
}
|
||||
/* XA ADPCM gain values */
|
||||
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) { return ((int)((-K0[fid]) * (1 << 10))); } /* K0/1 floats to int, K*2^10 = K*1024 */
|
||||
static int IK1(int fid) { return ((int)((-K1[fid]) * (1 << 10))); }
|
||||
|
||||
/* 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) {
|
||||
|
||||
static int head_table[8] = {0,2,8,10};
|
||||
VGMSTREAMCHANNEL * stream = &(vgmstream->ch[channel]);
|
||||
int predict_nr, shift_factor, sample;
|
||||
int32_t hist1=stream->adpcm_history1_32;
|
||||
int32_t hist2=stream->adpcm_history2_32;
|
||||
int HeadTable[8]={0,2,8,10};
|
||||
|
||||
short scale;
|
||||
off_t sp_offset;
|
||||
int i;
|
||||
int32_t sample_count;
|
||||
|
||||
int framesin = first_sample / (56 / channelspacing);
|
||||
|
||||
int frames_in, sample_count = 0;
|
||||
int32_t coef1, coef2, coef_index, shift_factor;
|
||||
int32_t hist1 = stream->adpcm_history1_32;
|
||||
int32_t hist2 = stream->adpcm_history2_32;
|
||||
|
||||
/* external interleave (fixed size), mono/stereo */
|
||||
frames_in = first_sample / (28*2 / channelspacing);
|
||||
first_sample = first_sample % 28;
|
||||
|
||||
vgmstream->xa_get_high_nibble=!vgmstream->xa_get_high_nibble;
|
||||
/* 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;
|
||||
|
||||
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;
|
||||
|
||||
predict_nr = read_8bit(stream->offset+HeadTable[framesin]+vgmstream->xa_get_high_nibble,stream->streamfile) >> 4;
|
||||
shift_factor = read_8bit(stream->offset+HeadTable[framesin]+vgmstream->xa_get_high_nibble,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 */
|
||||
|
||||
for (i=first_sample,sample_count=0; i<first_sample+samples_to_do; i++,sample_count+=channelspacing) {
|
||||
short sample_byte = (short)read_8bit(stream->offset+16+framesin+(i*4),stream->streamfile);
|
||||
coef1 = IK0(coef_index);
|
||||
coef2 = IK1(coef_index);
|
||||
|
||||
scale = ((vgmstream->xa_get_high_nibble ?
|
||||
sample_byte >> 4 :
|
||||
sample_byte & 0x0f)<<12);
|
||||
|
||||
sample = (short)(scale & 0xf000) >> shift_factor;
|
||||
sample <<= SH;
|
||||
sample -= (IK0(predict_nr) * hist1 + (IK1(predict_nr) * hist2)) >> SHC;
|
||||
/* decode nibbles */
|
||||
for (i = first_sample; i < first_sample + samples_to_do; i++) {
|
||||
int32_t new_sample;
|
||||
uint8_t nibbles = (uint8_t)read_8bit(stream->offset+0x10+frames_in+(i*0x04),stream->streamfile);
|
||||
|
||||
hist2=hist1;
|
||||
hist1=sample;
|
||||
new_sample = 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);
|
||||
|
||||
sample = CLAMP(sample, -32768 << SH, 32767 << SH);
|
||||
outbuf[sample_count] = (short)(sample >> SH);
|
||||
hist2 = hist1;
|
||||
hist1 = new_sample; /* must go before clamp, somehow */
|
||||
new_sample = new_sample >> 4;
|
||||
new_sample = clamp16(new_sample);
|
||||
|
||||
outbuf[sample_count] = new_sample;
|
||||
sample_count += channelspacing;
|
||||
}
|
||||
|
||||
stream->adpcm_history1_32=hist1;
|
||||
stream->adpcm_history2_32=hist2;
|
||||
stream->adpcm_history1_32 = hist1;
|
||||
stream->adpcm_history2_32 = hist2;
|
||||
}
|
||||
|
||||
size_t xa_bytes_to_samples(size_t bytes, int channels, int is_blocked) {
|
||||
|
|
|
@ -1,13 +1,16 @@
|
|||
#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 */
|
||||
|
||||
|
||||
static const char* extension_list[] = {
|
||||
//"", /* vgmstream can plays extensionless files too, but plugins must accept them manually */
|
||||
|
||||
"04sw",
|
||||
"2dx9",
|
||||
"2pfs",
|
||||
|
@ -38,6 +41,7 @@ static const char* extension_list[] = {
|
|||
"al2",
|
||||
"amts", //fake extension/header id for .stm (to be removed)
|
||||
"ao", //txth/reserved [Cloudphobia (PC)]
|
||||
"apc", //txth/reserved [MegaRace 3 (PC)]
|
||||
"as4",
|
||||
"asd",
|
||||
"asf",
|
||||
|
@ -343,6 +347,7 @@ static const char* extension_list[] = {
|
|||
"stx",
|
||||
"svag",
|
||||
"svs",
|
||||
"svg", //txth/reserved [Hunter: The Reckoning - Wayward (PS2)]
|
||||
"swag",
|
||||
"swav",
|
||||
"swd",
|
||||
|
@ -367,6 +372,7 @@ static const char* extension_list[] = {
|
|||
"v0",
|
||||
//"v1", //dual channel with v0
|
||||
"vag",
|
||||
"vai", //txth/reserved [Ratatouille (GC)]
|
||||
"vas",
|
||||
"vawx",
|
||||
"vb",
|
||||
|
@ -435,7 +441,7 @@ static const char* extension_list[] = {
|
|||
"zsd",
|
||||
"zwdsp",
|
||||
|
||||
"vgmstream"
|
||||
"vgmstream" /* fake extension, catch-all for FFmpeg/txth/etc */
|
||||
|
||||
//, NULL //end mark
|
||||
};
|
||||
|
@ -829,7 +835,7 @@ static const meta_info meta_info_list[] = {
|
|||
{meta_PS2_MCG, "Gunvari MCG Header"},
|
||||
{meta_ZSD, "ZSD 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_WII_NDP, "Icon Games NDP 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_PS2_VGS, "Princess Soft VGS 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_VAWX, "feelplus VAWX header"},
|
||||
{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_DSP_SADF, "Procyon Studio SADF header"},
|
||||
{meta_H4M, "Hudson HVQM4 header"},
|
||||
{meta_OGG_MUS, "Ogg Vorbis (MUS header)"},
|
||||
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
{meta_FFmpeg, "FFmpeg supported file format"},
|
||||
|
|
|
@ -69,6 +69,16 @@ void block_update_ea_schl(off_t block_offset, VGMSTREAM * vgmstream) {
|
|||
}
|
||||
|
||||
|
||||
vgmstream->current_block_offset = block_offset;
|
||||
vgmstream->next_block_offset = block_offset + block_size;
|
||||
vgmstream->current_block_samples = block_samples;
|
||||
vgmstream->current_block_size = 0; /* uses current_block_samples instead */
|
||||
|
||||
/* no need to setup offsets (plus could read over filesize near EOF) */
|
||||
if (block_samples == 0)
|
||||
return;
|
||||
|
||||
|
||||
/* 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
|
||||
* (with no blocks) may also have them in the first offset, but also may not. To simplify we just read them here. */
|
||||
|
@ -179,9 +189,4 @@ void block_update_ea_schl(off_t block_offset, VGMSTREAM * vgmstream) {
|
|||
|
||||
break;
|
||||
}
|
||||
|
||||
vgmstream->current_block_offset = block_offset;
|
||||
vgmstream->next_block_offset = block_offset + block_size;
|
||||
vgmstream->current_block_samples = block_samples;
|
||||
vgmstream->current_block_size = 0; /* uses current_block_samples instead */
|
||||
}
|
||||
|
|
|
@ -1,25 +1,53 @@
|
|||
#include "layout.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) {
|
||||
STREAMFILE *streamFile = vgmstream->ch[0].streamfile;
|
||||
size_t header_size, block_samples;
|
||||
int i;
|
||||
off_t start_offset;
|
||||
off_t interleave_size;
|
||||
int32_t nextFrame=0;
|
||||
STREAMFILE *streamFile=vgmstream->ch[0].streamfile;
|
||||
off_t seek_info_offset;
|
||||
|
||||
/* base header */
|
||||
seek_info_offset = read_32bitLE(block_offset+0x00,streamFile); /*64b */
|
||||
/* 0x08(8): seek table offset */
|
||||
/* 0x10(8): seek table offset again? */
|
||||
|
||||
/* seek info (per channel) */
|
||||
/* 0x00: start entry */
|
||||
/* 0x04: number of entries */
|
||||
/* 0x08: unknown */
|
||||
/* 0x0c: data size */
|
||||
|
||||
/* seek table (per all entries) */
|
||||
/* 0x00: start? */
|
||||
/* 0x04: end? */
|
||||
|
||||
|
||||
/* find header 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;
|
||||
|
||||
nextFrame=(read_32bitLE(vgmstream->current_block_offset+0x28,streamFile)<<12)+0x800;
|
||||
|
||||
vgmstream->next_block_offset = vgmstream->current_block_offset + nextFrame;
|
||||
vgmstream->current_block_size=read_32bitLE(block_offset+0x24,streamFile)/2;
|
||||
|
||||
start_offset=vgmstream->current_block_offset + 0x800;
|
||||
interleave_size=(read_32bitLE(block_offset+0x28,streamFile)<<12)/2;
|
||||
|
||||
for(i=0;i<vgmstream->channels;i++) {
|
||||
vgmstream->ch[i].offset = start_offset + (i*interleave_size);
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,19 +1,17 @@
|
|||
#include "layout.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) {
|
||||
STREAMFILE* streamFile = vgmstream->ch[0].streamfile;
|
||||
int i;
|
||||
|
||||
vgmstream->current_block_offset = block_offset;
|
||||
vgmstream->current_block_size = read_32bitLE(
|
||||
vgmstream->current_block_offset+0x4,
|
||||
vgmstream->ch[0].streamfile)*2;
|
||||
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++) {
|
||||
vgmstream->ch[i].offset = vgmstream->current_block_offset+0x20+(0x800*i);
|
||||
vgmstream->current_block_size = read_32bitLE(block_offset+0x04,streamFile); /* can be smaller than 0x800 */
|
||||
vgmstream->next_block_offset = block_offset + 0x800*vgmstream->channels;
|
||||
/* 0x08: number of remaning blocks, 0x10: some id/size? (shared in all blocks) */
|
||||
|
||||
for (i = 0; i < vgmstream->channels; i++) {
|
||||
vgmstream->ch[i].offset = block_offset + 0x20 + 0x800*i;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -48,9 +48,8 @@ VGMSTREAM * init_vgmstream_ahx(STREAMFILE *streamFile) {
|
|||
|
||||
{
|
||||
#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.cri_type = type;
|
||||
|
||||
|
|
|
@ -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) */
|
||||
VGMSTREAM * init_vgmstream_awc(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
awc_header awc;
|
||||
awc_header awc = {0};
|
||||
|
||||
/* check extension */
|
||||
if (!check_extensions(streamFile,"awc"))
|
||||
|
@ -137,8 +137,7 @@ VGMSTREAM * init_vgmstream_awc(STREAMFILE *streamFile) {
|
|||
#endif
|
||||
#ifdef VGM_USE_MPEG
|
||||
case 0x07: { /* MPEG (PS3) */
|
||||
mpeg_custom_config cfg;
|
||||
memset(&cfg, 0, sizeof(mpeg_custom_config));
|
||||
mpeg_custom_config cfg = {0};
|
||||
|
||||
cfg.chunk_size = awc.block_chunk;
|
||||
cfg.big_endian = awc.big_endian;
|
||||
|
@ -183,8 +182,6 @@ static int parse_awc_header(STREAMFILE* streamFile, awc_header* awc) {
|
|||
off_t off;
|
||||
int target_subsong = streamFile->stream_index;
|
||||
|
||||
memset(awc,0,sizeof(awc_header));
|
||||
|
||||
|
||||
/* check header */
|
||||
if (read_32bitBE(0x00,streamFile) != 0x41444154 && /* "ADAT" (LE) */
|
||||
|
|
|
@ -36,8 +36,8 @@ VGMSTREAM * init_vgmstream_bik(STREAMFILE *streamFile) {
|
|||
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
{
|
||||
ffmpeg_custom_config cfg;
|
||||
memset(&cfg, 0, sizeof(ffmpeg_custom_config));
|
||||
ffmpeg_custom_config cfg = {0};
|
||||
|
||||
cfg.stream_index = stream_index;
|
||||
|
||||
vgmstream->codec_data = init_ffmpeg_config(streamFile, NULL,0, 0x0,get_streamfile_size(streamFile), &cfg);
|
||||
|
|
|
@ -108,22 +108,26 @@ fail:
|
|||
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 * vgmstream = NULL;
|
||||
off_t start_offset = 0, header_offset = 0, sps_offset, max_offset;
|
||||
|
||||
/* checks */
|
||||
/* assumed to be .sps (no extensions in the archives) */
|
||||
/* should be .sps once extracted (filenames are hashed) */
|
||||
if (!check_extensions(streamFile,"sps"))
|
||||
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;
|
||||
|
||||
/* 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. */
|
||||
sps_offset = read_32bitBE(0x08, streamFile); /* points to some kind of table, number of entries unknown */
|
||||
/* actual offsets are probably somewhere but for now just manually search. */
|
||||
sps_offset = read_32bitBE(0x08, streamFile); /* seek table, number of entries unknown */
|
||||
max_offset = sps_offset + 0x3000;
|
||||
if (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):
|
||||
* &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) {
|
||||
VGM_LOG("EA SNS/SPS: unknown version\n");
|
||||
goto fail;
|
||||
|
|
|
@ -39,7 +39,7 @@ static size_t eaac_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset,
|
|||
off_t intrablock_offset, intradata_offset;
|
||||
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;
|
||||
|
||||
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 */
|
||||
}
|
||||
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) {
|
||||
#if 0
|
||||
|
@ -92,13 +92,13 @@ static size_t eaac_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset,
|
|||
if (to_read > length)
|
||||
to_read = length;
|
||||
if (to_read == 0)
|
||||
return total_read; /* should never happen... */
|
||||
break; /* should never happen... */
|
||||
|
||||
/* finally read and move buffer/offsets */
|
||||
bytes_read = read_streamfile(dest, data->physical_offset + intrablock_offset, to_read, streamfile);
|
||||
total_read += bytes_read;
|
||||
if (bytes_read != to_read)
|
||||
return total_read; /* couldn't read fully */
|
||||
break; /* couldn't read fully */
|
||||
|
||||
dest += 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) {
|
||||
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;
|
||||
|
||||
if (data->version == 0 && block_flag != 0x00 && block_flag != 0x80)
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
#define EA_VERSION_V3 0x03 // ~PS2 era
|
||||
|
||||
/* 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_PSX 0x01
|
||||
#define EA_PLATFORM_N64 0x02
|
||||
|
@ -21,6 +21,7 @@
|
|||
#define EA_PLATFORM_XBOX 0x07
|
||||
#define EA_PLATFORM_X360 0x09 // also "Xenon"
|
||||
#define EA_PLATFORM_PSP 0x0A
|
||||
#define EA_PLATFORM_PS3 0x0E // very rare [Need for Speed: Carbon (PS3)]
|
||||
#define EA_PLATFORM_3DS 0x14
|
||||
|
||||
/* 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
|
||||
|
||||
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->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;
|
||||
break;
|
||||
}
|
||||
|
||||
case EA_CODEC2_ATRAC3PLUS: /* regular ATRAC3plus chunked in SCxx blocks, including RIFF header */
|
||||
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 0x14: /* emdedded user data (free size/value) */
|
||||
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 0x1C: /* initial envelope volume (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 0xA6: /* azimuth ch5 */
|
||||
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);
|
||||
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_GC_WII
|
||||
|| ea->platform == EA_PLATFORM_X360
|
||||
|| ea->platform == EA_PLATFORM_PS3
|
||||
|| ea->platform == EA_PLATFORM_GENERIC) {
|
||||
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_X360: 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_GENERIC: ea->version = EA_VERSION_V2; break;
|
||||
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_X360: 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;
|
||||
default:
|
||||
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_X360: ea->sample_rate = 44100; 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)
|
||||
default:
|
||||
VGM_LOG("EA SCHl: unknown default sample rate for platform 0x%02x\n", ea->platform);
|
||||
|
|
|
@ -26,7 +26,7 @@ VGMSTREAM * init_vgmstream_ea_schl_fixed(STREAMFILE *streamFile) {
|
|||
VGMSTREAM * vgmstream = NULL;
|
||||
off_t start_offset;
|
||||
size_t header_size;
|
||||
ea_header ea;
|
||||
ea_header ea = {0};
|
||||
|
||||
|
||||
/* check extension */
|
||||
|
@ -94,8 +94,6 @@ fail:
|
|||
static int parse_fixed_header(STREAMFILE* streamFile, ea_header* ea, off_t begin_offset) {
|
||||
off_t offset = begin_offset;
|
||||
|
||||
memset(ea,0,sizeof(ea_header));
|
||||
|
||||
if (read_32bitBE(offset+0x00, streamFile) != 0x5041546C && /* "PATl" */
|
||||
read_32bitBE(offset+0x38, streamFile) != 0x544D706C) /* "TMpl" */
|
||||
goto fail;
|
||||
|
|
|
@ -34,8 +34,10 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) {
|
|||
* type 0x00 has an extra field (always 0?) at 0x1c */
|
||||
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;
|
||||
}
|
||||
|
||||
if (TargetSubsong == 0) TargetSubsong = 1; /* default to 1 */
|
||||
if (TargetSubsong > TotalSubsongs || TotalSubsongs <= 0) goto fail;
|
||||
|
|
|
@ -240,6 +240,12 @@ static const hcakey_info hcakey_list[] = {
|
|||
// PriPara: All Idol Perfect Stage (Takara Tomy) [Switch]
|
||||
{217735759}, // 000000000CFA624F
|
||||
|
||||
// Space Invaders Extreme (Taito Corporation, Backbone Entertainment) [PC]
|
||||
{91380310056}, // 0000001546B0E028
|
||||
|
||||
// CR Another God Hades Advent (Universal Entertainment Corporation) [iOS/Android]
|
||||
{64813795}, // 0000000003DCFAE3
|
||||
|
||||
};
|
||||
|
||||
#endif/*_HCA_KEYS_H_*/
|
||||
|
|
|
@ -2,67 +2,195 @@
|
|||
#include "../layout/layout.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 * vgmstream = NULL;
|
||||
ivaud_header ivaud = {0};
|
||||
int loop_flag;
|
||||
|
||||
char filename[PATH_LIMIT];
|
||||
off_t start_offset;
|
||||
off_t block_table_offset;
|
||||
int loop_flag = 0;
|
||||
int channel_count;
|
||||
int i;
|
||||
|
||||
|
||||
/* 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)
|
||||
/* checks */
|
||||
/* (hashed filenames are likely extensionless and .ivaud is added by tools) */
|
||||
if (!check_extensions(streamFile, "ivaud,"))
|
||||
goto fail;
|
||||
|
||||
/* never looped and allways 2 channels */
|
||||
/* check header */
|
||||
if (!parse_ivaud_header(streamFile, &ivaud))
|
||||
goto fail;
|
||||
|
||||
|
||||
loop_flag = 0;
|
||||
channel_count = 2;
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channel_count,loop_flag);
|
||||
vgmstream = allocate_vgmstream(ivaud.channel_count,loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
/* fill in the vital statistics */
|
||||
block_table_offset = read_32bitLE(0,streamFile);
|
||||
vgmstream->channels = channel_count;
|
||||
vgmstream->sample_rate = read_32bitLE(block_table_offset + 0x04,streamFile);
|
||||
vgmstream->sample_rate = ivaud.sample_rate;
|
||||
vgmstream->num_samples = ivaud.num_samples;
|
||||
vgmstream->num_streams = ivaud.total_subsongs;
|
||||
vgmstream->stream_size = ivaud.stream_size;
|
||||
vgmstream->meta_type = meta_IVAUD;
|
||||
|
||||
switch(ivaud.codec) {
|
||||
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;
|
||||
|
||||
case 0x0400:
|
||||
vgmstream->coding_type = coding_IMA_int;
|
||||
vgmstream->layout_type = ivaud.is_music ? layout_blocked_ivaud : layout_none;
|
||||
vgmstream->full_block_size = ivaud.block_size;
|
||||
break;
|
||||
|
||||
vgmstream->layout_type = layout_blocked_ivaud;
|
||||
vgmstream->meta_type = meta_PC_IVAUD;
|
||||
|
||||
/* open the file for reading */
|
||||
{
|
||||
for (i=0;i<channel_count;i++) {
|
||||
vgmstream->ch[i].streamfile = streamFile->open(streamFile,filename,0x2000);
|
||||
if (!vgmstream->ch[i].streamfile) goto fail;
|
||||
}
|
||||
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);
|
||||
if (!vgmstream_open_stream(vgmstream,streamFile,ivaud.stream_offset))
|
||||
goto fail;
|
||||
|
||||
block_update_ivaud(start_offset,vgmstream);
|
||||
if (vgmstream->layout_type == layout_blocked_ivaud)
|
||||
block_update_ivaud(ivaud.stream_offset, vgmstream);
|
||||
|
||||
return vgmstream;
|
||||
|
||||
/* clean up anything we may have opened */
|
||||
fail:
|
||||
if (vgmstream) close_vgmstream(vgmstream);
|
||||
close_vgmstream(vgmstream);
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -1,66 +1,52 @@
|
|||
#include "meta.h"
|
||||
#include "../util.h"
|
||||
|
||||
/* bxaimc - 2009-03-05
|
||||
- RRDS - found in Ridge Racer DS */
|
||||
#include "../coding/coding.h"
|
||||
|
||||
/* RRDS - from (some) NST games [Ridge Racer (DS), Metroid Prime Hunters - First Hunt (DS)] */
|
||||
VGMSTREAM * init_vgmstream_nds_rrds(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
char filename[PATH_LIMIT];
|
||||
int channel_count;
|
||||
int loop_flag;
|
||||
off_t start_offset;
|
||||
int loop_flag, channel_count;
|
||||
|
||||
/* check extension, case insensitive (made-up extension) */
|
||||
streamFile->get_name(streamFile,filename,sizeof(filename));
|
||||
if (strcasecmp("rrds",filename_extension(filename))) goto fail;
|
||||
|
||||
/* check size */
|
||||
if ((read_32bitLE(0x0,streamFile)+0x18) != get_streamfile_size(streamFile))
|
||||
/* checks */
|
||||
/* .rrds: made-up extension (files come from a bigfile and don't have filenames/extension) */
|
||||
if (!check_extensions(streamFile, ",rrds"))
|
||||
goto fail;
|
||||
|
||||
/* check type details */
|
||||
loop_flag = (read_32bitLE(0x14,streamFile) != 0);
|
||||
if ((read_32bitLE(0x00,streamFile)+0x18) != get_streamfile_size(streamFile))
|
||||
goto fail;
|
||||
|
||||
loop_flag = (read_32bitLE(0x14,streamFile) != 0); //todo not correct for MPH: First Hunt?
|
||||
channel_count = 1;
|
||||
start_offset = 0x1c;
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channel_count,loop_flag);
|
||||
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);
|
||||
|
||||
vgmstream->sample_rate = read_32bitLE(0x08,streamFile);
|
||||
vgmstream->num_samples = ima_bytes_to_samples(read_32bitLE(0x00,streamFile)-start_offset,channel_count);
|
||||
if (loop_flag) {
|
||||
vgmstream->loop_start_sample = (read_32bitLE(0x14,streamFile)-start_offset) / channel_count * 2;
|
||||
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->coding_type = coding_IMA_int;
|
||||
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_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;
|
||||
|
||||
/* clean up anything we may have opened */
|
||||
fail:
|
||||
if (vgmstream) close_vgmstream(vgmstream);
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
|
|
|
@ -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 */
|
||||
VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) {
|
||||
ogg_vorbis_meta_info_t ovmi = {0};
|
||||
|
@ -239,6 +261,7 @@ VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) {
|
|||
int is_rpgmvo = 0;
|
||||
int is_eno = 0;
|
||||
int is_gwm = 0;
|
||||
int is_mus = 0;
|
||||
|
||||
|
||||
/* check extension */
|
||||
|
@ -262,6 +285,8 @@ VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) {
|
|||
is_eno = 1;
|
||||
} else if (check_extensions(streamFile,"gwm")) { /* .gwm: Adagio: Cloudburst (PC) */
|
||||
is_gwm = 1;
|
||||
} else if (check_extensions(streamFile,"mus")) { /* .mus: Redux - Dark Matters (PC) */
|
||||
is_mus = 1;
|
||||
} else {
|
||||
goto fail;
|
||||
}
|
||||
|
@ -355,7 +380,6 @@ VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) {
|
|||
start_offset = 0x01;
|
||||
}
|
||||
|
||||
|
||||
/* check GWM [Adagio: Cloudburst (PC)], encrypted */
|
||||
if (is_gwm) {
|
||||
ovmi.xor_value = 0x5D;
|
||||
|
@ -363,6 +387,12 @@ VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) {
|
|||
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);
|
||||
|
||||
|
@ -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 */
|
||||
{
|
||||
OggVorbis_File temp_ovf;
|
||||
ogg_vorbis_streamfile temp_streamfile;
|
||||
OggVorbis_File temp_ovf = {0};
|
||||
ogg_vorbis_streamfile temp_streamfile = {0};
|
||||
|
||||
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;
|
||||
|
||||
/* 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))
|
||||
goto fail;
|
||||
|
||||
|
|
|
@ -41,8 +41,7 @@ VGMSTREAM * init_vgmstream_ogl(STREAMFILE *streamFile) {
|
|||
|
||||
#ifdef VGM_USE_VORBIS
|
||||
{
|
||||
vorbis_custom_config cfg;
|
||||
memset(&cfg, 0, sizeof(vorbis_custom_config));
|
||||
vorbis_custom_config cfg = {0};
|
||||
|
||||
vgmstream->layout_type = layout_none;
|
||||
vgmstream->coding_type = coding_VORBIS_custom;
|
||||
|
|
|
@ -46,13 +46,12 @@ static VGMSTREAM * init_vgmstream_opus(STREAMFILE *streamFile, meta_t meta_type,
|
|||
{
|
||||
uint8_t buf[0x100];
|
||||
size_t bytes;
|
||||
ffmpeg_custom_config cfg;
|
||||
ffmpeg_custom_config cfg = {0};
|
||||
ffmpeg_codec_data *ffmpeg_data;
|
||||
|
||||
bytes = ffmpeg_make_opus_header(buf,0x100, vgmstream->channels, skip, vgmstream->sample_rate);
|
||||
if (bytes <= 0) goto fail;
|
||||
|
||||
memset(&cfg, 0, sizeof(ffmpeg_custom_config));
|
||||
cfg.type = FFMPEG_SWITCH_OPUS;
|
||||
|
||||
ffmpeg_data = init_ffmpeg_config(streamFile, buf,bytes, start_offset,data_size, &cfg);
|
||||
|
|
|
@ -5,17 +5,14 @@
|
|||
VGMSTREAM * init_vgmstream_ps2_joe(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
off_t start_offset;
|
||||
int channel_count, loop_flag, sample_rate, num_samples;
|
||||
size_t file_size, data_size, unknown1, unknown2, interleave;
|
||||
int loop_flag;
|
||||
int channel_count;
|
||||
|
||||
|
||||
/* checks */
|
||||
if (!check_extensions(streamFile, "joe"))
|
||||
goto fail;
|
||||
|
||||
loop_flag = 1;
|
||||
channel_count = 2;
|
||||
|
||||
file_size = get_streamfile_size(streamFile);
|
||||
data_size = read_32bitLE(0x04,streamFile);
|
||||
unknown1 = read_32bitLE(0x08,streamFile);
|
||||
|
@ -48,14 +45,20 @@ VGMSTREAM * init_vgmstream_ps2_joe(STREAMFILE *streamFile) {
|
|||
|
||||
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 */
|
||||
vgmstream = allocate_vgmstream(channel_count,loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->sample_rate = read_32bitLE(0x00,streamFile);
|
||||
vgmstream->coding_type = coding_PSX;
|
||||
vgmstream->num_samples = ps_bytes_to_samples(data_size, channel_count);
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
vgmstream->num_samples = num_samples;
|
||||
|
||||
//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->interleave_block_size = interleave;
|
||||
vgmstream->meta_type = meta_PS2_JOE;
|
||||
|
|
|
@ -1,80 +1,57 @@
|
|||
#include "meta.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 * vgmstream = NULL;
|
||||
char filename[PATH_LIMIT];
|
||||
int loop_flag = 0;
|
||||
int channel_count;
|
||||
int i;
|
||||
int channel_count, loop_flag;
|
||||
off_t start_offset;
|
||||
|
||||
/* check extension, case insensitive */
|
||||
streamFile->get_name(streamFile,filename,sizeof(filename));
|
||||
if (strcasecmp("str",filename_extension(filename))) goto fail;
|
||||
|
||||
#if 0
|
||||
/* checks */
|
||||
/* .vs: real extension (from .nam container) , .str: partial header id */
|
||||
if (!check_extensions(streamFile, "vs,str"))
|
||||
goto fail;
|
||||
|
||||
/* 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;
|
||||
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;
|
||||
channel_count = 2;
|
||||
channel_count = (read_32bitBE(0x00,streamFile) == 0x5354524D) ? 1 : 2; /* "STRM"=mono (voices) */
|
||||
start_offset = 0x00;
|
||||
|
||||
/* 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 = 48000;
|
||||
vgmstream->sample_rate = 44100;
|
||||
vgmstream->coding_type = coding_PSX;
|
||||
|
||||
vgmstream->layout_type = layout_blocked_ps2_strlr;
|
||||
//vgmstream->interleave_block_size = read_32bitLE(0xC, streamFile);
|
||||
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->ch[i].streamfile = streamFile->open(streamFile, filename, 0x8000);
|
||||
if (!vgmstream->ch[i].streamfile) goto fail;
|
||||
vgmstream->next_block_offset = start_offset;
|
||||
do {
|
||||
block_update_ps2_strlr(vgmstream->next_block_offset,vgmstream);
|
||||
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);
|
||||
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;
|
||||
|
||||
/* clean up anything we may have opened */
|
||||
fail:
|
||||
if (vgmstream) close_vgmstream(vgmstream);
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#include "meta.h"
|
||||
#include "../util.h"
|
||||
#include "../coding/coding.h"
|
||||
|
||||
/* VPK */
|
||||
|
||||
|
@ -34,7 +34,7 @@ VGMSTREAM * init_vgmstream_ps2_vpk(STREAMFILE *streamFile) {
|
|||
|
||||
/* Check for Compression Scheme */
|
||||
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 */
|
||||
if(vgmstream->loop_flag) {
|
||||
|
@ -42,7 +42,7 @@ VGMSTREAM * init_vgmstream_ps2_vpk(STREAMFILE *streamFile) {
|
|||
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->meta_type = meta_PS2_VPK;
|
||||
|
||||
|
|
|
@ -32,8 +32,7 @@ VGMSTREAM * init_vgmstream_sk_aud(STREAMFILE *streamFile) {
|
|||
|
||||
#ifdef VGM_USE_VORBIS
|
||||
{
|
||||
vorbis_custom_config cfg;
|
||||
memset(&cfg, 0, sizeof(vorbis_custom_config));
|
||||
vorbis_custom_config cfg = {0};
|
||||
|
||||
vgmstream->layout_type = layout_none;
|
||||
vgmstream->coding_type = coding_VORBIS_custom;
|
||||
|
|
|
@ -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) */
|
||||
if (read_32bit(offset + 0x04, streamFile) != 0x01)
|
||||
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 */
|
||||
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;
|
||||
}
|
||||
#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) */
|
||||
if (sb->version == 0x00180005 && is_sb5) {
|
||||
|
|
|
@ -204,7 +204,7 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) {
|
|||
switch(ww.codec) {
|
||||
case PCM: /* common */
|
||||
/* 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;
|
||||
|
||||
vgmstream->coding_type = (ww.big_endian ? coding_PCM16BE : coding_PCM16LE);
|
||||
|
|
|
@ -39,8 +39,7 @@ VGMSTREAM * init_vgmstream_x360_ast(STREAMFILE *streamFile) {
|
|||
|
||||
{
|
||||
/* manually find sample offsets (XMA1 nonsense again) */
|
||||
ms_sample_data msd;
|
||||
memset(&msd,0,sizeof(ms_sample_data));
|
||||
ms_sample_data msd = {0};
|
||||
|
||||
msd.xma_version = 1;
|
||||
msd.channels = channel_count;
|
||||
|
|
|
@ -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 */
|
||||
if (is_xma1) {
|
||||
ms_sample_data msd;
|
||||
memset(&msd,0,sizeof(ms_sample_data));
|
||||
ms_sample_data msd = {0};
|
||||
|
||||
msd.xma_version = is_xma1 ? 1 : 2;
|
||||
msd.channels = channel_count;
|
||||
|
|
|
@ -19,7 +19,7 @@ VGMSTREAM * init_vgmstream_xvag(STREAMFILE *streamFile) {
|
|||
size_t chunk_size, stream_size;
|
||||
|
||||
/* check extension, case insensitive */
|
||||
if (!check_extensions(streamFile,"xvag"))
|
||||
if (!check_extensions(streamFile,"xvag,"))
|
||||
goto fail;
|
||||
|
||||
/* check header */
|
||||
|
|
|
@ -2,16 +2,28 @@
|
|||
#include "util.h"
|
||||
#include "streamtypes.h"
|
||||
|
||||
const char * filename_extension(const char * filename) {
|
||||
const char * ext;
|
||||
const char * filename_extension(const char * pathname) {
|
||||
const char * filename;
|
||||
const char * extension;
|
||||
|
||||
/* You know what would be nice? strrchrnul().
|
||||
* Instead I have to do it myself. */
|
||||
ext = strrchr(filename,'.');
|
||||
if (ext==NULL) ext=filename+strlen(filename); /* point to null, i.e. an empty string for the extension */
|
||||
else ext=ext+1; /* skip the dot */
|
||||
/* get basename + extension */
|
||||
filename = pathname;
|
||||
#if 0
|
||||
//must detect empty extensions in folders with . in the name; not too important and DIR_SEPARATOR could improved
|
||||
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 */
|
||||
|
|
|
@ -510,7 +510,7 @@ typedef enum {
|
|||
meta_PS2_MCG, /* Gunvari MCG Files (was name .GCM on disk) */
|
||||
meta_ZSD, /* Dragon Booster ZSD */
|
||||
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_NGC_LPS, /* Rave Master (Groove Adventure Rave)(GC) */
|
||||
meta_NAOMI_ADPCM, /* NAOMI/NAOMI2 ARcade games */
|
||||
|
@ -686,6 +686,7 @@ typedef enum {
|
|||
meta_TA_AAC_VITA, /* tri-Ace AAC (Judas Code) */
|
||||
meta_OGG_GWM, /* Ogg Vorbis with encryption [Metronomicon (PC)] */
|
||||
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
|
||||
meta_FFmpeg,
|
||||
|
|
Loading…
Reference in New Issue