Updated VGMStream to r1050-2574-g246222dd

CQTexperiment
Christopher Snowhill 2019-10-20 17:24:44 -07:00
parent 163a75f566
commit 242c0b9476
51 changed files with 1482 additions and 1054 deletions

View File

@ -564,6 +564,7 @@
83F0AA6021E2028C004BBC04 /* vsv_streamfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 83F0AA5D21E2028B004BBC04 /* vsv_streamfile.h */; };
83F0AA6121E2028C004BBC04 /* vsv.c in Sources */ = {isa = PBXBuildFile; fileRef = 83F0AA5E21E2028C004BBC04 /* vsv.c */; };
83F5F8831908D0A400C8E65F /* fsb5.c in Sources */ = {isa = PBXBuildFile; fileRef = 83F5F8821908D0A400C8E65F /* fsb5.c */; };
83FBD506235D31F800D35BCD /* riff_ogg_streamfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 83FBD502235D31F700D35BCD /* riff_ogg_streamfile.h */; };
83FF0EBC1E93282100C58054 /* wwise.c in Sources */ = {isa = PBXBuildFile; fileRef = 83FF0EBB1E93282100C58054 /* wwise.c */; };
/* End PBXBuildFile section */
@ -1247,6 +1248,7 @@
83F0AA5E21E2028C004BBC04 /* vsv.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = vsv.c; sourceTree = "<group>"; };
83F412871E932F9A002E37D0 /* Vorbis.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Vorbis.xcodeproj; path = ../Vorbis/macosx/Vorbis.xcodeproj; sourceTree = "<group>"; };
83F5F8821908D0A400C8E65F /* fsb5.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = fsb5.c; sourceTree = "<group>"; };
83FBD502235D31F700D35BCD /* riff_ogg_streamfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = riff_ogg_streamfile.h; sourceTree = "<group>"; };
83FF0EBB1E93282100C58054 /* wwise.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = wwise.c; sourceTree = "<group>"; };
/* End PBXFileReference section */
@ -1804,6 +1806,7 @@
837CEAE723487F2B00E62A4A /* raw_wavm.c */,
836F6EE218BDC2190095E648 /* redspark.c */,
834FE0D9215C79EA000A5D3D /* rfrm.c */,
83FBD502235D31F700D35BCD /* riff_ogg_streamfile.h */,
836F6EE318BDC2190095E648 /* riff.c */,
836F6EE418BDC2190095E648 /* rkv.c */,
836F6EE518BDC2190095E648 /* rs03.c */,
@ -2013,6 +2016,7 @@
834FE103215C79ED000A5D3D /* ea_schl_streamfile.h in Headers */,
83C7282722BC8C1500678B4A /* plugins.h in Headers */,
83F0AA6021E2028C004BBC04 /* vsv_streamfile.h in Headers */,
83FBD506235D31F800D35BCD /* riff_ogg_streamfile.h in Headers */,
48C2650F1A5D420800A0A3D6 /* vorbisfile.h in Headers */,
836F705718BDC2190095E648 /* util.h in Headers */,
836F6F9A18BDC2190095E648 /* meta.h in Headers */,

View File

@ -128,6 +128,7 @@ void decode_msadpcm_stereo(VGMSTREAM * vgmstream, sample_t * outbuf, int32_t fir
void decode_msadpcm_mono(VGMSTREAM * vgmstream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel);
void decode_msadpcm_ck(VGMSTREAM * vgmstream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel);
long msadpcm_bytes_to_samples(long bytes, int block_size, int channels);
int msadpcm_check_coefs(STREAMFILE *sf, off_t offset);
/* yamaha_decoder */
void decode_aica(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, int is_stereo);
@ -212,11 +213,18 @@ int test_hca_key(hca_codec_data * data, unsigned long long keycode);
#ifdef VGM_USE_VORBIS
/* ogg_vorbis_decoder */
void decode_ogg_vorbis(ogg_vorbis_codec_data * data, sample_t * outbuf, int32_t samples_to_do, int channels);
ogg_vorbis_codec_data* init_ogg_vorbis(STREAMFILE *sf, off_t start, off_t size, ogg_vorbis_io *io);
void decode_ogg_vorbis(ogg_vorbis_codec_data *data, sample_t *outbuf, int32_t samples_to_do, int channels);
void reset_ogg_vorbis(VGMSTREAM *vgmstream);
void seek_ogg_vorbis(VGMSTREAM *vgmstream, int32_t num_sample);
void free_ogg_vorbis(ogg_vorbis_codec_data *data);
int ogg_vorbis_get_comment(ogg_vorbis_codec_data *data, const char **comment);
void ogg_vorbis_get_info(ogg_vorbis_codec_data *data, int *p_channels, int *p_sample_rate);
void ogg_vorbis_get_samples(ogg_vorbis_codec_data *data, int *p_samples);
void ogg_vorbis_set_disable_reordering(ogg_vorbis_codec_data *data, int set);
STREAMFILE* ogg_vorbis_get_streamfile(ogg_vorbis_codec_data *data);
/* vorbis_custom_decoder */
vorbis_custom_codec_data *init_vorbis_custom(STREAMFILE *streamfile, off_t start_offset, vorbis_custom_t type, vorbis_custom_config * config);
void decode_vorbis_custom(VGMSTREAM * vgmstream, sample_t * outbuf, int32_t samples_to_do, int channels);

View File

@ -1160,25 +1160,12 @@ int w_bits(vgm_bitstream * ob, int num_bits, uint32_t value) {
/* ******************************************** */
STREAMFILE* setup_subfile_streamfile(STREAMFILE *sf, off_t subfile_offset, size_t subfile_size, const char* extension) {
STREAMFILE *temp_sf = NULL, *new_sf = NULL;
STREAMFILE *new_sf = NULL;
new_sf = open_wrap_streamfile(sf);
if (!new_sf) goto fail;
temp_sf = new_sf;
new_sf = open_clamp_streamfile(temp_sf, subfile_offset, subfile_size);
if (!new_sf) goto fail;
temp_sf = new_sf;
new_sf = open_clamp_streamfile_f(new_sf, subfile_offset, subfile_size);
if (extension) {
new_sf = open_fakename_streamfile(temp_sf, NULL, extension);
if (!new_sf) goto fail;
temp_sf = new_sf;
new_sf = open_fakename_streamfile_f(new_sf, NULL, extension);
}
return temp_sf;
fail:
close_streamfile(temp_sf);
return NULL;
return new_sf;
}

View File

@ -1,3 +1,4 @@
#include <math.h>
#include "coding.h"
#ifdef VGM_USE_FFMPEG
@ -540,6 +541,38 @@ fail:
}
/* When casting float to int value is simply truncated:
* - 0.0000518798828125 * 32768.0f = 1.7f, (int)1.7 = 1, (int)-1.7 = -1
* (instead of 1.7 = 2, -1.7 = -2)
*
* Alts for more accurate rounding could be:
* - (int)floor(f32 * 32768.0) //not quite ok negatives
* - (int)floor(f32 * 32768.0f + 0.5f) //Xiph Vorbis style
* - (int)(f32 < 0 ? f32 - 0.5f : f + 0.5f)
* - (((int) (f1 + 32768.5)) - 32768)
* - etc
* but since +-1 isn't really audible we'll just cast as it's the fastest.
*
* Regular C float-to-int casting ("int i = (int)f") is somewhat slow due to IEEE
* float requirements, but C99 adds some faster-but-less-precise casting functions
* we try to use (returning "long", though). They work ok without "fast float math" compiler
* flags, but probably should be enabled anyway to ensure no extra IEEE checks are needed.
*/
static inline int float_to_int(float val) {
#if defined(_MSC_VER) && (_MSC_VER < 1900) /* VS2015 */
return (int)val;
#else
return lrintf(val);
#endif
}
static inline int double_to_int(double val) {
#if defined(_MSC_VER) && (_MSC_VER < 1900) /* VS2015 */
return (int)val;
#else
return lrint(val); /* returns long tho */
#endif
}
/* sample copy helpers, using different functions to minimize branches.
*
* in theory, small optimizations like *outbuf++ vs outbuf[i] or alt clamping
@ -556,16 +589,6 @@ fail:
* int s16 = (int)(f32 * 32768.0f);
* if ((unsigned)(s16 + 0x8000) & 0xFFFF0000)
* s16 = (s16 >> 31) ^ 0x7FFF;
*
* when casting float to int, value is simply truncated:
* - 0.0000518798828125 * 32768.0f = 1.7f, (int)1.7 = 1, (int)-1.7 = -1
* alts for more accurate rounding could be:
* - (int)floor(f32 * 32768.0) //not quite ok negatives
* - (int)floor(f32 * 32768.0f + 0.5f) //Xiph Vorbis style
* - (int)(f32 < 0 ? f32 - 0.5f : f + 0.5f)
* - (((int) (f1 + 32768.5)) - 32768)
* - etc
* but since +-1 isn't really audible we'll just cast as it's the fastest
*/
static void samples_silence_s16(sample_t* obuf, int ochs, int samples) {
@ -621,7 +644,7 @@ static void samples_flt_to_s16(sample_t* obuf, float* ibuf, int ichs, int sample
int s, total_samples = samples * ichs;
float scale = invert ? -32768.0f : 32768.0f;
for (s = 0; s < total_samples; s++) {
obuf[s] = clamp16(ibuf[skip*ichs + s] * scale);
obuf[s] = clamp16(float_to_int(ibuf[skip*ichs + s] * scale));
}
}
static void samples_fltp_to_s16(sample_t* obuf, float** ibuf, int ichs, int samples, int skip, int invert) {
@ -629,21 +652,21 @@ static void samples_fltp_to_s16(sample_t* obuf, float** ibuf, int ichs, int samp
float scale = invert ? -32768.0f : 32768.0f;
for (ch = 0; ch < ichs; ch++) {
for (s = 0; s < samples; s++) {
obuf[s*ichs + ch] = clamp16(ibuf[ch][skip + s] * scale);
obuf[s*ichs + ch] = clamp16(float_to_int(ibuf[ch][skip + s] * scale));
}
}
}
static void samples_dbl_to_s16(sample_t* obuf, double* ibuf, int ichs, int samples, int skip) {
int s, total_samples = samples * ichs;
for (s = 0; s < total_samples; s++) {
obuf[s] = clamp16(ibuf[skip*ichs + s] * 32768.0);
obuf[s] = clamp16(double_to_int(ibuf[skip*ichs + s] * 32768.0));
}
}
static void samples_dblp_to_s16(sample_t* obuf, double** inbuf, int ichs, int samples, int skip) {
int s, ch;
for (ch = 0; ch < ichs; ch++) {
for (s = 0; s < samples; s++) {
obuf[s*ichs + ch] = clamp16(inbuf[ch][skip + s] * 32768.0);
obuf[s*ichs + ch] = clamp16(double_to_int(inbuf[ch][skip + s] * 32768.0));
}
}
}

View File

@ -17,12 +17,15 @@
* https://github.com/hcs64/ww2ogg
*/
static size_t make_oggs_first(uint8_t * buf, int buf_size, opus_config *cfg);
static size_t make_oggs_page(uint8_t * buf, int buf_size, size_t data_size, int page_sequence, int granule);
static size_t opus_get_packet_samples(const uint8_t * buf, int len);
static size_t get_xopus_packet_size(int packet, STREAMFILE * streamfile);
typedef enum { OPUS_SWITCH, OPUS_UE4_v1, OPUS_UE4_v2, OPUS_EA, OPUS_X } opus_type_t;
static size_t make_oggs_first(uint8_t *buf, int buf_size, opus_config *cfg);
static size_t make_oggs_page(uint8_t *buf, int buf_size, size_t data_size, int page_sequence, int granule);
static size_t opus_get_packet_samples(const uint8_t *buf, int len);
static size_t opus_get_packet_samples_sf(STREAMFILE *sf, off_t offset);
static size_t get_xopus_packet_size(int packet, STREAMFILE *streamfile);
static opus_type_t get_ue4opus_version(STREAMFILE *sf, off_t offset);
typedef enum { OPUS_SWITCH, OPUS_UE4, OPUS_EA, OPUS_X } opus_type_t;
typedef struct {
/* config */
opus_type_t type;
@ -94,19 +97,24 @@ static size_t opus_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset,
/* process new block */
if (data->page_size == 0) {
size_t data_size, skip_size, oggs_size;
size_t data_size, skip_size, oggs_size, packet_samples = 0;
switch(data->type) {
case OPUS_SWITCH: /* format seem to come from opus_test and not Nintendo-specific */
data_size = read_32bitBE(data->physical_offset, streamfile);
data_size = read_u32be(data->physical_offset, streamfile);
skip_size = 0x08; /* size + Opus state(?) */
break;
case OPUS_UE4:
data_size = (uint16_t)read_16bitLE(data->physical_offset, streamfile);
case OPUS_UE4_v1:
data_size = read_u16le(data->physical_offset, streamfile);
skip_size = 0x02;
break;
case OPUS_UE4_v2:
data_size = read_u16le(data->physical_offset + 0x00, streamfile);
packet_samples = read_u16le(data->physical_offset + 0x02, streamfile);
skip_size = 0x02 + 0x02;
break;
case OPUS_EA:
data_size = (uint16_t)read_16bitBE(data->physical_offset, streamfile);
data_size = read_u16be(data->physical_offset, streamfile);
skip_size = 0x02;
break;
case OPUS_X:
@ -130,8 +138,10 @@ static size_t opus_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset,
/* create fake OggS page (full page for checksums) */
read_streamfile(data->page_buffer+oggs_size, data->physical_offset + skip_size, data_size, streamfile); /* store page data */
data->samples_done += opus_get_packet_samples(data->page_buffer+oggs_size, data_size);
make_oggs_page(data->page_buffer,sizeof(data->page_buffer), data_size, data->sequence, data->samples_done);
if (packet_samples == 0)
packet_samples = opus_get_packet_samples(data->page_buffer + oggs_size, data_size);
data->samples_done += packet_samples;
make_oggs_page(data->page_buffer, sizeof(data->page_buffer), data_size, data->sequence, data->samples_done);
data->sequence++;
}
@ -191,15 +201,19 @@ static size_t opus_io_size(STREAMFILE *streamfile, opus_io_data* data) {
switch(data->type) {
case OPUS_SWITCH:
data_size = read_32bitBE(physical_offset, streamfile);
data_size = read_u32be(physical_offset, streamfile);
skip_size = 0x08;
break;
case OPUS_UE4:
data_size = (uint16_t)read_16bitLE(physical_offset, streamfile);
case OPUS_UE4_v1:
data_size = read_u16le(physical_offset, streamfile);
skip_size = 0x02;
break;
case OPUS_UE4_v2:
data_size = read_u16le(physical_offset, streamfile);
skip_size = 0x02 + 0x02;
break;
case OPUS_EA:
data_size = (uint16_t)read_16bitBE(physical_offset, streamfile);
data_size = read_u16be(physical_offset, streamfile);
skip_size = 0x02;
break;
case OPUS_X:
@ -505,6 +519,11 @@ fail:
static size_t opus_get_packet_samples(const uint8_t * buf, int len) {
return opus_packet_get_nb_frames(buf, len) * opus_packet_get_samples_per_frame(buf, 48000);
}
static size_t opus_get_packet_samples_sf(STREAMFILE *sf, off_t offset) {
uint8_t buf[0x04]; /* at least 0x02 */
read_streamfile(buf, offset, sizeof(buf), sf);
return opus_get_packet_samples(buf, sizeof(buf));
}
/************************** */
@ -512,48 +531,53 @@ static size_t get_xopus_packet_size(int packet, STREAMFILE * streamfile) {
/* XOPUS has a packet size table at the beginning, get size from there.
* Maybe should copy the table during setup to avoid IO, but all XOPUS are
* quite small so it isn't very noticeable. */
return (uint16_t)read_16bitLE(0x20 + packet*0x02, streamfile);
return read_u16le(0x20 + packet*0x02, streamfile);
}
static size_t custom_opus_get_samples(off_t offset, size_t stream_size, STREAMFILE *streamFile, opus_type_t type) {
static size_t custom_opus_get_samples(off_t offset, size_t stream_size, STREAMFILE *sf, opus_type_t type) {
size_t num_samples = 0;
off_t end_offset = offset + stream_size;
int packet = 0;
if (end_offset > get_streamfile_size(streamFile)) {
if (end_offset > get_streamfile_size(sf)) {
VGM_LOG("OPUS: wrong end offset found\n");
end_offset = get_streamfile_size(streamFile);
end_offset = get_streamfile_size(sf);
}
/* count by reading all frames */
while (offset < end_offset) {
uint8_t buf[4];
size_t data_size, skip_size;
size_t data_size, skip_size, packet_samples = 0;
switch(type) {
case OPUS_SWITCH:
data_size = read_32bitBE(offset, streamFile);
data_size = read_u32be(offset, sf);
skip_size = 0x08;
break;
case OPUS_UE4:
data_size = (uint16_t)read_16bitLE(offset, streamFile);
case OPUS_UE4_v1:
data_size = read_u16le(offset, sf);
skip_size = 0x02;
break;
case OPUS_UE4_v2:
data_size = read_u16le(offset + 0x00, sf);
packet_samples = read_u16le(offset + 0x02, sf);
skip_size = 0x02 + 0x02;
break;
case OPUS_EA:
data_size = (uint16_t)read_16bitBE(offset, streamFile);
data_size = read_u16be(offset, sf);
skip_size = 0x02;
break;
case OPUS_X:
data_size = get_xopus_packet_size(packet, streamFile);
data_size = get_xopus_packet_size(packet, sf);
skip_size = 0x00;
break;
default:
return 0;
}
read_streamfile(buf, offset+skip_size, 0x04, streamFile); /* at least 0x02 */
num_samples += opus_get_packet_samples(buf, 0x04);
if (packet_samples == 0)
packet_samples = opus_get_packet_samples_sf(sf, offset + skip_size);
num_samples += packet_samples;
offset += skip_size + data_size;
packet++;
@ -567,17 +591,20 @@ size_t switch_opus_get_samples(off_t offset, size_t stream_size, STREAMFILE *str
}
static size_t custom_opus_get_encoder_delay(off_t offset, STREAMFILE *streamFile, opus_type_t type) {
uint8_t buf[4];
size_t skip_size;
static size_t custom_opus_get_encoder_delay(off_t offset, STREAMFILE *sf, opus_type_t type) {
size_t skip_size, packet_samples = 0;
switch(type) {
case OPUS_SWITCH:
skip_size = 0x08;
break;
case OPUS_UE4:
case OPUS_UE4_v1:
skip_size = 0x02;
break;
case OPUS_UE4_v2:
packet_samples = read_u16le(offset + 0x02, sf);
skip_size = 0x02 + 0x02;
break;
case OPUS_EA:
skip_size = 0x02;
break;
@ -588,15 +615,16 @@ static size_t custom_opus_get_encoder_delay(off_t offset, STREAMFILE *streamFile
return 0;
}
if (packet_samples == 0)
packet_samples = opus_get_packet_samples_sf(sf, offset + skip_size);
/* encoder delay seems fixed to 1/8 of samples per frame, but may need more testing */
read_streamfile(buf, offset+skip_size, 0x04, streamFile); /* at least 0x02 */
return opus_get_packet_samples(buf, 0x04) / 8;
return packet_samples / 8;
}
size_t switch_opus_get_encoder_delay(off_t offset, STREAMFILE *streamFile) {
return custom_opus_get_encoder_delay(offset, streamFile, OPUS_SWITCH);
}
size_t ue4_opus_get_encoder_delay(off_t offset, STREAMFILE *streamFile) {
return custom_opus_get_encoder_delay(offset, streamFile, OPUS_UE4);
return custom_opus_get_encoder_delay(offset, streamFile, get_ue4opus_version(streamFile, offset));
}
size_t ea_opus_get_encoder_delay(off_t offset, STREAMFILE *streamFile) {
return custom_opus_get_encoder_delay(offset, streamFile, OPUS_EA);
@ -648,7 +676,7 @@ ffmpeg_codec_data * init_ffmpeg_switch_opus(STREAMFILE *streamFile, off_t start_
return init_ffmpeg_custom_opus(streamFile, start_offset, data_size, channels, skip, sample_rate, OPUS_SWITCH);
}
ffmpeg_codec_data * init_ffmpeg_ue4_opus(STREAMFILE *streamFile, off_t start_offset, size_t data_size, int channels, int skip, int sample_rate) {
return init_ffmpeg_custom_opus(streamFile, start_offset, data_size, channels, skip, sample_rate, OPUS_UE4);
return init_ffmpeg_custom_opus(streamFile, start_offset, data_size, channels, skip, sample_rate, get_ue4opus_version(streamFile, start_offset));
}
ffmpeg_codec_data * init_ffmpeg_ea_opus(STREAMFILE *streamFile, off_t start_offset, size_t data_size, int channels, int skip, int sample_rate) {
return init_ffmpeg_custom_opus(streamFile, start_offset, data_size, channels, skip, sample_rate, OPUS_EA);
@ -657,4 +685,16 @@ ffmpeg_codec_data * init_ffmpeg_x_opus(STREAMFILE *streamFile, off_t start_offse
return init_ffmpeg_custom_opus(streamFile, start_offset, data_size, channels, skip, sample_rate, OPUS_X);
}
static opus_type_t get_ue4opus_version(STREAMFILE *sf, off_t offset) {
int read_samples, calc_samples;
/* UE4OPUS v2 has packet samples right after packet size, check if data follows this */
read_samples = read_u16le(offset + 0x02, sf);
calc_samples = opus_get_packet_samples_sf(sf, offset + 0x04);
if (read_samples > 0 && read_samples == calc_samples)
return OPUS_UE4_v2;
else
return OPUS_UE4_v1;
}
#endif

View File

@ -2,14 +2,14 @@
#include "coding.h"
static const int msadpcm_steps[16] = {
static const int16_t msadpcm_steps[16] = {
230, 230, 230, 230,
307, 409, 512, 614,
768, 614, 512, 409,
307, 230, 230, 230
};
static const int msadpcm_coefs[7][2] = {
static const int16_t msadpcm_coefs[7][2] = {
{ 256, 0 },
{ 512, -256 },
{ 0, 0 },
@ -226,3 +226,29 @@ long msadpcm_bytes_to_samples(long bytes, int block_size, int channels) {
return (bytes / block_size) * (block_size - (7-1)*channels) * 2 / channels
+ ((bytes % block_size) ? ((bytes % block_size) - (7-1)*channels) * 2 / channels : 0);
}
/* test if MSADPCM coefs were re-defined (possible in theory but not used in practice) */
int msadpcm_check_coefs(STREAMFILE *sf, off_t offset) {
int i;
int count = read_16bitLE(offset, sf);
if (count != 7) {
VGM_LOG("MSADPCM: bad count %i at %lx\n", count, offset);
goto fail;
}
offset += 0x02;
for (i = 0; i < 7; i++) {
int16_t coef1 = read_16bitLE(offset + 0x00, sf);
int16_t coef2 = read_16bitLE(offset + 0x02, sf);
if (coef1 != msadpcm_coefs[i][0] || coef2 != msadpcm_coefs[i][1]) {
VGM_LOG("MSADPCM: bad coef %i/%i vs %i/%i\n", coef1, coef2, msadpcm_coefs[i][0], msadpcm_coefs[i][1]);
goto fail;
}
offset += 0x02 + 0x02;
}
return 1;
fail:
return 0;
}

View File

@ -5,21 +5,183 @@
#ifdef VGM_USE_VORBIS
#include <vorbis/vorbisfile.h>
#define OGG_DEFAULT_BITSTREAM 0
static void pcm_convert_float_to_16(int channels, sample_t * outbuf, int samples_to_do, float ** pcm, int disable_ordering);
/* opaque struct */
struct ogg_vorbis_codec_data {
OggVorbis_File ogg_vorbis_file;
int ovf_init;
int bitstream;
int disable_reordering; /* Xiph Ogg must reorder channels on output, but some pre-ordered games don't need it */
ogg_vorbis_io io;
vorbis_comment *comment;
int comment_number;
vorbis_info *info;
};
void decode_ogg_vorbis(ogg_vorbis_codec_data * data, sample_t * outbuf, int32_t samples_to_do, int channels) {
static void pcm_convert_float_to_16(int channels, sample_t *outbuf, int samples_to_do, float **pcm, int disable_ordering);
static size_t ov_read_func(void *ptr, size_t size, size_t nmemb, void *datasource);
static int ov_seek_func(void *datasource, ogg_int64_t offset, int whence);
static long ov_tell_func(void *datasource);
static int ov_close_func(void *datasource);
ogg_vorbis_codec_data* init_ogg_vorbis(STREAMFILE *sf, off_t start, off_t size, ogg_vorbis_io *io) {
ogg_vorbis_codec_data *data = NULL;
ov_callbacks callbacks = {0};
//todo clean up
callbacks.read_func = ov_read_func;
callbacks.seek_func = ov_seek_func;
callbacks.close_func = ov_close_func;
callbacks.tell_func = ov_tell_func;
/* test if this is a proper Ogg Vorbis file, with the current (from init_x) STREAMFILE
* (quick test without having to malloc first, though if one invoked this it'll probably success) */
{
OggVorbis_File temp_ovf = {0};
ogg_vorbis_io temp_io = {0};
temp_io.streamfile = sf;
temp_io.start = start;
temp_io.offset = 0;
temp_io.size = size;
if (io != NULL) {
temp_io.decryption_callback = io->decryption_callback;
temp_io.scd_xor = io->scd_xor;
temp_io.scd_xor_length = io->scd_xor_length;
temp_io.xor_value = io->xor_value;
}
/* open the ogg vorbis file for testing */
if (ov_test_callbacks(&temp_io, &temp_ovf, NULL, 0, callbacks))
goto fail;
/* we have to close this as it has the init_vgmstream meta-reading STREAMFILE */
ov_clear(&temp_ovf);
}
/* proceed to init codec_data and reopen a STREAMFILE for this codec */
{
data = calloc(1,sizeof(ogg_vorbis_codec_data));
if (!data) goto fail;
data->io.streamfile = reopen_streamfile(sf, 0);
if (!data->io.streamfile) goto fail;
data->io.start = start;
data->io.offset = 0;
data->io.size = size;
if (io != NULL) {
data->io.decryption_callback = io->decryption_callback;
data->io.scd_xor = io->scd_xor;
data->io.scd_xor_length = io->scd_xor_length;
data->io.xor_value = io->xor_value;
}
/* open the ogg vorbis file for real */
if (ov_open_callbacks(&data->io, &data->ogg_vorbis_file, NULL, 0, callbacks))
goto fail;
data->ovf_init = 1;
}
//todo could set bitstreams as subsongs?
/* get info from bitstream */
data->bitstream = OGG_DEFAULT_BITSTREAM;
data->comment = ov_comment(&data->ogg_vorbis_file, data->bitstream);
data->info = ov_info(&data->ogg_vorbis_file, data->bitstream);
return data;
fail:
free_ogg_vorbis(data);
return NULL;
}
static size_t ov_read_func(void *ptr, size_t size, size_t nmemb, void *datasource) {
ogg_vorbis_io *io = datasource;
size_t bytes_read, items_read;
off_t real_offset = io->start + io->offset;
size_t max_bytes = size * nmemb;
/* clamp for virtual filesize */
if (max_bytes > io->size - io->offset)
max_bytes = io->size - io->offset;
bytes_read = read_streamfile(ptr, real_offset, max_bytes, io->streamfile);
items_read = bytes_read / size;
/* may be encrypted */
if (io->decryption_callback) {
io->decryption_callback(ptr, size, items_read, io);
}
io->offset += items_read * size;
return items_read;
}
static int ov_seek_func(void *datasource, ogg_int64_t offset, int whence) {
ogg_vorbis_io *io = datasource;
ogg_int64_t base_offset, new_offset;
switch (whence) {
case SEEK_SET:
base_offset = 0;
break;
case SEEK_CUR:
base_offset = io->offset;
break;
case SEEK_END:
base_offset = io->size;
break;
default:
return -1;
break;
}
new_offset = base_offset + offset;
if (new_offset < 0 || new_offset > io->size) {
return -1; /* *must* return -1 if stream is unseekable */
} else {
io->offset = new_offset;
return 0;
}
}
static long ov_tell_func(void *datasource) {
ogg_vorbis_io *io = datasource;
return io->offset;
}
static int ov_close_func(void *datasource) {
/* needed as setting ov_close_func in ov_callbacks to NULL doesn't seem to work
* (closing the streamfile is done in free_ogg_vorbis) */
return 0;
}
/* ********************************************** */
void decode_ogg_vorbis(ogg_vorbis_codec_data *data, sample_t *outbuf, int32_t samples_to_do, int channels) {
int samples_done = 0;
long rc;
float **pcm_channels; /* pointer to Xiph's double array buffer */
while (samples_done < samples_to_do) {
rc = ov_read_float(
&data->ogg_vorbis_file, /* context */
&pcm_channels, /* buffer pointer */
(samples_to_do - samples_done), /* samples to produce */
&data->bitstream); /* bitstream*/
&data->ogg_vorbis_file, /* context */
&pcm_channels, /* buffer pointer */
(samples_to_do - samples_done), /* samples to produce */
&data->bitstream); /* bitstream */
if (rc <= 0) goto fail; /* rc is samples done */
pcm_convert_float_to_16(channels, outbuf, rc, pcm_channels, data->disable_reordering);
@ -32,13 +194,13 @@ void decode_ogg_vorbis(ogg_vorbis_codec_data * data, sample_t * outbuf, int32_t
/* we use ov_read_float as to reuse the xiph's buffer for easier remapping,
* but seems ov_read is slightly faster due to optimized (asm) float-to-int. */
rc = ov_read(
&data->ogg_vorbis_file, /* context */
(char *)(outbuf), /* buffer */
&data->ogg_vorbis_file, /* context */
(char *)(outbuf), /* buffer */
(samples_to_do - samples_done) * sizeof(sample_t) * channels, /* length in bytes */
0, /* pcm endianness */
sizeof(sample), /* pcm size */
1, /* pcm signedness */
&data->bitstream); /* bitstream */
0, /* pcm endianness */
sizeof(sample), /* pcm size */
1, /* pcm signedness */
&data->bitstream); /* bitstream */
if (rc <= 0) goto fail; /* rc is bytes done (for all channels) */
swap_samples_le(outbuf, rc / sizeof(sample_t)); /* endianness is a bit weird with ov_read though */
@ -56,7 +218,7 @@ fail:
/* vorbis encodes channels in non-standard order, so we remap during conversion to fix this oddity.
* (feels a bit weird as one would think you could leave as-is and set the player's output
* order, but that isn't possible and remapping is what FFmpeg and every other plugin do). */
* order, but that isn't possible and remapping is what FFmpeg and every other plugin does). */
static const int xiph_channel_map[8][8] = {
{ 0 }, /* 1ch: FC > same */
{ 0, 1 }, /* 2ch: FL FR > same */
@ -115,10 +277,61 @@ void seek_ogg_vorbis(VGMSTREAM *vgmstream, int32_t num_sample) {
void free_ogg_vorbis(ogg_vorbis_codec_data *data) {
if (!data) return;
ov_clear(&data->ogg_vorbis_file);
if (data->ovf_init)
ov_clear(&data->ogg_vorbis_file);
close_streamfile(data->ov_streamfile.streamfile);
close_streamfile(data->io.streamfile);
free(data);
}
/* ********************************************** */
int ogg_vorbis_get_comment(ogg_vorbis_codec_data *data, const char **comment) {
if (!data) return 0;
/* dumb reset */
if (comment == NULL) {
data->comment_number = 0;
return 1;
}
if (data->comment_number >= data->comment->comments)
return 0;
*comment = data->comment->user_comments[data->comment_number];
data->comment_number++;
return 1;
}
void ogg_vorbis_get_info(ogg_vorbis_codec_data *data, int *p_channels, int *p_sample_rate) {
if (!data) {
if (p_channels) *p_channels = 0;
if (p_sample_rate) *p_sample_rate = 0;
return;
}
if (p_channels) *p_channels = data->info->channels;
if (p_sample_rate) *p_sample_rate = (int)data->info->rate;
}
void ogg_vorbis_get_samples(ogg_vorbis_codec_data *data, int *p_samples) {
if (!data) {
if (p_samples) *p_samples = 0;
return;
}
if (p_samples) *p_samples = ov_pcm_total(&data->ogg_vorbis_file,-1);
}
void ogg_vorbis_set_disable_reordering(ogg_vorbis_codec_data *data, int set) {
if (!data) return;
data->disable_reordering = set;
}
STREAMFILE* ogg_vorbis_get_streamfile(ogg_vorbis_codec_data *data) {
if (!data) return NULL;
return data->io.streamfile;
}
#endif

View File

@ -5,31 +5,36 @@
/* parse a CD-XA raw mode2/form2 sector */
void block_update_xa(off_t block_offset, VGMSTREAM * vgmstream) {
STREAMFILE* streamFile = vgmstream->ch[0].streamfile;
int i;
int i, is_audio;
size_t block_samples;
uint8_t xa_submode;
uint8_t xa_target, xa_submode, config_target;
/* XA mode2/form2 sector, size 0x930
* 0x00: sync word
* 0x0c: header = minute, second, sector, mode (always 0x02)
* 0x10: subheader = file, channel (substream marker), submode flags, xa header
* 0x10: subheader = file, channel, submode flags, xa header
* 0x14: subheader again (for error correction)
* 0x18: data
* 0x918: unused
* 0x92c: EDC/checksum or null
* 0x930: end
* Sectors with no data may exist near other with data
*/
xa_target = (uint8_t)read_16bitBE(block_offset + 0x10, streamFile);
config_target = vgmstream->codec_config;
/* Sector subheader's file+channel markers are used to interleave streams (music/sfx/voices)
* by reading one target file+channel while ignoring the rest. This is needed to adjust
* CD drive spinning <> decoding speed (data is read faster otherwise, so can't have 2
* sectors of the same channel), Normally N channels = N streams (usually 8/16/32 depending
* on sample rate/stereo), though channels can be empty or contain video (like 7 or 15 video
* sectors + 1 audio frame). Normally interleaved channels use with the same file ID, but some
* games change ID too. Extractors deinterleave and split .xa using file + channel + EOF flags. */
/* channel markers supposedly could be used to interleave streams, ex. audio languages within video
* (extractors may split .XA using channels?) */
VGM_ASSERT(block_offset + 0x930 < get_streamfile_size(streamFile) &&
(uint8_t)read_8bit(block_offset + 0x000 + 0x11,streamFile) !=
(uint8_t)read_8bit(block_offset + 0x930 + 0x11,streamFile),
"XA block: subchannel change at %x\n", (uint32_t)block_offset);
/* submode flag bits (typical audio value = 0x64 01100100)
* - 7 (0x80 10000000): end of file
* - 7 (0x80 10000000): end of file (usually at data end, not per subheader's file)
* - 6 (0x40 01000000): real time mode
* - 5 (0x20 00100000): sector form (0=form1, 1=form2)
* - 4 (0x10 00010000): trigger (for application)
@ -37,11 +42,19 @@ void block_update_xa(off_t block_offset, VGMSTREAM * vgmstream) {
* - 2 (0x04 00000100): audio sector
* - 1 (0x02 00000010): video sector
* - 0 (0x01 00000001): end of audio
* Empty sectors with no flags may exist interleaved with other with audio/data.
*/
xa_submode = (uint8_t)read_8bit(block_offset + 0x12,streamFile);
/* audio sector must set/not set certain flags, as per spec */
if (!(xa_submode & 0x08) && (xa_submode & 0x04) && !(xa_submode & 0x02)) {
is_audio = !(xa_submode & 0x08) && (xa_submode & 0x04) && !(xa_submode & 0x02);
if (xa_target != config_target) {
//;VGM_LOG("XA block: ignored block at %x\n", (uint32_t)block_offset);
block_samples = 0; /* not a target sector */
}
else if (is_audio) {
if (xa_submode & 0x20) {
/* form2 audio: size 0x900, 18 frames of size 0x80 with 8 subframes of 28 samples */
block_samples = (28*8 / vgmstream->channels) * 18;

View File

@ -1,69 +1,51 @@
#include "meta.h"
#include "../util.h"
#include "../coding/coding.h"
/* 2DX9 (found in beatmaniaIIDX16 - EMPRESS (Arcade) */
/* 2DX9 - from Konami arcade games [beatmaniaIIDX16: EMPRESS (AC), BeatStream (AC), REFLEC BEAT (AC)] */
VGMSTREAM * init_vgmstream_2dx9(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
char filename[PATH_LIMIT];
off_t start_offset;
int channel_count, loop_flag;
int loop_flag;
int channel_count;
/* check extension, case insensitive */
streamFile->get_name(streamFile,filename,sizeof(filename));
if (strcasecmp("2dx9",filename_extension(filename))) goto fail;
/* checks */
if (!check_extensions(streamFile, "2dx9"))
goto fail;
/* check header */
if (read_32bitBE(0x0,streamFile) != 0x32445839) /* 2DX9 */
goto fail;
if (read_32bitBE(0x18,streamFile) != 0x52494646) /* RIFF */
goto fail;
if (read_32bitBE(0x20,streamFile) != 0x57415645) /* WAVE */
goto fail;
if (read_32bitBE(0x24,streamFile) != 0x666D7420) /* fmt */
goto fail;
if (read_32bitBE(0x00,streamFile) != 0x32445839) /* 2DX9 */
goto fail;
if (read_32bitBE(0x18,streamFile) != 0x52494646) /* RIFF */
goto fail;
if (read_32bitBE(0x20,streamFile) != 0x57415645) /* WAVE */
goto fail;
if (read_32bitBE(0x24,streamFile) != 0x666D7420) /* fmt */
goto fail;
if (read_32bitBE(0x6a,streamFile) != 0x64617461) /* data */
goto fail;
goto fail;
loop_flag = 0;
channel_count = read_16bitLE(0x2e,streamFile);
start_offset = 0x72;
/* build the VGMSTREAM */
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail;
/* fill in the vital statistics */
start_offset = 0x72;
vgmstream->channels = channel_count;
vgmstream->sample_rate = read_32bitLE(0x30,streamFile);
vgmstream->coding_type = coding_MSADPCM;
vgmstream->num_samples = read_32bitLE(0x66,streamFile);
vgmstream->layout_type = layout_none;
vgmstream->interleave_block_size = read_16bitLE(0x38,streamFile);
vgmstream->meta_type = meta_2DX9;
vgmstream->sample_rate = read_32bitLE(0x30,streamFile);
vgmstream->num_samples = read_32bitLE(0x66,streamFile);
vgmstream->coding_type = coding_MSADPCM;
vgmstream->layout_type = layout_none;
vgmstream->frame_size = read_16bitLE(0x38,streamFile);
if (!msadpcm_check_coefs(streamFile, 0x40))
goto fail;
/* open the file for reading */
{
int i;
STREAMFILE * file;
file = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE);
if (!file) goto fail;
for (i=0;i<channel_count;i++) {
vgmstream->ch[i].streamfile = file;
vgmstream->ch[i].channel_start_offset=
vgmstream->ch[i].offset=start_offset+
vgmstream->interleave_block_size*i;
}
}
return vgmstream;
if (!vgmstream_open_stream(vgmstream, streamFile, start_offset))
goto fail;
return vgmstream;
fail:
/* clean up anything we may have opened */
if (vgmstream) close_vgmstream(vgmstream);
close_vgmstream(vgmstream);
return NULL;
}

View File

@ -70,24 +70,15 @@ fail:
/* ************************************** */
//todo maybe use reopen sf? since internal buffer is going to be read
#define ACB_TABLE_BUFFER_SIZE 0x4000
STREAMFILE* setup_acb_streamfile(STREAMFILE *streamFile, size_t buffer_size) {
STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL;
STREAMFILE* setup_acb_streamfile(STREAMFILE *sf, size_t buffer_size) {
STREAMFILE *new_sf = NULL;
new_streamFile = open_wrap_streamfile(streamFile);
if (!new_streamFile) goto fail;
temp_streamFile = new_streamFile;
new_streamFile = open_buffer_streamfile(temp_streamFile, buffer_size);
if (!new_streamFile) goto fail;
temp_streamFile = new_streamFile;
return temp_streamFile;
fail:
close_streamfile(temp_streamFile);
return NULL;
new_sf = open_wrap_streamfile(sf);
new_sf = open_buffer_streamfile_f(new_sf, buffer_size);
return new_sf;
}

View File

@ -110,7 +110,7 @@ static VGMSTREAM *build_layered_vgmstream(STREAMFILE *streamFile, off_t segment_
VGMSTREAM *vgmstream = NULL;
layered_layout_data* data = NULL;
int i;
STREAMFILE* temp_streamFile = NULL;
STREAMFILE* temp_sf = NULL;
/* build layers */
@ -119,17 +119,17 @@ static VGMSTREAM *build_layered_vgmstream(STREAMFILE *streamFile, off_t segment_
for (i = 0; i < layer_count; i++) {
/* build the layer STREAMFILE */
temp_streamFile = setup_aix_streamfile(streamFile, segment_offset, segment_size, i, "adx");
if (!temp_streamFile) goto fail;
temp_sf = setup_aix_streamfile(streamFile, segment_offset, segment_size, i, "adx");
if (!temp_sf) goto fail;
/* build the sub-VGMSTREAM */
data->layers[i] = init_vgmstream_adx(temp_streamFile);
data->layers[i] = init_vgmstream_adx(temp_sf);
if (!data->layers[i]) goto fail;
data->layers[i]->stream_size = get_streamfile_size(temp_streamFile);
data->layers[i]->stream_size = get_streamfile_size(temp_sf);
close_streamfile(temp_streamFile);
temp_streamFile = NULL;
close_streamfile(temp_sf);
temp_sf = NULL;
}
if (!setup_layout_layered(data))
@ -145,7 +145,7 @@ static VGMSTREAM *build_layered_vgmstream(STREAMFILE *streamFile, off_t segment_
fail:
if (!vgmstream) free_layout_layered(data);
close_vgmstream(vgmstream);
close_streamfile(temp_streamFile);
close_streamfile(temp_sf);
return NULL;
}

View File

@ -112,40 +112,22 @@ static size_t aix_io_size(STREAMFILE *streamfile, aix_io_data* data) {
}
/* Handles deinterleaving of AIX blocked layer streams */
static STREAMFILE* setup_aix_streamfile(STREAMFILE *streamFile, off_t stream_offset, size_t stream_size, int layer_number, const char* extension) {
STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL;
static STREAMFILE* setup_aix_streamfile(STREAMFILE *sf, off_t stream_offset, size_t stream_size, int layer_number, const char* extension) {
STREAMFILE *new_sf = NULL;
aix_io_data io_data = {0};
size_t io_data_size = sizeof(aix_io_data);
io_data.stream_offset = stream_offset;
io_data.stream_size = stream_size;
io_data.layer_number = layer_number;
io_data.logical_offset = -1; /* force reset */
/* setup subfile */
new_streamFile = open_wrap_streamfile(streamFile);
if (!new_streamFile) goto fail;
temp_streamFile = new_streamFile;
new_streamFile = open_io_streamfile(new_streamFile, &io_data,io_data_size, aix_io_read,aix_io_size);
if (!new_streamFile) goto fail;
temp_streamFile = new_streamFile;
new_streamFile = open_buffer_streamfile(new_streamFile,0);
if (!new_streamFile) goto fail;
temp_streamFile = new_streamFile;
new_sf = open_wrap_streamfile(sf);
new_sf = open_io_streamfile_f(new_sf, &io_data, sizeof(aix_io_data), aix_io_read, aix_io_size);
new_sf = open_buffer_streamfile_f(new_sf, 0);
if (extension) {
new_streamFile = open_fakename_streamfile(temp_streamFile, NULL,extension);
if (!new_streamFile) goto fail;
temp_streamFile = new_streamFile;
new_sf = open_fakename_streamfile_f(new_sf, NULL, extension);
}
return temp_streamFile;
fail:
close_streamfile(temp_streamFile);
return NULL;
return new_sf;
}
#endif /* _AIX_STREAMFILE_H_ */

View File

@ -91,7 +91,7 @@ VGMSTREAM * init_vgmstream_akb(STREAMFILE *streamFile) {
case 0x02: { /* MSADPCM [Dragon Quest II (iOS) sfx] */
vgmstream->coding_type = coding_MSADPCM;
vgmstream->layout_type = layout_none;
vgmstream->interleave_block_size = read_16bitLE(extradata_offset + 0x02,streamFile);
vgmstream->frame_size = read_16bitLE(extradata_offset + 0x02,streamFile);
/* adjusted samples; bigger or smaller than base samples, akb lib uses these fields instead
* (base samples may have more than possible and read over file size otherwise, very strange)
@ -277,7 +277,7 @@ VGMSTREAM * init_vgmstream_akb2(STREAMFILE *streamFile) {
case 0x02: { /* MSADPCM [The Irregular at Magic High School Lost Zero (Android)] */
vgmstream->coding_type = coding_MSADPCM;
vgmstream->layout_type = layout_none;
vgmstream->interleave_block_size = read_16bitLE(extradata_offset + 0x02, streamFile);
vgmstream->frame_size = read_16bitLE(extradata_offset + 0x02, streamFile);
/* adjusted samples; bigger or smaller than base samples, akb lib uses these fields instead
* (base samples may have more than possible and read over file size otherwise, very strange)

View File

@ -5,20 +5,32 @@
VGMSTREAM * init_vgmstream_bwav(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
off_t start_offset;
int channel_count, loop_flag;
int32_t coef_start_offset, coef_spacing;
int channel_count, loop_flag, codec;
size_t interleave = 0;
/* checks */
if (!check_extensions(streamFile, "bwav"))
goto fail;
/* BWAV header */
if (read_32bitBE(0x00, streamFile) != 0x42574156) /* "BWAV" */
goto fail;
/* 0x04: BOM */
/* 0x06: version? */
/* 0x08: ??? */
/* 0x0c: null? */
channel_count = read_16bitLE(0x0E, streamFile);
/* - per channel (size 0x4c) */
codec = read_16bitLE(0x10, streamFile);
/* see below */
start_offset = read_32bitLE(0x40, streamFile);
loop_flag = read_32bitLE(0x4C, streamFile);
loop_flag = read_32bitLE(0x4C, streamFile) != -1;
if (channel_count > 1)
interleave = read_32bitLE(0x8C, streamFile) - start_offset;
//TODO should make sure channels match and offsets make a proper interleave (see bfwav)
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count, loop_flag);
@ -29,14 +41,24 @@ VGMSTREAM * init_vgmstream_bwav(STREAMFILE *streamFile) {
vgmstream->loop_start_sample = read_32bitLE(0x50, streamFile);
vgmstream->loop_end_sample = read_32bitLE(0x4C, streamFile);
vgmstream->meta_type = meta_BWAV;
vgmstream->layout_type = (channel_count == 1) ? layout_none : layout_interleave;
vgmstream->interleave_block_size = read_32bitLE(0x8C, streamFile) - start_offset;
vgmstream->coding_type = coding_NGC_DSP;
coef_start_offset = 0x20;
coef_spacing = 0x4C;
dsp_read_coefs_le(vgmstream, streamFile, coef_start_offset, coef_spacing);
switch(codec) {
case 0x0000:
vgmstream->coding_type = coding_PCM16LE;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = interleave;
break;
case 0x0001:
vgmstream->coding_type = coding_NGC_DSP;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = interleave;
dsp_read_coefs_le(vgmstream, streamFile, 0x20, 0x4C);
break;
default:
goto fail;
}
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
goto fail;

View File

@ -56,7 +56,7 @@ VGMSTREAM * init_vgmstream_cks(STREAMFILE *streamFile) {
break;
case 0x02: /* adpcm [Part Time UFO (Android), Mega Man 1-6 (Android)] */
vgmstream->coding_type = coding_MSADPCM_ck;
/* frame_size is always 0x18 */
vgmstream->frame_size = block_size / channel_count; /* always 0x18 */
break;
default:
goto fail;

View File

@ -76,7 +76,7 @@ VGMSTREAM * init_vgmstream_dec(STREAMFILE *streamFile) {
vgmstream->loop_end_sample = loop_end;
vgmstream->coding_type = coding_MSADPCM;
vgmstream->interleave_block_size = 0x800;
vgmstream->frame_size = 0x800;
vgmstream->layout_type = layout_blocked_dec;
if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) )
@ -122,10 +122,10 @@ static int get_falcom_looping(STREAMFILE *streamFile, int *out_loop_start, int *
while (txt_offset < get_streamfile_size(streamText)) {
char line[TXT_LINE_MAX];
char name[TXT_LINE_MAX];
int ok, line_done, loop, bytes_read;
int ok, line_ok, loop, bytes_read;
bytes_read = get_streamfile_text_line(TXT_LINE_MAX,line, txt_offset,streamText, &line_done);
if (!line_done) goto end;
bytes_read = read_line(line, TXT_LINE_MAX, txt_offset, streamText, &line_ok);
if (!line_ok) goto end;
txt_offset += bytes_read;

View File

@ -225,13 +225,15 @@ VGMSTREAM * init_vgmstream_genh(STREAMFILE *streamFile) {
vgmstream->interleave_block_size = genh.interleave;
vgmstream->layout_type = layout_none;
break;
case coding_MSADPCM:
if (vgmstream->channels > 2) goto fail;
if (!genh.interleave) goto fail; /* creates garbage */
if (!genh.interleave) goto fail;
vgmstream->interleave_block_size = genh.interleave;
vgmstream->frame_size = genh.interleave;
vgmstream->layout_type = layout_none;
break;
case coding_XBOX_IMA:
if (genh.codec_mode == 1) { /* mono interleave */
coding = coding_XBOX_IMA_int;

View File

@ -128,7 +128,7 @@ typedef struct {
} ogg_vorbis_meta_info_t;
VGMSTREAM * init_vgmstream_ogg_vorbis_callbacks(STREAMFILE *streamFile, ov_callbacks *callbacks, off_t other_header_bytes, const ogg_vorbis_meta_info_t *ovmi);
VGMSTREAM * init_vgmstream_ogg_vorbis_callbacks(STREAMFILE *streamFile, ov_callbacks *callbacks, off_t start, const ogg_vorbis_meta_info_t *ovmi);
#endif
VGMSTREAM * init_vgmstream_hca(STREAMFILE *streamFile);

View File

@ -188,15 +188,15 @@ static char** parse_mus(STREAMFILE *streamFile, int *out_file_count, int *out_lo
char** names = NULL;
char filename[NAME_LENGTH];
char line_buffer[NAME_LENGTH];
char line[NAME_LENGTH];
char * end_ptr;
char name_base[NAME_LENGTH];
char dir_name[NAME_LENGTH];
char subdir_name[NAME_LENGTH];
int file_count;
size_t line_bytes;
int whole_line_read = 0;
size_t bytes_read;
int line_ok = 0;
off_t mus_offset = 0;
int i;
@ -204,10 +204,10 @@ static char** parse_mus(STREAMFILE *streamFile, int *out_file_count, int *out_lo
/* read file name base */
line_bytes = get_streamfile_text_line(sizeof(line_buffer),line_buffer, mus_offset, streamFile, &whole_line_read);
if (!whole_line_read) goto fail;
mus_offset += line_bytes;
memcpy(name_base,line_buffer,sizeof(name_base));
bytes_read = read_line(line, sizeof(line), mus_offset, streamFile, &line_ok);
if (!line_ok) goto fail;
mus_offset += bytes_read;
memcpy(name_base,line,sizeof(name_base));
/* uppercase name_base */
{
@ -217,11 +217,11 @@ static char** parse_mus(STREAMFILE *streamFile, int *out_file_count, int *out_lo
}
/* read track entry count */
line_bytes = get_streamfile_text_line(sizeof(line_buffer),line_buffer, mus_offset, streamFile, &whole_line_read);
if (!whole_line_read) goto fail;
if (line_buffer[0] == '\0') goto fail;
mus_offset += line_bytes;
file_count = strtol(line_buffer,&end_ptr,10);
bytes_read = read_line(line, sizeof(line), mus_offset, streamFile, &line_ok);
if (!line_ok) goto fail;
if (line[0] == '\0') goto fail;
mus_offset += bytes_read;
file_count = strtol(line,&end_ptr,10);
/* didn't parse whole line as an integer (optional opening whitespace) */
if (*end_ptr != '\0') goto fail;
@ -262,13 +262,11 @@ static char** parse_mus(STREAMFILE *streamFile, int *out_file_count, int *out_lo
for (i = 0; i < file_count; i++)
{
int fields_matched;
line_bytes =
get_streamfile_text_line(sizeof(line_buffer),line_buffer,
mus_offset, streamFile, &whole_line_read);
if (!whole_line_read) goto fail;
mus_offset += line_bytes;
bytes_read = read_line(line,sizeof(line), mus_offset, streamFile, &line_ok);
if (!line_ok) goto fail;
mus_offset += bytes_read;
fields_matched = sscanf(line_buffer,"%s %s %s",name,
fields_matched = sscanf(line,"%s %s %s",name,
loop_name_base_temp,loop_name_temp);
if (fields_matched < 1) goto fail;

View File

@ -45,15 +45,22 @@ VGMSTREAM * init_vgmstream_mzrt(STREAMFILE *streamFile) {
goto fail;
}
/* skip fmt-extra data */
if (codec == 0x0002 || codec == 0x0166) {
/* skip MSADPCM data */
if (codec == 0x0002) {
if (!msadpcm_check_coefs(streamFile, start_offset + 0x02 + 0x02))
goto fail;
start_offset += 0x02 + read_16bitLE(start_offset, streamFile);
}
/* skip extra data */
if (codec == 0x0166) {
start_offset += 0x02 + read_16bitLE(start_offset, streamFile);
}
/* skip unknown table */
if (codec == 0x0000) {
start_offset += 0x04 + read_32bitBE(start_offset, streamFile) * 0x04;
}
/* skip unknown table */
@ -95,7 +102,7 @@ VGMSTREAM * init_vgmstream_mzrt(STREAMFILE *streamFile) {
if (bps != 4) goto fail;
vgmstream->coding_type = coding_MSADPCM;
vgmstream->layout_type = layout_none;
vgmstream->interleave_block_size = block_size;
vgmstream->frame_size = block_size;
break;
#ifdef VGM_USE_FFMPEG

View File

@ -2,12 +2,12 @@
#include "../coding/coding.h"
static STREAMFILE* make_nub_streamfile(STREAMFILE* streamFile, off_t header_offset, size_t header_size, off_t stream_offset, size_t stream_size, const char* fake_ext);
static STREAMFILE* setup_nub_streamfile(STREAMFILE *sf, off_t header_offset, size_t header_size, off_t stream_offset, size_t stream_size, const char *fake_ext);
/* .nub - Namco's nu Sound v2 audio container */
VGMSTREAM * init_vgmstream_nub(STREAMFILE *streamFile) {
VGMSTREAM *vgmstream = NULL;
STREAMFILE *temp_streamFile = NULL;
STREAMFILE *temp_sf = NULL;
off_t name_offset = 0;
size_t name_size = 0;
int total_subsongs, target_subsong = streamFile->stream_index;
@ -133,8 +133,8 @@ VGMSTREAM * init_vgmstream_nub(STREAMFILE *streamFile) {
//;VGM_LOG("NUB: subfile header=%lx + %x, offset=%lx + %x\n", header_offset, header_size, stream_offset, stream_size);
temp_streamFile = make_nub_streamfile(streamFile, header_offset, header_size, stream_offset, stream_size, fake_ext);
if (!temp_streamFile) goto fail;
temp_sf = setup_nub_streamfile(streamFile, header_offset, header_size, stream_offset, stream_size, fake_ext);
if (!temp_sf) goto fail;
}
/* get names */
@ -166,66 +166,36 @@ VGMSTREAM * init_vgmstream_nub(STREAMFILE *streamFile) {
}
/* init the VGMSTREAM */
vgmstream = init_vgmstream_function(temp_streamFile);
vgmstream = init_vgmstream_function(temp_sf);
if (!vgmstream) goto fail;
vgmstream->stream_size = get_streamfile_size(temp_streamFile);
vgmstream->stream_size = get_streamfile_size(temp_sf);
vgmstream->num_streams = total_subsongs;
if (name[0] != '\0')
strcpy(vgmstream->stream_name, name);
close_streamfile(temp_streamFile);
close_streamfile(temp_sf);
return vgmstream;
fail:
close_streamfile(temp_streamFile);
close_streamfile(temp_sf);
close_vgmstream(vgmstream);
return NULL;
}
/* *********************************************************** */
static STREAMFILE* make_nub_streamfile(STREAMFILE* streamFile, off_t header_offset, size_t header_size, off_t stream_offset, size_t stream_size, const char* fake_ext) {
STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL;
STREAMFILE *segment_streamFiles[2] = {0};
int i;
static STREAMFILE* setup_nub_streamfile(STREAMFILE *sf, off_t header_offset, size_t header_size, off_t stream_offset, size_t stream_size, const char *fake_ext) {
STREAMFILE *new_sf = NULL;
STREAMFILE *multi_sf[2] = {0};
new_streamFile = open_wrap_streamfile(streamFile);
if (!new_streamFile) goto fail;
segment_streamFiles[0] = new_streamFile;
new_streamFile = open_wrap_streamfile(streamFile);
if (!new_streamFile) goto fail;
segment_streamFiles[1] = new_streamFile;
new_streamFile = open_clamp_streamfile(segment_streamFiles[0], header_offset,header_size);
if (!new_streamFile) goto fail;
segment_streamFiles[0] = new_streamFile;
new_streamFile = open_clamp_streamfile(segment_streamFiles[1], stream_offset,stream_size);
if (!new_streamFile) goto fail;
segment_streamFiles[1] = new_streamFile;
new_streamFile = open_multifile_streamfile(segment_streamFiles, 2);
if (!new_streamFile) goto fail;
temp_streamFile = new_streamFile;
new_streamFile = open_fakename_streamfile(temp_streamFile, NULL, fake_ext);
if (!new_streamFile) goto fail;
temp_streamFile = new_streamFile;
return temp_streamFile;
fail:
if (!temp_streamFile) {
for (i = 0; i < 2; i++) {
close_streamfile(segment_streamFiles[i]);
}
} else {
close_streamfile(temp_streamFile); /* closes all segments */
}
return NULL;
multi_sf[0] = open_wrap_streamfile(sf);
multi_sf[0] = open_clamp_streamfile_f(multi_sf[0], header_offset, header_size);
multi_sf[1] = open_wrap_streamfile(sf);
multi_sf[1] = open_clamp_streamfile_f(multi_sf[1], stream_offset, stream_size);
new_sf = open_multifile_streamfile_f(multi_sf, 2);
new_sf = open_fakename_streamfile_f(new_sf, NULL, fake_ext);
return new_sf;
}
/* *********************************************************** */
@ -318,7 +288,7 @@ fail:
/* .nub at3 - from Namco NUB archives [Ridge Racer 7 (PS3), Katamari Forever (PS3)] */
VGMSTREAM * init_vgmstream_nub_at3(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
STREAMFILE *temp_streamFile = NULL;
STREAMFILE *temp_sf = NULL;
off_t subfile_offset = 0;
size_t subfile_size = 0;
@ -333,16 +303,16 @@ VGMSTREAM * init_vgmstream_nub_at3(STREAMFILE *streamFile) {
subfile_offset = 0x100;
subfile_size = read_32bitLE(subfile_offset + 0x04, streamFile) + 0x08; /* RIFF size */
temp_streamFile = setup_subfile_streamfile(streamFile, subfile_offset,subfile_size, NULL);
if (!temp_streamFile) goto fail;
temp_sf = setup_subfile_streamfile(streamFile, subfile_offset,subfile_size, NULL);
if (!temp_sf) goto fail;
vgmstream = init_vgmstream_riff(temp_streamFile);
vgmstream = init_vgmstream_riff(temp_sf);
if (!vgmstream) goto fail;
close_streamfile(temp_streamFile);
close_streamfile(temp_sf);
return vgmstream;
fail:
close_streamfile(temp_streamFile);
close_streamfile(temp_sf);
close_vgmstream(vgmstream);
return NULL;
}
@ -512,7 +482,7 @@ fail:
/* .nub is14 - from Namco NUB archives [Tales of Vesperia (PS3)] */
VGMSTREAM * init_vgmstream_nub_is14(STREAMFILE *streamFile) {
VGMSTREAM *vgmstream = NULL;
STREAMFILE *temp_streamFile = NULL;
STREAMFILE *temp_sf = NULL;
off_t header_offset, stream_offset;
size_t header_size, stream_size, sdat_size;
int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL;
@ -542,16 +512,16 @@ VGMSTREAM * init_vgmstream_nub_is14(STREAMFILE *streamFile) {
stream_size = sdat_size;
temp_streamFile = make_nub_streamfile(streamFile, header_offset, header_size, stream_offset, stream_size, "bnsf");
if (!temp_streamFile) goto fail;
temp_sf = setup_nub_streamfile(streamFile, header_offset, header_size, stream_offset, stream_size, "bnsf");
if (!temp_sf) goto fail;
vgmstream = init_vgmstream_bnsf(temp_streamFile);
vgmstream = init_vgmstream_bnsf(temp_sf);
if (!vgmstream) goto fail;
close_streamfile(temp_streamFile);
close_streamfile(temp_sf);
return vgmstream;
fail:
close_streamfile(temp_streamFile);
close_streamfile(temp_sf);
close_vgmstream(vgmstream);
return NULL;
}

View File

@ -1,123 +1,60 @@
#include "../vgmstream.h"
#ifdef VGM_USE_VORBIS
#include <stdio.h>
#include <string.h>
#include <vorbis/vorbisfile.h>
#include "meta.h"
#include "../coding/coding.h"
#include "ogg_vorbis_streamfile.h"
#define OGG_DEFAULT_BITSTREAM 0
static size_t ov_read_func(void *ptr, size_t size, size_t nmemb, void * datasource) {
ogg_vorbis_streamfile * const ov_streamfile = datasource;
size_t bytes_read, items_read;
off_t real_offset = ov_streamfile->start + ov_streamfile->offset;
size_t max_bytes = size * nmemb;
/* clamp for virtual filesize */
if (max_bytes > ov_streamfile->size - ov_streamfile->offset)
max_bytes = ov_streamfile->size - ov_streamfile->offset;
bytes_read = read_streamfile(ptr, real_offset, max_bytes, ov_streamfile->streamfile);
items_read = bytes_read / size;
/* may be encrypted */
if (ov_streamfile->decryption_callback) {
ov_streamfile->decryption_callback(ptr, size, items_read, ov_streamfile);
}
ov_streamfile->offset += items_read * size;
return items_read;
}
static int ov_seek_func(void *datasource, ogg_int64_t offset, int whence) {
ogg_vorbis_streamfile * const ov_streamfile = datasource;
ogg_int64_t base_offset, new_offset;
switch (whence) {
case SEEK_SET:
base_offset = 0;
break;
case SEEK_CUR:
base_offset = ov_streamfile->offset;
break;
case SEEK_END:
base_offset = ov_streamfile->size;
break;
default:
return -1;
break;
}
new_offset = base_offset + offset;
if (new_offset < 0 || new_offset > ov_streamfile->size) {
return -1; /* *must* return -1 if stream is unseekable */
} else {
ov_streamfile->offset = new_offset;
return 0;
}
}
static long ov_tell_func(void * datasource) {
ogg_vorbis_streamfile * const ov_streamfile = datasource;
return ov_streamfile->offset;
}
static int ov_close_func(void * datasource) {
/* needed as setting ov_close_func in ov_callbacks to NULL doesn't seem to work
* (closing the streamfile is done in free_ogg_vorbis) */
return 0;
}
static void um3_ogg_decryption_callback(void *ptr, size_t size, size_t nmemb, void *datasource) {
size_t bytes_read = size*nmemb;
ogg_vorbis_streamfile * const ov_streamfile = datasource;
uint8_t *ptr8 = ptr;
size_t bytes_read = size * nmemb;
ogg_vorbis_io *io = datasource;
int i;
/* first 0x800 bytes are xor'd */
if (ov_streamfile->offset < 0x800) {
int num_crypt = 0x800 - ov_streamfile->offset;
if (io->offset < 0x800) {
int num_crypt = 0x800 - io->offset;
if (num_crypt > bytes_read)
num_crypt = bytes_read;
for (i = 0; i < num_crypt; i++)
((uint8_t*)ptr)[i] ^= 0xff;
ptr8[i] ^= 0xff;
}
}
static void kovs_ogg_decryption_callback(void *ptr, size_t size, size_t nmemb, void *datasource) {
size_t bytes_read = size*nmemb;
ogg_vorbis_streamfile * const ov_streamfile = datasource;
uint8_t *ptr8 = ptr;
size_t bytes_read = size * nmemb;
ogg_vorbis_io *io = datasource;
int i;
/* first 0x100 bytes are xor'd */
if (ov_streamfile->offset < 0x100) {
int max_offset = ov_streamfile->offset + bytes_read;
if (io->offset < 0x100) {
int max_offset = io->offset + bytes_read;
if (max_offset > 0x100)
max_offset = 0x100;
for (i = ov_streamfile->offset; i < max_offset; i++) {
((uint8_t*)ptr)[i-ov_streamfile->offset] ^= i;
for (i = io->offset; i < max_offset; i++) {
ptr8[i-io->offset] ^= i;
}
}
}
static void psychic_ogg_decryption_callback(void *ptr, size_t size, size_t nmemb, void *datasource) {
ogg_vorbis_streamfile * const ov_streamfile = datasource;
size_t bytes_read = size*nmemb;
uint8_t key[6] = { 0x23,0x31,0x20,0x2e,0x2e,0x28 };
static const uint8_t key[6] = {
0x23,0x31,0x20,0x2e,0x2e,0x28
};
uint8_t *ptr8 = ptr;
size_t bytes_read = size * nmemb;
ogg_vorbis_io *io = datasource;
int i;
//todo incorrect, picked value changes (fixed order for all files), or key is bigger
/* bytes add key that changes every 0x64 bytes */
for (i = 0; i < bytes_read; i++) {
int pos = (ov_streamfile->offset + i) / 0x64;
((uint8_t*)ptr)[i] += key[pos % sizeof(key)];
int pos = (io->offset + i) / 0x64;
ptr8[i] += key[pos % sizeof(key)];
}
}
@ -125,21 +62,22 @@ static void rpgmvo_ogg_decryption_callback(void *ptr, size_t size, size_t nmemb,
static const uint8_t header[16] = { /* OggS, packet type, granule, stream id(empty) */
0x4F,0x67,0x67,0x53,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
};
uint8_t *ptr8 = ptr;
size_t bytes_read = size*nmemb;
ogg_vorbis_streamfile * const ov_streamfile = datasource;
ogg_vorbis_io *io = datasource;
int i;
/* first 0x10 are xor'd, but header can be easily reconstructed
* (key is also in (game)/www/data/System.json "encryptionKey") */
for (i = 0; i < bytes_read; i++) {
if (ov_streamfile->offset+i < 0x10) {
((uint8_t*)ptr)[i] = header[(ov_streamfile->offset + i) % 16];
if (io->offset+i < 0x10) {
ptr8[i] = header[(io->offset + i) % 16];
/* last two bytes are the stream id, get from next OggS */
if (ov_streamfile->offset+i == 0x0e)
((uint8_t*)ptr)[i] = read_8bit(0x58, ov_streamfile->streamfile);
if (ov_streamfile->offset+i == 0x0f)
((uint8_t*)ptr)[i] = read_8bit(0x59, ov_streamfile->streamfile);
if (io->offset+i == 0x0e)
ptr8[i] = read_8bit(0x58, io->streamfile);
if (io->offset+i == 0x0f)
ptr8[i] = read_8bit(0x59, io->streamfile);
}
}
}
@ -158,7 +96,7 @@ static const uint32_t xiph_mappings[] = {
};
/* Ogg Vorbis, by way of libvorbisfile; may contain loop comments */
/* Ogg Vorbis, may contain loop comments */
VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
STREAMFILE *temp_streamFile = NULL;
@ -181,7 +119,7 @@ VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) {
/* check extension */
/* .ogg: standard/various, .logg: renamed for plugins
* .adx: KID [Remember11 (PC)]
* .adx: KID games [Remember11 (PC)]
* .rof: The Rhythm of Fighters (Mobile)
* .acm: Planescape Torment Enhanced Edition (PC)
* .sod: Zone 4 (PC)
@ -190,7 +128,8 @@ VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) {
is_ogg = 1;
} else if (check_extensions(streamFile,"um3")) {
is_um3 = 1;
} else if (check_extensions(streamFile,"kvs,kovs")) { /* .kvs: Atelier Sophie (PC), kovs: header id only? */
} else if (check_extensions(streamFile,"kvs,kovs")) {
/* .kvs: Atelier Sophie (PC), kovs: header id only? */
is_kovs = 1;
} else if (check_extensions(streamFile,"sngw")) { /* .sngw: Capcom [Devil May Cry 4 SE (PC), Biohazard 6 (PC)] */
is_sngw = 1;
@ -228,11 +167,11 @@ VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) {
}
else if (read_32bitBE(0x00,streamFile) == 0x00000000 && /* null instead of "OggS" [Yuppie Psycho (PC)] */
read_32bitBE(0x3a,streamFile) == 0x4F676753) {
read_32bitBE(0x3a,streamFile) == 0x4F676753) { /* "OggS" in next page */
cfg.is_header_swap = 1;
cfg.is_encrypted = 1;
}
else if (read_32bitBE(0x00,streamFile) == 0x4f676753) { /* "OggS" (standard) */
else if (read_32bitBE(0x00,streamFile) == 0x4F676753) { /* "OggS" (standard) */
;
}
else {
@ -382,7 +321,7 @@ VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) {
if (is_eno) { /* [Metronomicon (PC)] */
/* first byte probably derives into key, but this works too */
cfg.key[0] = (uint8_t)read_8bit(0x05,streamFile); /* regular ogg have a zero at this offset = easy key */;
cfg.key[0] = (uint8_t)read_8bit(0x05,streamFile); /* regular ogg have a zero at this offset = easy key */
cfg.key_len = 1;
cfg.is_encrypted = 1;
start_offset = 0x01; /* "OggS" starts after key-thing */
@ -394,7 +333,7 @@ VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) {
cfg.is_encrypted = 1;
}
if (is_mus) { /* [Redux - Dark Matters (PC)] */
if (is_mus) { /* [Redux: Dark Matters (PC)] */
static const uint8_t mus_key[16] = {
0x21,0x4D,0x6F,0x01,0x20,0x4C,0x6E,0x02,0x1F,0x4B,0x6D,0x03,0x20,0x4C,0x6E,0x02
};
@ -464,12 +403,12 @@ fail:
return NULL;
}
VGMSTREAM * init_vgmstream_ogg_vorbis_callbacks(STREAMFILE *streamFile, ov_callbacks *callbacks_p, off_t start, const ogg_vorbis_meta_info_t *ovmi) {
VGMSTREAM * init_vgmstream_ogg_vorbis_callbacks(STREAMFILE *streamFile, ov_callbacks *callbacks, off_t start, const ogg_vorbis_meta_info_t *ovmi) {
VGMSTREAM * vgmstream = NULL;
ogg_vorbis_codec_data * data = NULL;
OggVorbis_File *ovf = NULL;
vorbis_info *vi;
ogg_vorbis_codec_data* data = NULL;
ogg_vorbis_io io = {0};
char name[STREAM_NAME_SIZE] = {0};
int channels, sample_rate, num_samples;
int loop_flag = ovmi->loop_flag;
int32_t loop_start = ovmi->loop_start;
@ -480,156 +419,96 @@ VGMSTREAM * init_vgmstream_ogg_vorbis_callbacks(STREAMFILE *streamFile, ov_callb
size_t stream_size = ovmi->stream_size ?
ovmi->stream_size :
get_streamfile_size(streamFile) - start;
ov_callbacks default_callbacks;
if (!callbacks_p) {
default_callbacks.read_func = ov_read_func;
default_callbacks.seek_func = ov_seek_func;
default_callbacks.close_func = ov_close_func;
default_callbacks.tell_func = ov_tell_func;
callbacks_p = &default_callbacks;
}
/* test if this is a proper Ogg Vorbis file, with the current (from init_x) STREAMFILE */
{
OggVorbis_File temp_ovf = {0};
ogg_vorbis_streamfile temp_streamfile = {0};
temp_streamfile.streamfile = streamFile;
temp_streamfile.start = start;
temp_streamfile.offset = 0;
temp_streamfile.size = stream_size;
temp_streamfile.decryption_callback = ovmi->decryption_callback;
temp_streamfile.scd_xor = ovmi->scd_xor;
temp_streamfile.scd_xor_length = ovmi->scd_xor_length;
temp_streamfile.xor_value = ovmi->xor_value;
/* open the ogg vorbis file for testing */
if (ov_test_callbacks(&temp_streamfile, &temp_ovf, NULL, 0, *callbacks_p))
goto fail;
/* we have to close this as it has the init_vgmstream meta-reading STREAMFILE */
ov_clear(&temp_ovf);
}
int disable_reordering = ovmi->disable_reordering;
/* proceed to init codec_data and reopen a STREAMFILE for this stream */
{
char filename[PATH_LIMIT];
//todo improve how to pass config
io.decryption_callback = ovmi->decryption_callback;
io.scd_xor = ovmi->scd_xor;
io.scd_xor_length = ovmi->scd_xor_length;
io.xor_value = ovmi->xor_value;
data = calloc(1,sizeof(ogg_vorbis_codec_data));
if (!data) goto fail;
streamFile->get_name(streamFile,filename,sizeof(filename));
data->ov_streamfile.streamfile = streamFile->open(streamFile,filename, STREAMFILE_DEFAULT_BUFFER_SIZE);
if (!data->ov_streamfile.streamfile) goto fail;
data->ov_streamfile.start = start;
data->ov_streamfile.offset = 0;
data->ov_streamfile.size = stream_size;
data->ov_streamfile.decryption_callback = ovmi->decryption_callback;
data->ov_streamfile.scd_xor = ovmi->scd_xor;
data->ov_streamfile.scd_xor_length = ovmi->scd_xor_length;
data->ov_streamfile.xor_value = ovmi->xor_value;
/* open the ogg vorbis file for real */
if (ov_open_callbacks(&data->ov_streamfile, &data->ogg_vorbis_file, NULL, 0, *callbacks_p))
goto fail;
ovf = &data->ogg_vorbis_file;
}
//todo could set bitstreams as subsongs?
/* get info from bitstream 0 */
data->bitstream = OGG_DEFAULT_BITSTREAM;
vi = ov_info(ovf,OGG_DEFAULT_BITSTREAM);
/* other settings */
data->disable_reordering = ovmi->disable_reordering;
data = init_ogg_vorbis(streamFile, start, stream_size, &io);
if (!data) goto fail;
/* search for loop comments */
{//todo ignore if loop flag already set?
int i;
vorbis_comment *comment = ov_comment(ovf,OGG_DEFAULT_BITSTREAM);
const char * comment = NULL;
for (i = 0; i < comment->comments; i++) {
const char * user_comment = comment->user_comments[i];
if (strstr(user_comment,"loop_start=")==user_comment || /* PSO4 */
strstr(user_comment,"LOOP_START=")==user_comment || /* PSO4 */
strstr(user_comment,"COMMENT=LOOPPOINT=")==user_comment ||
strstr(user_comment,"LOOPSTART=")==user_comment ||
strstr(user_comment,"um3.stream.looppoint.start=")==user_comment ||
strstr(user_comment,"LOOP_BEGIN=")==user_comment || /* Hatsune Miku: Project Diva F (PS3) */
strstr(user_comment,"LoopStart=")==user_comment || /* Devil May Cry 4 (PC) */
strstr(user_comment,"XIPH_CUE_LOOPSTART=")==user_comment) { /* Super Mario Run (Android) */
loop_start = atol(strrchr(user_comment,'=')+1);
while (ogg_vorbis_get_comment(data, &comment)) {
if (strstr(comment,"loop_start=") == comment || /* PSO4 */
strstr(comment,"LOOP_START=") == comment || /* PSO4 */
strstr(comment,"COMMENT=LOOPPOINT=") == comment ||
strstr(comment,"LOOPSTART=") == comment ||
strstr(comment,"um3.stream.looppoint.start=") == comment ||
strstr(comment,"LOOP_BEGIN=") == comment || /* Hatsune Miku: Project Diva F (PS3) */
strstr(comment,"LoopStart=") == comment || /* Devil May Cry 4 (PC) */
strstr(comment,"XIPH_CUE_LOOPSTART=") == comment) { /* Super Mario Run (Android) */
loop_start = atol(strrchr(comment,'=')+1);
loop_flag = (loop_start >= 0);
}
else if (strstr(user_comment,"LOOPLENGTH=")==user_comment) {/* (LOOPSTART pair) */
loop_length = atol(strrchr(user_comment,'=')+1);
else if (strstr(comment,"LOOPLENGTH=") == comment) {/* (LOOPSTART pair) */
loop_length = atol(strrchr(comment,'=')+1);
loop_length_found = 1;
}
else if (strstr(user_comment,"title=-lps")==user_comment) { /* KID [Memories Off #5 (PC), Remember11 (PC)] */
loop_start = atol(user_comment+10);
else if (strstr(comment,"title=-lps") == comment) { /* KID [Memories Off #5 (PC), Remember11 (PC)] */
loop_start = atol(comment+10);
loop_flag = (loop_start >= 0);
}
else if (strstr(user_comment,"album=-lpe")==user_comment) { /* (title=-lps pair) */
loop_end = atol(user_comment+10);
else if (strstr(comment,"album=-lpe") == comment) { /* (title=-lps pair) */
loop_end = atol(comment+10);
loop_flag = 1;
loop_end_found = 1;
}
else if (strstr(user_comment,"LoopEnd=")==user_comment) { /* (LoopStart pair) */
else if (strstr(comment,"LoopEnd=") == comment) { /* (LoopStart pair) */
if(loop_flag) {
loop_length = atol(strrchr(user_comment,'=')+1)-loop_start;
loop_length = atol(strrchr(comment,'=')+1)-loop_start;
loop_length_found = 1;
}
}
else if (strstr(user_comment,"LOOP_END=")==user_comment) { /* (LOOP_BEGIN pair) */
else if (strstr(comment,"LOOP_END=") == comment) { /* (LOOP_BEGIN pair) */
if(loop_flag) {
loop_length = atol(strrchr(user_comment,'=')+1)-loop_start;
loop_length = atol(strrchr(comment,'=')+1)-loop_start;
loop_length_found = 1;
}
}
else if (strstr(user_comment,"lp=")==user_comment) {
sscanf(strrchr(user_comment,'=')+1,"%d,%d", &loop_start,&loop_end);
else if (strstr(comment,"lp=") == comment) {
sscanf(strrchr(comment,'=')+1,"%d,%d", &loop_start,&loop_end);
loop_flag = 1;
loop_end_found = 1;
}
else if (strstr(user_comment,"LOOPDEFS=")==user_comment) { /* Fairy Fencer F: Advent Dark Force */
sscanf(strrchr(user_comment,'=')+1,"%d,%d", &loop_start,&loop_end);
else if (strstr(comment,"LOOPDEFS=") == comment) { /* Fairy Fencer F: Advent Dark Force */
sscanf(strrchr(comment,'=')+1,"%d,%d", &loop_start,&loop_end);
loop_flag = 1;
loop_end_found = 1;
}
else if (strstr(user_comment,"COMMENT=loop(")==user_comment) { /* Zero Time Dilemma (PC) */
sscanf(strrchr(user_comment,'(')+1,"%d,%d", &loop_start,&loop_end);
else if (strstr(comment,"COMMENT=loop(") == comment) { /* Zero Time Dilemma (PC) */
sscanf(strrchr(comment,'(')+1,"%d,%d", &loop_start,&loop_end);
loop_flag = 1;
loop_end_found = 1;
}
else if (strstr(user_comment, "XIPH_CUE_LOOPEND=") == user_comment) { /* XIPH_CUE_LOOPSTART pair */
else if (strstr(comment, "XIPH_CUE_LOOPEND=") == comment) { /* (XIPH_CUE_LOOPSTART pair) */
if (loop_flag) {
loop_length = atol(strrchr(user_comment, '=') + 1) - loop_start;
loop_length = atol(strrchr(comment, '=') + 1) - loop_start;
loop_length_found = 1;
}
}
else if (strstr(user_comment, "omment=") == user_comment) { /* Air (Android) */
sscanf(strstr(user_comment, "=LOOPSTART=") + 11, "%d,LOOPEND=%d", &loop_start, &loop_end);
else if (strstr(comment, "omment=") == comment) { /* Air (Android) */
sscanf(strstr(comment, "=LOOPSTART=") + 11, "%d,LOOPEND=%d", &loop_start, &loop_end);
loop_flag = 1;
loop_end_found = 1;
}
else if (strstr(user_comment,"MarkerNum=0002")==user_comment) { /* Megaman X Legacy Collection: MMX1/2/3 (PC) flag */
else if (strstr(comment,"MarkerNum=0002") == comment) { /* Megaman X Legacy Collection: MMX1/2/3 (PC) flag */
/* uses LoopStart=-1 LoopEnd=-1, then 3 secuential comments: "MarkerNum" + "M=7F(start)" + "M=7F(end)" */
loop_flag = 1;
}
else if (strstr(user_comment,"M=7F")==user_comment) { /* Megaman X Legacy Collection: MMX1/2/3 (PC) start/end */
else if (strstr(comment,"M=7F") == comment) { /* Megaman X Legacy Collection: MMX1/2/3 (PC) start/end */
if (loop_flag && loop_start < 0) { /* LoopStart should set as -1 before */
sscanf(user_comment,"M=7F%x", &loop_start);
sscanf(comment,"M=7F%x", &loop_start);
}
else if (loop_flag && loop_start >= 0) {
sscanf(user_comment,"M=7F%x", &loop_end);
sscanf(comment,"M=7F%x", &loop_end);
loop_end_found = 1;
}
}
@ -637,26 +516,33 @@ VGMSTREAM * init_vgmstream_ogg_vorbis_callbacks(STREAMFILE *streamFile, ov_callb
/* Hatsune Miku Project DIVA games, though only 'Arcade Future Tone' has >4ch files
* ENCODER tag is common but ogg_vorbis_encode looks unique enough
* (arcade ends with "2010-11-26" while consoles have "2011-02-07" */
if (strstr(user_comment, "ENCODER=ogg_vorbis_encode/") == user_comment) {
data->disable_reordering = 1;
if (strstr(comment, "ENCODER=ogg_vorbis_encode/") == comment) {
disable_reordering = 1;
}
if (strstr(user_comment, "TITLE=") == user_comment) {
strncpy(name, user_comment + 6, sizeof(name) - 1);
if (strstr(comment, "TITLE=") == comment) {
strncpy(name, comment + 6, sizeof(name) - 1);
}
;VGM_LOG("OGG: user_comment=%s\n", user_comment);
;VGM_LOG("OGG: user_comment=%s\n", comment);
}
}
ogg_vorbis_set_disable_reordering(data, disable_reordering);
ogg_vorbis_get_info(data, &channels, &sample_rate);
ogg_vorbis_get_samples(data, &num_samples); /* let libvorbisfile find total samples */
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(vi->channels,loop_flag);
vgmstream = allocate_vgmstream(channels,loop_flag);
if (!vgmstream) goto fail;
vgmstream->codec_data = data; /* store our fun extra datas */
vgmstream->channels = vi->channels;
vgmstream->sample_rate = vi->rate;
vgmstream->codec_data = data;
vgmstream->coding_type = coding_OGG_VORBIS;
vgmstream->layout_type = layout_none;
vgmstream->meta_type = ovmi->meta_type;
vgmstream->sample_rate = sample_rate;
vgmstream->stream_size = stream_size;
if (ovmi->total_subsongs) /* not setting it has some effect when showing stream names */
@ -665,11 +551,11 @@ VGMSTREAM * init_vgmstream_ogg_vorbis_callbacks(STREAMFILE *streamFile, ov_callb
if (name[0] != '\0')
strcpy(vgmstream->stream_name, name);
vgmstream->num_samples = ov_pcm_total(ovf,-1); /* let libvorbisfile find total samples */
vgmstream->num_samples = num_samples;
if (loop_flag) {
vgmstream->loop_start_sample = loop_start;
if (loop_length_found)
vgmstream->loop_end_sample = loop_start+loop_length;
vgmstream->loop_end_sample = loop_start + loop_length;
else if (loop_end_found)
vgmstream->loop_end_sample = loop_end;
else
@ -679,10 +565,6 @@ VGMSTREAM * init_vgmstream_ogg_vorbis_callbacks(STREAMFILE *streamFile, ov_callb
vgmstream->loop_end_sample = vgmstream->num_samples;
}
vgmstream->coding_type = coding_OGG_VORBIS;
vgmstream->layout_type = layout_none;
vgmstream->meta_type = ovmi->meta_type;
if (vgmstream->channels <= 8) {
vgmstream->channel_layout = xiph_mappings[vgmstream->channels];
}
@ -690,18 +572,7 @@ VGMSTREAM * init_vgmstream_ogg_vorbis_callbacks(STREAMFILE *streamFile, ov_callb
return vgmstream;
fail:
/* clean up anything we may have opened */
if (data) {
if (ovf)
ov_clear(&data->ogg_vorbis_file);//same as ovf
if (data->ov_streamfile.streamfile)
close_streamfile(data->ov_streamfile.streamfile);
free(data);
}
if (vgmstream) {
vgmstream->codec_data = NULL;
close_vgmstream(vgmstream);
}
close_vgmstream(vgmstream);
return NULL;
}

View File

@ -1,15 +1,12 @@
#include <string.h>
#include "meta.h"
#include "../coding/coding.h"
#include "../layout/layout.h"
#include "../util.h"
#include <string.h>
#include "riff_ogg_streamfile.h"
/* RIFF - Resource Interchange File Format, standard container used in many games */
#ifdef VGM_USE_VORBIS
static VGMSTREAM *parse_riff_ogg(STREAMFILE * streamFile, off_t start_offset, size_t data_size);
#endif
/* return milliseconds */
static long parse_adtl_marker(unsigned char * marker) {
@ -160,6 +157,8 @@ static int read_fmt(int big_endian, STREAMFILE * streamFile, off_t current_chunk
case 0x02: /* MSADPCM */
if (fmt->bps == 4) {
fmt->coding_type = coding_MSADPCM;
if (!msadpcm_check_coefs(streamFile, fmt->offset + 0x08 + 0x14))
goto fail;
}
else if (fmt->bps == 16 && fmt->block_size == 0x02 * fmt->channel_count && fmt->size == 0x14) {
fmt->coding_type = coding_IMA; /* MX vs ATV Unleashed (PC) codec hijack */
@ -286,6 +285,7 @@ fail:
}
static int is_ue4_msadpcm(VGMSTREAM* vgmstream, STREAMFILE* streamFile, riff_fmt_chunk* fmt, int fact_sample_count, off_t start_offset);
static size_t get_ue4_msadpcm_interleave(STREAMFILE *sf, riff_fmt_chunk *fmt, off_t start, size_t size);
VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) {
@ -448,7 +448,7 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) {
break;
case 0x66616374: /* "fact" */
if (chunk_size == 0x04) { /* standard, usually found with ADPCM */
if (chunk_size == 0x04) { /* standard (usually for ADPCM, MS recommends to set for non-PCM codecs) */
fact_sample_count = read_32bitLE(current_chunk+0x08, streamFile);
}
else if (chunk_size == 0x10 && read_32bitBE(current_chunk+0x08+0x04, streamFile) == 0x4C794E20) { /* "LyN " */
@ -537,13 +537,6 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) {
goto fail;
}
#ifdef VGM_USE_VORBIS
/* special case using init_vgmstream_ogg_vorbis */
if (fmt.coding_type == coding_OGG_VORBIS) {
return parse_riff_ogg(streamFile, start_offset, data_size);
}
#endif
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(fmt.channel_count,loop_flag);
@ -555,7 +548,6 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) {
/* coding, layout, interleave */
vgmstream->coding_type = fmt.coding_type;
switch (fmt.coding_type) {
case coding_MSADPCM:
case coding_MS_IMA:
case coding_AICA:
case coding_XBOX_IMA:
@ -568,10 +560,19 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) {
#endif
#ifdef VGM_USE_ATRAC9
case coding_ATRAC9:
#endif
#ifdef VGM_USE_VORBIS
case coding_OGG_VORBIS:
#endif
vgmstream->layout_type = layout_none;
vgmstream->interleave_block_size = fmt.block_size;
break;
case coding_MSADPCM:
vgmstream->layout_type = layout_none;
vgmstream->frame_size = fmt.block_size;
break;
default:
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = fmt.interleave;
@ -694,10 +695,35 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) {
break;
}
#endif
#ifdef VGM_USE_VORBIS
case coding_OGG_VORBIS: {
/* special handling of Liar-soft's buggy RIFF+Ogg made with Soundforge [Shikkoku no Sharnoth (PC)] */
STREAMFILE *temp_sf = setup_riff_ogg_streamfile(streamFile, start_offset, data_size);
if (!temp_sf) goto fail;
vgmstream->codec_data = init_ogg_vorbis(temp_sf, 0x00, get_streamfile_size(temp_sf), NULL);
if (!vgmstream->codec_data) goto fail;
/* Soundforge includes fact_samples and should be equal to Ogg samples */
vgmstream->num_samples = fact_sample_count;
break;
}
#endif
default:
goto fail;
}
/* UE4 uses interleaved mono MSADPCM, try to autodetect without breaking normal MSADPCM */
if (fmt.coding_type == coding_MSADPCM && is_ue4_msadpcm(vgmstream, streamFile, &fmt, fact_sample_count, start_offset)) {
vgmstream->coding_type = coding_MSADPCM_int;
vgmstream->frame_size = fmt.block_size;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = get_ue4_msadpcm_interleave(streamFile, &fmt, start_offset, data_size);
if (fmt.size == 0x36)
vgmstream->num_samples = read_s32le(fmt.offset+0x08+0x32, streamFile);
}
/* Dynasty Warriors 5 (Xbox) 6ch interleaves stereo frames, probably not official */
if (vgmstream->coding_type == coding_XBOX_IMA && vgmstream->channels > 2) {
vgmstream->layout_type = layout_interleave;
@ -742,23 +768,9 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) {
vgmstream->meta_type = meta_RIFF_WAVE_MWV;
}
if ( !vgmstream_open_stream(vgmstream,streamFile,start_offset) )
if (!vgmstream_open_stream(vgmstream, streamFile, start_offset))
goto fail;
/* UE4 uses half-interleave mono MSADPCM, try to autodetect without breaking normal MSADPCM */
if (fmt.coding_type == coding_MSADPCM && is_ue4_msadpcm(vgmstream, streamFile, &fmt, fact_sample_count, start_offset)) {
int ch;
size_t half_interleave = data_size / vgmstream->channels;
vgmstream->coding_type = coding_MSADPCM_int;
/* only works with half-interleave as frame_size and interleave are merged ATM */
for (ch = 0; ch < vgmstream->channels; ch++) {
vgmstream->ch[ch].channel_start_offset =
vgmstream->ch[ch].offset = start_offset + half_interleave*ch;
}
}
return vgmstream;
fail:
@ -767,10 +779,10 @@ fail:
}
/* UE4 MSADPCM is quite normal but has a few minor quirks we can use to detect it */
static int is_ue4_msadpcm(VGMSTREAM* vgmstream, STREAMFILE* streamFile, riff_fmt_chunk* fmt, int fact_sample_count, off_t start_offset) {
static int is_ue4_msadpcm(VGMSTREAM* vgmstream, STREAMFILE* streamFile, riff_fmt_chunk* fmt, int fact_sample_count, off_t start) {
/* stereo only */
if (fmt->channel_count != 2)
/* multichannel ok */
if (fmt->channel_count < 2)
goto fail;
/* UE4 class is "ADPCM", assume it's the extension too */
@ -785,13 +797,13 @@ static int is_ue4_msadpcm(VGMSTREAM* vgmstream, STREAMFILE* streamFile, riff_fmt
if (fmt->block_size != 0x200)
goto fail;
/* later UE4 versions use 0x36 (at 0x32 may be fact_samples?) */
/* later UE4 versions use 0x36 */
if (fmt->size != 0x32 && fmt->size != 0x36)
goto fail;
/* size 0x32 in older UE4 matches standard MSADPCM, so add extra detection */
if (fmt->size == 0x32) {
off_t offset = start_offset;
off_t offset = start;
off_t max_offset = 5 * fmt->block_size; /* try N blocks */
if (max_offset > get_streamfile_size(streamFile))
max_offset = get_streamfile_size(streamFile);
@ -810,6 +822,64 @@ fail:
return 0;
}
/* for maximum annoyance later UE4 versions (~v4.2x?) interleave single frames instead of
* half interleave, but don't have flags to detect so we need some heuristics */
static size_t get_ue4_msadpcm_interleave(STREAMFILE *sf, riff_fmt_chunk *fmt, off_t start, size_t size) {
size_t v1_interleave = size / fmt->channel_count;
size_t v2_interleave = fmt->block_size;
uint8_t nibbles1[0x08] = {0};
uint8_t nibbles2[0x08] = {0};
/* old versions */
if (fmt->size == 0x32)
return v1_interleave;
/* 6ch only observed in later versions [Fortnite (PC)], not padded */
if (fmt->channel_count > 2)
return v2_interleave;
read_streamfile(nibbles1, start + size - 0x08, sizeof(nibbles2), sf);
read_streamfile(nibbles2, start + v1_interleave - 0x08, sizeof(nibbles2), sf);
/* last frame is almost always padded, so should at half interleave */
if (get_u64be(nibbles1) == 0 && get_u64be(nibbles2) == 0)
return v1_interleave;
/* last frame is silent-ish, so should at half interleave (TSA's SML_DarknessLoop_01, TSA_CAD_YAKATA)
* this doesn't work too well b/c num_samples at 0x36 uses all data, may need adjustment */
{
int i;
int empty_nibbles1 = 1, empty_nibbles2 = 1;
for (i = 0; i < sizeof(nibbles1); i++) {
uint8_t n1 = ((nibbles1[i] >> 0) & 0x0f);
uint8_t n2 = ((nibbles1[i] >> 4) & 0x0f);
if ((n1 != 0x0 && n1 != 0xf && n1 != 0x1) || (n2 != 0x0 && n2 != 0xf && n2 != 0x1)) {
empty_nibbles1 = 0;
break;
}
}
for (i = 0; i < sizeof(nibbles2); i++) {
uint8_t n1 = ((nibbles2[i] >> 0) & 0x0f);
uint8_t n2 = ((nibbles2[i] >> 4) & 0x0f);
if ((n1 != 0x0 && n1 != 0xf && n1 != 0x1) || (n2 != 0x0 && n2 != 0xf && n2 != 0x1)) {
empty_nibbles2 = 0;
break;
}
}
if (empty_nibbles1 && empty_nibbles2)
return v1_interleave;
}
/* other tests? */
return v2_interleave; /* favor newer games */
}
VGMSTREAM * init_vgmstream_rifx(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
riff_fmt_chunk fmt = {0};
@ -940,95 +1010,3 @@ fail:
close_vgmstream(vgmstream);
return NULL;
}
#ifdef VGM_USE_VORBIS
typedef struct {
off_t patch_offset;
} riff_ogg_io_data;
static size_t riff_ogg_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length, riff_ogg_io_data* data) {
size_t bytes_read = streamfile->read(streamfile, dest, offset, length);
/* has garbage init Oggs pages, patch bad flag */
if (data->patch_offset && data->patch_offset >= offset && data->patch_offset < offset + bytes_read) {
VGM_ASSERT(dest[data->patch_offset - offset] != 0x02, "RIFF Ogg: bad patch offset\n");
dest[data->patch_offset - offset] = 0x00;
}
return bytes_read;
}
/* special handling of Liar-soft's buggy RIFF+Ogg made with Soundforge [Shikkoku no Sharnoth (PC)] */
static VGMSTREAM *parse_riff_ogg(STREAMFILE * streamFile, off_t start_offset, size_t data_size) {
off_t patch_offset = 0;
size_t real_size = data_size;
/* initial page flag is repeated and causes glitches in decoders, find bad offset */
{
off_t offset = start_offset + 0x04+0x02;
off_t offset_limit = start_offset + data_size; /* usually in the first 0x3000 but can be +0x100000 */
while (offset < offset_limit) {
if (read_32bitBE(offset+0x00, streamFile) == 0x4f676753 && /* "OggS" */
read_16bitBE(offset+0x04, streamFile) == 0x0002) { /* start page flag */
//todo callback should patch on-the-fly by analyzing all "OggS", but is problematic due to arbitrary offsets
if (patch_offset) {
VGM_LOG("RIFF Ogg: found multiple repeated start pages\n");
return NULL;
}
patch_offset = offset /*- start_offset*/ + 0x04+0x01;
}
offset++; //todo could be optimized to do OggS page sizes
}
}
/* last pages don't have the proper flag and confuse decoders, find actual end */
{
size_t max_size = data_size;
off_t offset_limit = start_offset + data_size - 0x1000; /* not worth checking more, let decoder try */
off_t offset = start_offset + data_size - 0x1a;
while (offset > offset_limit) {
if (read_32bitBE(offset+0x00, streamFile) == 0x4f676753) { /* "OggS" */
if (read_16bitBE(offset+0x04, streamFile) == 0x0004) { /* last page flag */
real_size = max_size;
break;
} else {
max_size = offset - start_offset; /* ignore bad pages */
}
}
offset--;
}
}
/* Soundforge includes fact_samples but should be equal to Ogg samples */
/* actual Ogg init with custom callback to patch weirdness */
{
VGMSTREAM *vgmstream = NULL;
STREAMFILE *custom_streamFile = NULL;
ogg_vorbis_meta_info_t ovmi = {0};
riff_ogg_io_data io_data = {0};
size_t io_data_size = sizeof(riff_ogg_io_data);
ovmi.meta_type = meta_RIFF_WAVE;
ovmi.stream_size = real_size;
//inf.loop_flag = 0; /* not observed */
io_data.patch_offset = patch_offset;
custom_streamFile = open_io_streamfile(open_wrap_streamfile(streamFile), &io_data,io_data_size, riff_ogg_io_read,NULL);
if (!custom_streamFile) return NULL;
vgmstream = init_vgmstream_ogg_vorbis_callbacks(custom_streamFile, NULL, start_offset, &ovmi);
close_streamfile(custom_streamFile);
return vgmstream;
}
}
#endif

View File

@ -0,0 +1,130 @@
#ifndef _RIFF_OGG_STREAMFILE_H_
#define _RIFF_OGG_STREAMFILE_H_
#include "../streamfile.h"
#ifdef VGM_USE_VORBIS
typedef struct {
off_t patch_offset;
} riff_ogg_io_data;
static size_t riff_ogg_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length, riff_ogg_io_data* data) {
size_t bytes_read = streamfile->read(streamfile, dest, offset, length);
/* has garbage init Oggs pages, patch bad flag */
if (data->patch_offset && data->patch_offset >= offset && data->patch_offset < offset + bytes_read) {
VGM_ASSERT(dest[data->patch_offset - offset] != 0x02, "RIFF Ogg: bad patch offset at %lx\n", data->patch_offset);
dest[data->patch_offset - offset] = 0x00;
}
return bytes_read;
}
static size_t ogg_get_page(uint8_t *buf, size_t bufsize, off_t offset, STREAMFILE *sf) {
size_t segments, bytes, page_size;
int i;
if (0x1b > bufsize) goto fail;
bytes = read_streamfile(buf, offset, 0x1b, sf);
if (bytes != 0x1b) goto fail;
segments = get_u8(buf + 0x1a);
if (0x1b + segments > bufsize) goto fail;
bytes = read_streamfile(buf + 0x1b, offset + 0x1b, segments, sf);
if (bytes != segments) goto fail;
page_size = 0x1b + segments;
for (i = 0; i < segments; i++) {
page_size += get_u8(buf + 0x1b + i);
}
return page_size;
fail:
return 0;
}
/* patches Oggs with weirdness */
static STREAMFILE* setup_riff_ogg_streamfile(STREAMFILE *sf, off_t start, size_t size) {
off_t patch_offset = 0;
size_t real_size = size;
uint8_t buf[0x1000];
/* initial page flag is repeated and causes glitches in decoders, find bad offset */
//todo callback could patch on-the-fly by analyzing all "OggS", but is problematic due to arbitrary offsets
{
off_t offset = start;
size_t page_size;
off_t offset_limit = start + size; /* usually in the first 0x3000 but can be +0x100000 */
//todo this doesn't seem to help much
STREAMFILE *temp_sf = reopen_streamfile(sf, 0x100); /* use small-ish sf to avoid reading the whole thing */
/* first page is ok */
page_size = ogg_get_page(buf, sizeof(buf), offset, temp_sf);
offset += page_size;
while (offset < offset_limit) {
page_size = ogg_get_page(buf, sizeof(buf), offset, temp_sf);
if (page_size == 0) break;
if (get_u32be(buf + 0x00) != 0x4f676753) /* "OggS" */
break;
if (get_u16be(buf + 0x04) == 0x0002) { /* start page flag */
//;VGM_ASSERT(patch_offset > 0, "RIFF Ogg: found multiple repeated start pages\n");
patch_offset = (offset - start) + 0x04 + 0x01; /* clamp'ed */
break;
}
offset += page_size;
}
close_streamfile(temp_sf);
if (patch_offset == 0)
return NULL;
}
/* has a bunch of padding(?) pages at the end with no data nor flag that confuse decoders, find actual end */
{
size_t chunk_size = sizeof(buf); /* not worth testing more */
size_t max_size = size;
size_t pos;
off_t read_offset = start + size - chunk_size;
pos = read_streamfile(buf, read_offset, chunk_size, sf);
if (read_offset < 0 || pos <= 0x1a) return NULL;
pos -= 0x1a; /* at least one OggS page */
while (pos > 0) {
if (get_u32be(buf + pos + 0x00) == 0x4f676753) { /* "OggS" */
if (get_u16be(buf + pos + 0x04) == 0x0004) { /* last page flag is ok */
real_size = max_size;
break;
}
else { /* last page flag is wrong */
max_size = size - (chunk_size - pos); /* update size up to this page */
}
}
pos--;
}
}
/* actual custom streamfile init */
{
STREAMFILE *new_sf = NULL;
riff_ogg_io_data io_data = {0};
io_data.patch_offset = patch_offset;
new_sf = open_wrap_streamfile(sf);
new_sf = open_clamp_streamfile_f(new_sf, start, real_size);
new_sf = open_io_streamfile_f(new_sf, &io_data, sizeof(riff_ogg_io_data), riff_ogg_io_read, NULL);
return new_sf;
}
}
#endif /* VGM_USE_VORBIS */
#endif /* _RIFF_OGG_STREAMFILE_H_ */

View File

@ -1,17 +1,16 @@
#include "meta.h"
#include "../util.h"
#include "../coding/coding.h"
/* SD9 (found in beatmania IIDX Arcade games) */
/* SD9 - from Konami arcade games [beatmania IIDX series (AC), BeatStream (AC)] */
VGMSTREAM * init_vgmstream_sd9(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
off_t start_offset;
int loop_flag, channel_count;
/* check extension */
/* checks */
if (!check_extensions(streamFile, "sd9"))
goto fail;
/* check header */
if (read_32bitBE(0x0, streamFile) != 0x53443900) /* SD9 */
goto fail;
if (read_32bitBE(0x20, streamFile) != 0x52494646) /* RIFF */
@ -30,27 +29,26 @@ VGMSTREAM * init_vgmstream_sd9(STREAMFILE *streamFile) {
//loop_flag = (read_16bitLE(0x0e,streamFile)==0x1);
loop_flag = read_32bitLE(0x18, streamFile); // use loop end
channel_count = read_16bitLE(0x36, streamFile);
start_offset = 0x7a;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count, loop_flag);
if (!vgmstream) goto fail;
/* fill in the vital statistics */
start_offset = 0x7a;
vgmstream->channels = channel_count;
vgmstream->sample_rate = read_32bitLE(0x38, streamFile);
vgmstream->coding_type = coding_MSADPCM;
vgmstream->num_samples = read_32bitLE(0x6e, streamFile);
if (loop_flag) {
vgmstream->loop_start_sample = read_32bitLE(0x14, streamFile) / 2 / channel_count;
vgmstream->loop_end_sample = read_32bitLE(0x18, streamFile) / 2 / channel_count;
}
vgmstream->coding_type = coding_MSADPCM;
vgmstream->layout_type = layout_none;
vgmstream->interleave_block_size = read_16bitLE(0x40, streamFile);
vgmstream->frame_size = read_16bitLE(0x40, streamFile);
vgmstream->meta_type = meta_SD9;
if (!msadpcm_check_coefs(streamFile, 0x48))
goto fail;
/* open the file for reading */
if (!vgmstream_open_stream(vgmstream, streamFile, start_offset))
goto fail;
return vgmstream;
@ -58,4 +56,4 @@ VGMSTREAM * init_vgmstream_sd9(STREAMFILE *streamFile) {
fail:
close_vgmstream(vgmstream);
return NULL;
}
}

View File

@ -112,6 +112,14 @@ VGMSTREAM * init_vgmstream_sgxd(STREAMFILE *streamFile) {
read_string(vgmstream->stream_name,STREAM_NAME_SIZE, name_offset,streamHeader);
switch (type) {
#ifdef VGM_USE_FFMPEG
case 0x02: /* Ogg Vorbis [Ni no Kuni: Wrath of the White Witch Remastered (PC)] (codec hijack?) */
vgmstream->codec_data = init_ogg_vorbis(streamFile, start_offset, stream_size, NULL);
if (!vgmstream->codec_data) goto fail;
vgmstream->coding_type = coding_OGG_VORBIS;
vgmstream->layout_type = layout_none;
break;
#endif
case 0x03: /* PS-ADPCM [Genji (PS3), Ape Escape Move (PS3)]*/
vgmstream->coding_type = coding_PSX;
vgmstream->layout_type = layout_interleave;

View File

@ -53,39 +53,39 @@ VGMSTREAM * init_vgmstream_sli_ogg(STREAMFILE *streamFile) {
/* find loop text */
{
char linebuffer[PATH_LIMIT];
char line[PATH_LIMIT];
size_t bytes_read;
off_t sli_offset;
int done;
int line_ok;
sli_offset = 0;
while ((loop_start == -1 || loop_length == -1) && sli_offset < get_streamfile_size(streamFile)) {
char *endptr, *foundptr;
bytes_read = get_streamfile_text_line(sizeof(linebuffer),linebuffer,sli_offset,streamFile,&done);
if (!done) goto fail;
bytes_read = read_line(line, sizeof(line), sli_offset, streamFile, &line_ok);
if (!line_ok) goto fail;
if (memcmp("LoopStart=",linebuffer,10)==0 && linebuffer[10] != '\0') {
loop_start = strtol(linebuffer+10,&endptr,10);
if (memcmp("LoopStart=",line,10)==0 && line[10] != '\0') {
loop_start = strtol(line+10,&endptr,10);
if (*endptr != '\0') {
loop_start = -1; /* if it didn't parse cleanly */
}
}
else if (memcmp("LoopLength=",linebuffer,11)==0 && linebuffer[11] != '\0') {
loop_length = strtol(linebuffer+11,&endptr,10);
else if (memcmp("LoopLength=",line,11)==0 && line[11] != '\0') {
loop_length = strtol(line+11,&endptr,10);
if (*endptr != '\0') {
loop_length = -1; /* if it didn't parse cleanly */
}
}
/* a completely different format (2.0?), also with .sli extension and can be handled similarly */
if ((foundptr = strstr(linebuffer,"To=")) != NULL && isdigit(foundptr[3])) {
if ((foundptr = strstr(line,"To=")) != NULL && isdigit(foundptr[3])) {
loop_to = strtol(foundptr+3,&endptr,10);
if (*endptr != ';') {
loop_to = -1;
}
}
if ((foundptr = strstr(linebuffer,"From=")) != NULL && isdigit(foundptr[5])) {
if ((foundptr = strstr(line,"From=")) != NULL && isdigit(foundptr[5])) {
loop_from = strtol(foundptr+5,&endptr,10);
if (*endptr != ';') {
loop_from = -1;

View File

@ -74,11 +74,12 @@ VGMSTREAM * init_vgmstream_smp(STREAMFILE *streamFile) {
case 0x04:
if (bps != 4) goto fail;
/* 0x34: standard MSADPCM coef table */
if (!msadpcm_check_coefs(streamFile, 0x36))
goto fail;
vgmstream->coding_type = coding_MSADPCM;
vgmstream->layout_type = layout_none;
vgmstream->interleave_block_size = 0x86*channel_count;
vgmstream->frame_size = 0x86*channel_count;
break;
case 0x06:

View File

@ -1,109 +1,110 @@
#include "meta.h"
#include "../util.h"
#include "../coding/coding.h"
/* SPT+SPT
2008-11-27 - manakoAT : First try for splitted files...
*/
/* SPD+SPT - Nintendo bank (bgm or sfx) format [Bloodrayne (GC), 4x4 EVO 2 (GC), Table Tennis (Wii)] */
VGMSTREAM * init_vgmstream_spt_spd(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
STREAMFILE * streamFileSPT = NULL;
char filename[PATH_LIMIT];
char filenameSPT[PATH_LIMIT];
int channel_count;
int loop_flag;
int i;
VGMSTREAM *vgmstream = NULL;
STREAMFILE *sf_h = NULL;
int channel_count, loop_flag, sample_rate;
off_t header_offset, extra_offset, start_offset;
int32_t loop_start, loop_end, stream_start, stream_end;
size_t stream_size;
uint32_t flags;
int total_subsongs, target_subsong = streamFile->stream_index;
/* check extension, case insensitive */
streamFile->get_name(streamFile,filename,sizeof(filename));
if (strcasecmp("spd",filename_extension(filename))) goto fail;
strcpy(filenameSPT,filename);
strcpy(filenameSPT+strlen(filenameSPT)-3,"spt");
streamFileSPT = streamFile->open(streamFile,filenameSPT,STREAMFILE_DEFAULT_BUFFER_SIZE);
if (!streamFileSPT)
/* checks */
if (!check_extensions(streamFile, "spd"))
goto fail;
if (read_32bitBE(0x0,streamFileSPT) != 0x1) // make sure that it's not a container
sf_h = open_streamfile_by_ext(streamFile, "spt");
if (!sf_h) goto fail;
/* ignore alt .spt+spd [Spyro: Enter the Dragonfly (GC)] */
if (read_u16be(0x00, sf_h) != 0) /* always 0xA20C? */
goto fail;
channel_count = 1;
loop_flag = (read_32bitBE(0x0C,streamFileSPT) == 0x2);
total_subsongs = read_s32be(0x00, sf_h);
if (target_subsong == 0) target_subsong = 1;
if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail;
header_offset = 0x04 + 0x1c * (target_subsong-1);
extra_offset = 0x04 + 0x1c * total_subsongs + 0x2e * (target_subsong-1);
flags = read_u32be(header_offset + 0x00, sf_h);
sample_rate = read_s32be(header_offset + 0x04, sf_h);
loop_start = read_s32be(header_offset + 0x08, sf_h);
loop_end = read_s32be(header_offset + 0x0c, sf_h);
stream_end = read_s32be(header_offset + 0x10, sf_h);
stream_start = read_s32be(header_offset + 0x14, sf_h);
/* 0x18: null */
channel_count = 1;
loop_flag = (flags & 1);
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail;
/* fill in the vital statistics */
vgmstream->channels = channel_count;
vgmstream->sample_rate = read_32bitBE(0x08,streamFileSPT);
switch ((read_32bitBE(0x4,streamFileSPT))) {
case 0:
case 1:
vgmstream->coding_type = coding_NGC_DSP;
vgmstream->num_samples=read_32bitBE(0x14,streamFileSPT)*14/16/channel_count;
if(loop_flag) {
vgmstream->loop_start_sample = 0;
vgmstream->loop_end_sample = read_32bitBE(0x14,streamFileSPT)*14/16/channel_count;
}
break;
case 2:
vgmstream->coding_type = coding_PCM16BE;
vgmstream->num_samples=read_32bitBE(0x14,streamFileSPT)/channel_count;
if(loop_flag) {
vgmstream->loop_start_sample = 0;
vgmstream->loop_end_sample = read_32bitBE(0x14,streamFileSPT)/channel_count;
}
break;
default:
goto fail;
}
if (channel_count == 1) {
vgmstream->layout_type = layout_none;
} else if (channel_count == 2) {
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size=(read_32bitBE(0x34,streamFileSPT)*channel_count)/2;
}
vgmstream->meta_type = meta_SPT_SPD;
vgmstream->allow_dual_stereo = 1;
vgmstream->sample_rate = sample_rate;
vgmstream->layout_type = layout_none;
/* open the file for reading */
{
for (i=0;i<channel_count;i++) {
/* Not sure, i'll put a fake value here for now */
vgmstream->ch[i].streamfile = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE);
vgmstream->ch[i].offset = 0;
if (!vgmstream->ch[i].streamfile) goto fail;
}
}
vgmstream->num_streams = total_subsongs;
if (vgmstream->coding_type == coding_NGC_DSP) {
int i;
for (i=0;i<16;i++) {
vgmstream->ch[0].adpcm_coef[i] = read_16bitBE(0x20+i*2,streamFileSPT);
}
if (vgmstream->channels == 2) {
for (i=0;i<16;i++) {
vgmstream->ch[1].adpcm_coef[i] = read_16bitBE(0x40+i*2,streamFileSPT);
switch(flags & (~1)) { /* bitflags */
case 0: /* common */
/* values in file nibbles? */
start_offset = (stream_start / 2 - 1);
stream_size = (stream_end / 2 + 1) - (stream_start / 2 - 1);
if (loop_flag) {
loop_start = (loop_start / 2 - 1) - start_offset;
loop_end = (loop_end / 2 + 1) - start_offset;
}
}
vgmstream->coding_type = coding_NGC_DSP;
vgmstream->num_samples = dsp_bytes_to_samples(stream_size, channel_count);
if (loop_flag) {
vgmstream->loop_start_sample = dsp_bytes_to_samples(loop_start, channel_count);
vgmstream->loop_end_sample = dsp_bytes_to_samples(loop_end, channel_count);
}
dsp_read_coefs_be(vgmstream, sf_h, extra_offset + 0x00, 0x00);
dsp_read_hist_be (vgmstream, sf_h, extra_offset + 0x24, 0x00);
break;
case 2: /* rare [Monster Jam: Maximum Destruction (GC)] */
/* values in samples? */
start_offset = (stream_start * 2);
stream_size = (stream_end * 2) - (stream_start * 2);
if (loop_flag) {
loop_start = (loop_start * 2) - start_offset;
loop_end = (loop_end * 2) - start_offset;
}
vgmstream->coding_type = coding_PCM16BE;
vgmstream->num_samples = pcm_bytes_to_samples(stream_size, channel_count, 16);
if (loop_flag) {
vgmstream->loop_start_sample = loop_start;
vgmstream->loop_end_sample = loop_end;
}
break;
case 4: /* supposedly PCM8 */
default:
goto fail;
}
vgmstream->stream_size = stream_size;
close_streamfile(streamFileSPT); streamFileSPT=NULL;
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
goto fail;
close_streamfile(sf_h);
return vgmstream;
/* clean up anything we may have opened */
fail:
if (streamFileSPT) close_streamfile(streamFileSPT);
if (vgmstream) close_vgmstream(vgmstream);
close_streamfile(sf_h);
close_vgmstream(vgmstream);
return NULL;
}

View File

@ -270,13 +270,15 @@ VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE *streamFile) {
case 0x0C: /* MS ADPCM [Final Fantasy XIV (PC) sfx] */
vgmstream->coding_type = coding_MSADPCM;
vgmstream->layout_type = layout_none;
vgmstream->interleave_block_size = read_16bit(extradata_offset+0x0c,streamFile);
/* in extradata_offset is a WAVEFORMATEX (including coefs and all) */
vgmstream->frame_size = read_16bit(extradata_offset + 0x0c, streamFile);
/* WAVEFORMATEX in extradata_offset */
if (!msadpcm_check_coefs(streamFile, extradata_offset + 0x14))
goto fail;
vgmstream->num_samples = msadpcm_bytes_to_samples(stream_size, vgmstream->interleave_block_size, vgmstream->channels);
vgmstream->num_samples = msadpcm_bytes_to_samples(stream_size, vgmstream->frame_size, vgmstream->channels);
if (loop_flag) {
vgmstream->loop_start_sample = msadpcm_bytes_to_samples(loop_start, vgmstream->interleave_block_size, vgmstream->channels);
vgmstream->loop_end_sample = msadpcm_bytes_to_samples(loop_end, vgmstream->interleave_block_size, vgmstream->channels);
vgmstream->loop_start_sample = msadpcm_bytes_to_samples(loop_start, vgmstream->frame_size, vgmstream->channels);
vgmstream->loop_end_sample = msadpcm_bytes_to_samples(loop_end, vgmstream->frame_size, vgmstream->channels);
}
break;
@ -416,23 +418,24 @@ fail:
#ifdef VGM_USE_VORBIS
static void scd_ogg_v2_decryption_callback(void *ptr, size_t size, size_t nmemb, void *datasource) {
size_t bytes_read = size*nmemb;
ogg_vorbis_streamfile * ov_streamfile = (ogg_vorbis_streamfile*)datasource;
uint8_t *ptr8 = ptr;
size_t bytes_read = size * nmemb;
ogg_vorbis_io *io = datasource;
/* no encryption, sometimes happens */
if (ov_streamfile->scd_xor == 0x00)
if (io->scd_xor == 0x00)
return;
/* header is XOR'd with a constant byte */
if (ov_streamfile->offset < ov_streamfile->scd_xor_length) {
if (io->offset < io->scd_xor_length) {
int i, num_crypt;
num_crypt = ov_streamfile->scd_xor_length - ov_streamfile->offset;
num_crypt = io->scd_xor_length - io->offset;
if (num_crypt > bytes_read)
num_crypt = bytes_read;
for (i = 0; i < num_crypt; i++) {
((uint8_t*)ptr)[i] ^= (uint8_t)ov_streamfile->scd_xor;
ptr8[i] ^= (uint8_t)io->scd_xor;
}
}
}
@ -457,25 +460,25 @@ static void scd_ogg_v3_decryption_callback(void *ptr, size_t size, size_t nmemb,
0xE2, 0xA2, 0x67, 0x32, 0x32, 0x12, 0x32, 0xB2, 0x32, 0x32, 0x32, 0x32, 0x75, 0xA3, 0x26, 0x7B, // E0-EF
0x83, 0x26, 0xF9, 0x83, 0x2E, 0xFF, 0xE3, 0x16, 0x7D, 0xC0, 0x1E, 0x63, 0x21, 0x07, 0xE3, 0x01, // F0-FF
};
size_t bytes_read = size*nmemb;
ogg_vorbis_streamfile *ov_streamfile = (ogg_vorbis_streamfile*)datasource;
uint8_t *ptr8 = ptr;
size_t bytes_read = size * nmemb;
ogg_vorbis_io *io = datasource;
/* file is XOR'd with a table (algorithm and table by Ioncannon) */
{ //if (ov_streamfile->offset < ov_streamfile->scd_xor_length)
{ //if (io->offset < io->scd_xor_length)
int i, num_crypt;
uint8_t byte1, byte2, xor_byte;
num_crypt = bytes_read;
byte1 = ov_streamfile->scd_xor & 0x7F;
byte2 = ov_streamfile->scd_xor & 0x3F;
byte1 = io->scd_xor & 0x7F;
byte2 = io->scd_xor & 0x3F;
for (i = 0; i < num_crypt; i++) {
xor_byte = scd_ogg_v3_lookuptable[(byte2 + ov_streamfile->offset + i) & 0xFF];
xor_byte = scd_ogg_v3_lookuptable[(byte2 + io->offset + i) & 0xFF];
xor_byte &= 0xFF;
xor_byte ^= ((uint8_t*)ptr)[i];
xor_byte ^= ptr8[i];
xor_byte ^= byte1;
((uint8_t*)ptr)[i] = (uint8_t)xor_byte;
ptr8[i] = xor_byte;
}
}
}

View File

@ -125,11 +125,11 @@ VGMSTREAM * init_vgmstream_sqex_sead(STREAMFILE * streamFile) {
/* 0x00 (2): null?, 0x02(2): entry size? */
vgmstream->coding_type = coding_MSADPCM;
vgmstream->layout_type = layout_none;
vgmstream->interleave_block_size = read_16bit(sead.extradata_offset+0x04,streamFile);
vgmstream->frame_size = read_16bit(sead.extradata_offset+0x04,streamFile);
/* much like AKBs, there are slightly different loop values here, probably more accurate
* (if no loop, loop_end doubles as num_samples) */
vgmstream->num_samples = msadpcm_bytes_to_samples(sead.stream_size, vgmstream->interleave_block_size, vgmstream->channels);
vgmstream->num_samples = msadpcm_bytes_to_samples(sead.stream_size, vgmstream->frame_size, vgmstream->channels);
vgmstream->loop_start_sample = read_32bit(sead.extradata_offset+0x08, streamFile); //loop_start
vgmstream->loop_end_sample = read_32bit(sead.extradata_offset+0x0c, streamFile); //loop_end
break;

View File

@ -337,13 +337,15 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) {
vgmstream->interleave_block_size = txth.interleave;
vgmstream->layout_type = layout_none;
break;
case coding_MSADPCM:
if (vgmstream->channels > 2) goto fail;
if (!txth.interleave) goto fail; /* creates garbage */
if (!txth.interleave) goto fail;
vgmstream->interleave_block_size = txth.interleave;
vgmstream->frame_size = txth.interleave;
vgmstream->layout_type = layout_none;
break;
case coding_XBOX_IMA:
if (txth.codec_mode == 1) { /* mono interleave */
coding = coding_XBOX_IMA_int;
@ -772,12 +774,12 @@ static int parse_txth(txth_header * txth) {
/* read lines */
while (txt_offset < file_size) {
char line[TXT_LINE_MAX] = {0};
char line[TXT_LINE_MAX];
char key[TXT_LINE_MAX] = {0}, val[TXT_LINE_MAX] = {0}; /* at least as big as a line to avoid overflows (I hope) */
int ok, bytes_read, line_done;
int ok, bytes_read, line_ok;
bytes_read = get_streamfile_text_line(TXT_LINE_MAX,line, txt_offset,txth->streamText, &line_done);
if (!line_done) goto fail;
bytes_read = read_line(line, sizeof(line), txt_offset, txth->streamText, &line_ok);
if (!line_ok) goto fail;
//;VGM_LOG("TXTH: line=%s\n",line);
txt_offset += bytes_read;
@ -1441,12 +1443,12 @@ static int parse_name_table(txth_header * txth, char * name_list) {
/* read lines and find target filename, format is (filename): value1, ... valueN */
while (txt_offset < file_size) {
char line[TXT_LINE_MAX] = {0};
char line[TXT_LINE_MAX];
char key[TXT_LINE_MAX] = {0}, val[TXT_LINE_MAX] = {0};
int ok, bytes_read, line_done;
int ok, bytes_read, line_ok;
bytes_read = get_streamfile_text_line(TXT_LINE_MAX,line, txt_offset,nameFile, &line_done);
if (!line_done) goto fail;
bytes_read = read_line(line, sizeof(line), txt_offset, nameFile, &line_ok);
if (!line_ok) goto fail;
//;VGM_LOG("TXTH: line=%s\n",line);
txt_offset += bytes_read;

View File

@ -1434,13 +1434,13 @@ static txtp_header* parse_txtp(STREAMFILE* streamFile) {
/* read and parse lines */
while (txt_offset < file_size) {
char line[TXTP_LINE_MAX] = {0};
char line[TXTP_LINE_MAX];
char key[TXTP_LINE_MAX] = {0}, val[TXTP_LINE_MAX] = {0}; /* at least as big as a line to avoid overflows (I hope) */
char filename[TXTP_LINE_MAX] = {0};
int ok, bytes_read, line_done;
int ok, bytes_read, line_ok;
bytes_read = get_streamfile_text_line(TXTP_LINE_MAX,line, txt_offset,streamFile, &line_done);
if (!line_done) goto fail;
bytes_read = read_line(line, sizeof(line), txt_offset, streamFile, &line_ok);
if (!line_ok) goto fail;
txt_offset += bytes_read;

View File

@ -379,22 +379,17 @@ static VGMSTREAM * init_vgmstream_ubi_bao_base(ubi_bao_header * bao, STREAMFILE
vgmstream->layout_type = layout_none;
break;
}
#endif
#ifdef VGM_USE_VORBIS
case FMT_OGG: {
ffmpeg_codec_data *ffmpeg_data;
ffmpeg_data = init_ffmpeg_offset(streamData, start_offset, bao->stream_size);
if (!ffmpeg_data) goto fail;
vgmstream->codec_data = ffmpeg_data;
vgmstream->coding_type = coding_FFmpeg;
vgmstream->codec_data = init_ogg_vorbis(streamData, start_offset, bao->stream_size, NULL);
if (!vgmstream->codec_data) goto fail;
vgmstream->coding_type = coding_OGG_VORBIS;
vgmstream->layout_type = layout_none;
vgmstream->num_samples = bao->num_samples; /* ffmpeg_data->totalSamples */
VGM_ASSERT(bao->num_samples != ffmpeg_data->totalSamples,
"UBI BAO: header samples %i vs ffmpeg %i differ\n", bao->num_samples, (uint32_t)ffmpeg_data->totalSamples);
vgmstream->num_samples = bao->num_samples; /* same as Ogg samples */
break;
}
#endif
default:
goto fail;

View File

@ -107,10 +107,10 @@ static int parse_name_bnh(ubi_hx_header * hx, STREAMFILE *sf, uint32_t cuuid1, u
/* each .bnh line has a cuuid, a bunch of repeated fields and name (sometimes name is filename or "bad name") */
while (txt_offset < get_streamfile_size(sf)) {
int line_read, bytes_read;
int line_ok, bytes_read;
bytes_read = get_streamfile_text_line(TXT_LINE_MAX,line, txt_offset,sf_t, &line_read);
if (!line_read) break;
bytes_read = read_line(line, sizeof(line), txt_offset, sf_t, &line_ok);
if (!line_ok) break;
txt_offset += bytes_read;
if (strncmp(line,cuuid,31) != 0)

View File

@ -209,9 +209,9 @@ VGMSTREAM * init_vgmstream_ubi_jade(STREAMFILE *streamFile) {
vgmstream->coding_type = coding_MSADPCM;
vgmstream->layout_type = layout_none;
vgmstream->interleave_block_size = 0x24*channel_count;
vgmstream->frame_size = block_size;
vgmstream->num_samples = msadpcm_bytes_to_samples(data_size, vgmstream->interleave_block_size, channel_count);
vgmstream->num_samples = msadpcm_bytes_to_samples(data_size, vgmstream->frame_size, channel_count);
if (!is_jade_v2) {
vgmstream->loop_start_sample = 0;
vgmstream->loop_end_sample = vgmstream->num_samples;
@ -221,7 +221,7 @@ VGMSTREAM * init_vgmstream_ubi_jade(STREAMFILE *streamFile) {
case 0x0001: { /* PS3 */
VGMSTREAM *temp_vgmstream = NULL;
STREAMFILE *temp_streamFile = NULL;
STREAMFILE *temp_sf = NULL;
if (fmt_size != 0x10) goto fail;
if (block_size != 0x02*channel_count) goto fail;
@ -230,11 +230,11 @@ VGMSTREAM * init_vgmstream_ubi_jade(STREAMFILE *streamFile) {
if (read_32bitBE(start_offset, streamFile) != 0x4D534643) /* "MSF\43" */
goto fail;
temp_streamFile = setup_subfile_streamfile(streamFile, start_offset, data_size, "msf");
if (!temp_streamFile) goto fail;
temp_sf = setup_subfile_streamfile(streamFile, start_offset, data_size, "msf");
if (!temp_sf) goto fail;
temp_vgmstream = init_vgmstream_msf(temp_streamFile);
close_streamfile(temp_streamFile);
temp_vgmstream = init_vgmstream_msf(temp_sf);
close_streamfile(temp_sf);
if (!temp_vgmstream) goto fail;
temp_vgmstream->meta_type = vgmstream->meta_type;

View File

@ -94,9 +94,12 @@ VGMSTREAM * init_vgmstream_ubi_raki(STREAMFILE *streamFile) {
/* chunks: "data" */
vgmstream->coding_type = coding_MSADPCM;
vgmstream->layout_type = layout_none;
vgmstream->interleave_block_size = block_align;
vgmstream->frame_size = block_align;
vgmstream->num_samples = msadpcm_bytes_to_samples(data_size, vgmstream->interleave_block_size, channel_count);
vgmstream->num_samples = msadpcm_bytes_to_samples(data_size, vgmstream->frame_size, channel_count);
if (!msadpcm_check_coefs(streamFile, fmt_offset + 0x14))
goto fail;
break;
case 0x5769692061647063: /* "Wii adpc" */

View File

@ -661,14 +661,12 @@ static VGMSTREAM * init_vgmstream_ubi_sb_base(ubi_sb_header *sb, STREAMFILE *str
xma_fix_raw_samples_ch(vgmstream, streamData, start_offset, sb->stream_size, sb->channels, 0, 0);
break;
}
#endif
#ifdef VGM_USE_VORBIS
case FMT_OGG: {
ffmpeg_codec_data *ffmpeg_data;
ffmpeg_data = init_ffmpeg_offset(streamData, start_offset, sb->stream_size);
if ( !ffmpeg_data ) goto fail;
vgmstream->codec_data = ffmpeg_data;
vgmstream->coding_type = coding_FFmpeg;
vgmstream->codec_data = init_ogg_vorbis(streamData, start_offset, sb->stream_size, NULL);
if (!vgmstream->codec_data) goto fail;
vgmstream->coding_type = coding_OGG_VORBIS;
vgmstream->layout_type = layout_none;
break;
}

View File

@ -9,23 +9,23 @@ VGMSTREAM * init_vgmstream_vxn(STREAMFILE *streamFile) {
size_t stream_size;
int total_subsongs, target_subsong = streamFile->stream_index;
/* check extensions */
/* checks */
if (!check_extensions(streamFile,"vxn"))
goto fail;
/* check header/version chunk (RIFF-like format with many custom chunks) */
if (read_32bitBE(0x00,streamFile) != 0x566F784E) /* "VoxN" */
goto fail;
if (read_32bitLE(0x10,streamFile) != get_streamfile_size(streamFile) )
goto fail;
/* header is RIFF-like with many custom chunks */
if (!find_chunk_le(streamFile, 0x41666D74,first_offset,0, &chunk_offset,NULL)) /* "Afmt" */
goto fail;
codec = (uint16_t)read_16bitLE(chunk_offset+0x00, streamFile);
channel_count = (uint16_t)read_16bitLE(chunk_offset+0x02, streamFile);
sample_rate = read_32bitLE(chunk_offset+0x04, streamFile);
block_align = (uint16_t)read_16bitLE(chunk_offset+0x08, streamFile);
bits = (uint16_t)read_16bitLE(chunk_offset+0x0a, streamFile);
bits = read_16bitLE(chunk_offset+0x0a, streamFile);
/* files are divided into segment subsongs, often a leadout and loop in that order
* (the "Plst" and "Rule" chunks may have order info) */
@ -67,8 +67,13 @@ VGMSTREAM * init_vgmstream_vxn(STREAMFILE *streamFile) {
if (bits != 4) goto fail;
vgmstream->coding_type = coding_MSADPCM;
vgmstream->interleave_block_size = block_align;
vgmstream->frame_size = block_align;
vgmstream->layout_type = layout_none;
if (find_chunk_le(streamFile, 0x4D736165,first_offset,0, &chunk_offset,NULL)) { /* "Msae" */
if (!msadpcm_check_coefs(streamFile, chunk_offset + 0x02))
goto fail;
}
break;
case 0x0011: /* MS-IMA (ex. Asphalt 6) */
@ -80,17 +85,17 @@ VGMSTREAM * init_vgmstream_vxn(STREAMFILE *streamFile) {
break;
#ifdef VGM_USE_FFMPEG
case 0x0800: { /* Musepack (ex. Asphalt Xtreme) */
ffmpeg_codec_data * ffmpeg_data = NULL;
if (bits != 0xFFFF) goto fail;
case 0x0800: /* Musepack (ex. Asphalt Xtreme) */
if (bits != -1) goto fail;
ffmpeg_data = init_ffmpeg_offset(streamFile, start_offset,stream_size);
if (!ffmpeg_data) goto fail;
vgmstream->codec_data = ffmpeg_data;
vgmstream->codec_data = init_ffmpeg_offset(streamFile, start_offset,stream_size);
if (!vgmstream->codec_data) goto fail;
vgmstream->coding_type = coding_FFmpeg;
vgmstream->layout_type = layout_none;
/* unlike standard .mpc, .vxn has no seek table so no need to fix */
//ffmpeg_set_force_seek(vgmstream->codec_data);
break;
}
#endif
default:
@ -98,7 +103,6 @@ VGMSTREAM * init_vgmstream_vxn(STREAMFILE *streamFile) {
goto fail;
}
/* open the file for reading */
if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) )
goto fail;
return vgmstream;

View File

@ -12,7 +12,7 @@ VGMSTREAM * init_vgmstream_waf(STREAMFILE *streamFile) {
if (!check_extensions(streamFile, "waf"))
goto fail;
if (read_32bitBE(0x00,streamFile) != 0x57414600) /* "WAF\0" "*/
if (read_32bitBE(0x00,streamFile) != 0x57414600) /* "WAF\0" */
goto fail;
if (read_32bitLE(0x34,streamFile) + 0x38 != get_streamfile_size(streamFile))
goto fail;
@ -25,13 +25,17 @@ VGMSTREAM * init_vgmstream_waf(STREAMFILE *streamFile) {
vgmstream = allocate_vgmstream(channel_count, loop_flag);
if (!vgmstream) goto fail;
vgmstream->sample_rate = read_32bitLE(0x08, streamFile);
vgmstream->meta_type = meta_WAF;
vgmstream->sample_rate = read_32bitLE(0x08, streamFile);
vgmstream->coding_type = coding_MSADPCM;
vgmstream->layout_type = layout_none;
vgmstream->interleave_block_size = read_16bitLE(0x10, streamFile);
vgmstream->num_samples = msadpcm_bytes_to_samples(read_32bitLE(0x34,streamFile), vgmstream->interleave_block_size, channel_count);
/* 0x04: null?, 0x0c: avg br, 0x12: bps, 0x14: s_p_f, 0x16~34: count + standard MSADPCM coefs (a modified RIFF fmt) */
vgmstream->frame_size = read_16bitLE(0x10, streamFile);
/* 0x04: null?, 0x0c: avg br, 0x12: bps, 0x14: s_p_f, 0x16~34: coefs (a modified RIFF fmt) */
if (!msadpcm_check_coefs(streamFile, 0x16))
goto fail;
vgmstream->num_samples = msadpcm_bytes_to_samples(read_32bitLE(0x34,streamFile), vgmstream->frame_size, channel_count);
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
goto fail;

View File

@ -2,21 +2,26 @@
#include "../layout/layout.h"
#include "../coding/coding.h"
/* CD-XA - from Sony PS1 and Philips CD-i CD audio */
/* CD-XA - from Sony PS1 and Philips CD-i CD audio, also Saturn streams */
VGMSTREAM * init_vgmstream_xa(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
off_t start_offset;
int loop_flag = 0, channel_count, sample_rate;
int is_blocked;
size_t file_size = get_streamfile_size(streamFile);
int is_riff = 0, is_blocked = 0;
size_t file_size, stream_size;
int total_subsongs, /*target_subsong = streamFile->stream_index,*/ target_config;
/* checks
* .xa: common, .str: sometimes (mainly videos)
* .xa: common
* .str: often (but not always) videos
* .adp: Phantasy Star Collection (SAT) raw XA */
if ( !check_extensions(streamFile,"xa,str,adp") )
goto fail;
file_size = get_streamfile_size(streamFile);
/* Proper XA comes in raw (BIN 2352 mode2/form2) CD sectors, that contain XA subheaders.
* This also has minimal support for headerless (ISO 2048 mode1/data) mode. */
@ -25,6 +30,7 @@ VGMSTREAM * init_vgmstream_xa(STREAMFILE *streamFile) {
read_32bitBE(0x08,streamFile) == 0x43445841 && /* "CDXA" */
read_32bitBE(0x0C,streamFile) == 0x666D7420) { /* "fmt " */
is_blocked = 1;
is_riff = 1;
start_offset = 0x2c; /* after "data" (chunk size tends to be a bit off) */
}
else {
@ -36,22 +42,34 @@ VGMSTREAM * init_vgmstream_xa(STREAMFILE *streamFile) {
start_offset = 0x00;
}
else { /* headerless and possibly incorrectly ripped */
is_blocked = 0;
start_offset = 0x00;
}
}
/* test some blocks (except when RIFF) since other .XA/STR may start blank */
if (start_offset == 0) {
int i, j, block;
if (!is_riff) {
int i, j, block = 0, miss = 0;
off_t test_offset = start_offset;
size_t sector_size = (is_blocked ? 0x900 : 0x800);
size_t block_size = 0x80;
const size_t sector_size = (is_blocked ? 0x900 : 0x800);
const size_t block_size = 0x80;
const int block_max = 3;
const int miss_max = 25;
while (block < block_max) {
uint8_t xa_submode = read_u8(test_offset + 0x12, streamFile);
int is_audio = !(xa_submode & 0x08) && (xa_submode & 0x04) && !(xa_submode & 0x02);
if (is_blocked && !is_audio) {
miss++;
if (block == 0 && miss > miss_max) /* no a single audio block found */
goto fail;
test_offset += sector_size + (is_blocked ? 0x18 + 0x18 : 0);
continue;
}
for (block = 0; block < 3; block++) {
test_offset += (is_blocked ? 0x18 : 0x00); /* header */
for (i = 0; i < (sector_size/block_size); i++) {
for (i = 0; i < (sector_size / block_size); i++) {
/* XA headers checks: filter indexes should be 0..3, and shifts 0..C */
for (j = 0; j < 16; j++) {
uint8_t header = (uint8_t)read_8bit(test_offset + j, streamFile);
@ -75,13 +93,72 @@ VGMSTREAM * init_vgmstream_xa(STREAMFILE *streamFile) {
}
test_offset += (is_blocked ? 0x18 : 0x00); /* footer */
block++;
}
}
/* find subsongs as XA can interleave sectors using 'file' and 'channel' makers (see blocked_xa.c) */
if (is_blocked) {
off_t offset;
STREAMFILE *sf_test = NULL;
/* mini buffer to speed up by reading headers only (not sure if O.S. has buffer though */
sf_test = reopen_streamfile(streamFile, 0x10);
if (!sf_test) goto fail;
//TODO add subsongs (optimized for giant xa)
// - only do if first sector and next sector have different channels?
// N sectors of the same channel then N sectors of another should't happen, but
// we need to read all sectors to count samples anyway.
// - read all sectors
// - skip non-audio sectors
// - find first actual stream start
// - detect file+channel change + register new subsong (or detect file end flags too)
// - save total sectors subsong_sectors[subsong] = N for quick sample calcs + stream size
// (block_update is much slower since it buffers all data)
total_subsongs = 0;
target_config = -1;
offset = start_offset;
while (offset < file_size) {
uint16_t xa_subheader = read_u16be(offset + 0x10, sf_test);
uint8_t xa_submode = read_u8 (offset + 0x12, sf_test);
int is_audio = !(xa_submode & 0x08) && (xa_submode & 0x04) && !(xa_submode & 0x02);
if (!is_audio) {
offset += 0x900 + 0x18 + 0x18;
continue;
}
//if target_subsong ..
//total_subsongs++
//...
target_config = xa_subheader;
start_offset = offset; //stream_offset
break;
}
close_streamfile(sf_test);
stream_size = file_size;
//stream_size = ...;
//if (target_subsong == 0) target_subsong = 1;
//if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail;
/* file has no audio */
if (target_config < 0) {
VGM_LOG("XA: no audio found");
goto fail;
}
}
/* data is ok: parse header */
if (is_blocked) {
/* parse 0x18 sector header (also see xa_blocked.c) */
/* parse 0x18 sector header (also see blocked_xa.c) */
uint8_t xa_header = (uint8_t)read_8bit(start_offset + 0x13,streamFile);
switch((xa_header >> 0) & 3) { /* 0..1: mono/stereo */
@ -94,9 +171,9 @@ VGMSTREAM * init_vgmstream_xa(STREAMFILE *streamFile) {
case 1: sample_rate = 18900; break;
default: goto fail;
}
switch((xa_header >> 4) & 3) { /* 4..5: bits per sample (0=4, 1=8) */
switch((xa_header >> 4) & 3) { /* 4..5: bits per sample (0=4-bit ADPCM, 1=8-bit ADPCM) */
case 0: break;
default: /* PS1 games only do 4b */
default: /* PS1 games only do 4-bit */
VGM_LOG("XA: unknown bits per sample found\n");
goto fail;
}
@ -150,11 +227,16 @@ VGMSTREAM * init_vgmstream_xa(STREAMFILE *streamFile) {
vgmstream->coding_type = coding_XA;
vgmstream->layout_type = is_blocked ? layout_blocked_xa : layout_none;
/* open the file for reading */
if (is_blocked) {
vgmstream->codec_config = target_config;
vgmstream->num_streams = total_subsongs;
vgmstream->stream_size = stream_size;
}
if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) )
goto fail;
if (is_blocked) {
/* calc num_samples as blocks may be empty or smaller than usual depending on flags */
vgmstream->next_block_offset = start_offset;
@ -162,7 +244,7 @@ VGMSTREAM * init_vgmstream_xa(STREAMFILE *streamFile) {
block_update(vgmstream->next_block_offset,vgmstream);
vgmstream->num_samples += vgmstream->current_block_samples;
}
while (vgmstream->next_block_offset < get_streamfile_size(streamFile));
while (vgmstream->next_block_offset < file_size);
block_update(start_offset,vgmstream);
}
else {

View File

@ -79,7 +79,12 @@ VGMSTREAM * init_vgmstream_xnb(STREAMFILE *streamFile) {
block_align = read_16bit(current_offset+0x0c, streamFile);
bps = read_16bit(current_offset+0x0e, streamFile);
if (codec == 0x166) {
if (codec == 0x0002) {
if (!msadpcm_check_coefs(streamFile, current_offset + 0x14))
goto fail;
}
if (codec == 0x0166) {
xma2_parse_fmt_chunk_extra(streamFile, current_offset, &loop_flag, &num_samples, &loop_start, &loop_end, big_endian);
xma_chunk_offset = current_offset;
}
@ -122,7 +127,7 @@ VGMSTREAM * init_vgmstream_xnb(STREAMFILE *streamFile) {
if (!block_align) goto fail;
vgmstream->coding_type = coding_MSADPCM;
vgmstream->layout_type = layout_none;
vgmstream->interleave_block_size = block_align;
vgmstream->frame_size = block_align;
vgmstream->num_samples = msadpcm_bytes_to_samples(data_size, block_align, channel_count);
break;

View File

@ -449,7 +449,7 @@ VGMSTREAM * init_vgmstream_xwb(STREAMFILE *streamFile) {
case MS_ADPCM: /* Persona 4 Ultimax (AC) */
vgmstream->coding_type = coding_MSADPCM;
vgmstream->layout_type = layout_none;
vgmstream->interleave_block_size = (xwb.block_align + 22) * xwb.channels; /*22=CONVERSION_OFFSET (?)*/
vgmstream->frame_size = (xwb.block_align + 22) * xwb.channels; /*22=CONVERSION_OFFSET (?)*/
break;
#ifdef VGM_USE_FFMPEG
@ -540,11 +540,12 @@ VGMSTREAM * init_vgmstream_xwb(STREAMFILE *streamFile) {
vgmstream->layout_type = layout_none;
break;
}
#endif
#ifdef VGM_USE_VORBIS
case OGG: { /* Oddworld: Strangers Wrath (iOS/Android) extension */
vgmstream->codec_data = init_ffmpeg_offset(streamFile, xwb.stream_offset, xwb.stream_size);
if ( !vgmstream->codec_data ) goto fail;
vgmstream->coding_type = coding_FFmpeg;
vgmstream->codec_data = init_ogg_vorbis(streamFile, xwb.stream_offset, xwb.stream_size, NULL);
if (!vgmstream->codec_data) goto fail;
vgmstream->coding_type = coding_OGG_VORBIS;
vgmstream->layout_type = layout_none;
break;
}

View File

@ -97,14 +97,15 @@ VGMSTREAM * init_vgmstream_xwc(STREAMFILE *streamFile) {
xma_fix_raw_samples(vgmstream, streamFile, start_offset,data_size, 0, 0,0); /* samples are ok, fix delay */
break;
}
#endif
#ifdef VGM_USE_VORBIS
case 0x564F5242: { /* "VORB" (PC) */
start_offset = 0x30;
data_size = data_size - start_offset;
vgmstream->codec_data = init_ffmpeg_offset(streamFile, start_offset,data_size);
vgmstream->codec_data = init_ogg_vorbis(streamFile, start_offset, data_size, NULL);
if ( !vgmstream->codec_data ) goto fail;
vgmstream->coding_type = coding_FFmpeg;
vgmstream->coding_type = coding_OGG_VORBIS;
vgmstream->layout_type = layout_none;
vgmstream->sample_rate = read_32bitLE(start_offset + 0x28, streamFile);

View File

@ -138,8 +138,8 @@ void vgmstream_tags_close(VGMSTREAM_TAGS *tags) {
int vgmstream_tags_next_tag(VGMSTREAM_TAGS* tags, STREAMFILE* tagfile) {
off_t file_size = get_streamfile_size(tagfile);
char currentname[VGMSTREAM_TAGS_LINE_MAX] = {0};
char line[VGMSTREAM_TAGS_LINE_MAX] = {0};
int ok, bytes_read, line_done, n1,n2;
char line[VGMSTREAM_TAGS_LINE_MAX];
int ok, bytes_read, line_ok, n1,n2;
if (!tags)
return 0;
@ -193,8 +193,8 @@ int vgmstream_tags_next_tag(VGMSTREAM_TAGS* tags, STREAMFILE* tagfile) {
goto fail;
}
bytes_read = get_streamfile_text_line(VGMSTREAM_TAGS_LINE_MAX,line, tags->offset,tagfile, &line_done);
if (!line_done || bytes_read == 0) goto fail;
bytes_read = read_line(line, sizeof(line), tags->offset, tagfile, &line_ok);
if (!line_ok || bytes_read == 0) goto fail;
tags->offset += bytes_read;

View File

@ -20,13 +20,13 @@ typedef struct {
size_t filesize; /* buffered file size */
} STDIO_STREAMFILE;
static STREAMFILE * open_stdio_streamfile_buffer(const char * const filename, size_t buffersize);
static STREAMFILE * open_stdio_streamfile_buffer_by_file(FILE *infile,const char * const filename, size_t buffersize);
static STREAMFILE* open_stdio_streamfile_buffer(const char * const filename, size_t buffersize);
static STREAMFILE* open_stdio_streamfile_buffer_by_file(FILE *infile, const char * const filename, size_t buffersize);
static size_t read_stdio(STDIO_STREAMFILE *streamfile,uint8_t * dest, off_t offset, size_t length) {
static size_t read_stdio(STDIO_STREAMFILE *streamfile, uint8_t *dst, off_t offset, size_t length) {
size_t length_read_total = 0;
if (!streamfile->infile || !dest || length <= 0 || offset < 0)
if (!streamfile->infile || !dst || length <= 0 || offset < 0)
return 0;
/* is the part of the requested length in the buffer? */
@ -38,11 +38,11 @@ static size_t read_stdio(STDIO_STREAMFILE *streamfile,uint8_t * dest, off_t offs
if (length_to_read > length)
length_to_read = length;
memcpy(dest,streamfile->buffer + offset_into_buffer,length_to_read);
memcpy(dst, streamfile->buffer + offset_into_buffer, length_to_read);
length_read_total += length_to_read;
length -= length_to_read;
offset += length_to_read;
dest += length_to_read;
dst += length_to_read;
}
#ifdef VGM_DEBUG_OUTPUT
@ -77,7 +77,7 @@ static size_t read_stdio(STDIO_STREAMFILE *streamfile,uint8_t * dest, off_t offs
/* fill the buffer (offset now is beyond buffer_offset) */
streamfile->buffer_offset = offset;
streamfile->validsize = fread(streamfile->buffer,sizeof(uint8_t),streamfile->buffersize,streamfile->infile);
streamfile->validsize = fread(streamfile->buffer, sizeof(uint8_t), streamfile->buffersize, streamfile->infile);
/* decide how much must be read this time */
if (length > streamfile->buffersize)
@ -87,55 +87,55 @@ static size_t read_stdio(STDIO_STREAMFILE *streamfile,uint8_t * dest, off_t offs
/* give up on partial reads (EOF) */
if (streamfile->validsize < length_to_read) {
memcpy(dest,streamfile->buffer,streamfile->validsize);
memcpy(dst, streamfile->buffer, streamfile->validsize);
offset += streamfile->validsize;
length_read_total += streamfile->validsize;
break;
}
/* use the new buffer */
memcpy(dest,streamfile->buffer,length_to_read);
memcpy(dst, streamfile->buffer, length_to_read);
offset += length_to_read;
length_read_total += length_to_read;
length -= length_to_read;
dest += length_to_read;
dst += length_to_read;
}
streamfile->offset = offset; /* last fread offset */
return length_read_total;
}
static size_t get_size_stdio(STDIO_STREAMFILE * streamfile) {
static size_t get_size_stdio(STDIO_STREAMFILE *streamfile) {
return streamfile->filesize;
}
static off_t get_offset_stdio(STDIO_STREAMFILE *streamfile) {
return streamfile->offset;
}
static void get_name_stdio(STDIO_STREAMFILE *streamfile,char *buffer,size_t length) {
strncpy(buffer,streamfile->name,length);
static void get_name_stdio(STDIO_STREAMFILE *streamfile, char *buffer, size_t length) {
strncpy(buffer, streamfile->name, length);
buffer[length-1]='\0';
}
static void close_stdio(STDIO_STREAMFILE * streamfile) {
static void close_stdio(STDIO_STREAMFILE *streamfile) {
if (streamfile->infile)
fclose(streamfile->infile);
free(streamfile->buffer);
free(streamfile);
}
static STREAMFILE *open_stdio(STDIO_STREAMFILE *streamFile,const char * const filename,size_t buffersize) {
static STREAMFILE* open_stdio(STDIO_STREAMFILE *streamfile, const char * const filename, size_t buffersize) {
if (!filename)
return NULL;
#if !defined (__ANDROID__)
// if same name, duplicate the file pointer we already have open
if (streamFile->infile && !strcmp(streamFile->name,filename)) {
if (streamfile->infile && !strcmp(streamfile->name,filename)) {
int newfd;
FILE *newfile;
STREAMFILE *newstreamFile;
STREAMFILE *new_sf;
if ( ((newfd = dup(fileno(streamFile->infile))) >= 0) && (newfile = fdopen( newfd, "rb")) ) {
newstreamFile = open_stdio_streamfile_buffer_by_file(newfile,filename,buffersize);
if (newstreamFile) {
return newstreamFile;
if ( ((newfd = dup(fileno(streamfile->infile))) >= 0) && (newfile = fdopen(newfd, "rb")) ) {
new_sf = open_stdio_streamfile_buffer_by_file(newfile, filename, buffersize);
if (new_sf) {
return new_sf;
}
// failure, close it and try the default path (which will probably fail a second time)
fclose(newfile);
@ -143,12 +143,12 @@ static STREAMFILE *open_stdio(STDIO_STREAMFILE *streamFile,const char * const fi
}
#endif
// a normal open, open a new file
return open_stdio_streamfile_buffer(filename,buffersize);
return open_stdio_streamfile_buffer(filename, buffersize);
}
static STREAMFILE * open_stdio_streamfile_buffer_by_file(FILE *infile, const char * const filename, size_t buffersize) {
uint8_t * buffer = NULL;
STDIO_STREAMFILE * streamfile = NULL;
static STREAMFILE* open_stdio_streamfile_buffer_by_file(FILE *infile, const char * const filename, size_t buffersize) {
uint8_t *buffer = NULL;
STDIO_STREAMFILE *streamfile = NULL;
buffer = calloc(buffersize,1);
if (!buffer) goto fail;
@ -167,7 +167,7 @@ static STREAMFILE * open_stdio_streamfile_buffer_by_file(FILE *infile, const cha
streamfile->buffersize = buffersize;
streamfile->buffer = buffer;
strncpy(streamfile->name,filename,sizeof(streamfile->name));
strncpy(streamfile->name, filename, sizeof(streamfile->name));
streamfile->name[sizeof(streamfile->name)-1] = '\0';
/* cache filesize */
@ -195,9 +195,9 @@ fail:
return NULL;
}
static STREAMFILE * open_stdio_streamfile_buffer(const char * const filename, size_t buffersize) {
FILE * infile;
STREAMFILE *streamFile;
static STREAMFILE* open_stdio_streamfile_buffer(const char * const filename, size_t bufsize) {
FILE *infile = NULL;
STREAMFILE *streamfile = NULL;
infile = fopen(filename,"rb");
if (!infile) {
@ -206,21 +206,20 @@ static STREAMFILE * open_stdio_streamfile_buffer(const char * const filename, si
return NULL;
}
streamFile = open_stdio_streamfile_buffer_by_file(infile,filename,buffersize);
if (!streamFile) {
streamfile = open_stdio_streamfile_buffer_by_file(infile, filename, bufsize);
if (!streamfile) {
if (infile) fclose(infile);
}
return streamFile;
return streamfile;
}
STREAMFILE * open_stdio_streamfile(const char * filename) {
return open_stdio_streamfile_buffer(filename,STREAMFILE_DEFAULT_BUFFER_SIZE);
STREAMFILE* open_stdio_streamfile(const char *filename) {
return open_stdio_streamfile_buffer(filename, STREAMFILE_DEFAULT_BUFFER_SIZE);
}
STREAMFILE * open_stdio_streamfile_by_file(FILE * file, const char * filename) {
return open_stdio_streamfile_buffer_by_file(file,filename,STREAMFILE_DEFAULT_BUFFER_SIZE);
STREAMFILE* open_stdio_streamfile_by_file(FILE *file, const char *filename) {
return open_stdio_streamfile_buffer_by_file(file, filename, STREAMFILE_DEFAULT_BUFFER_SIZE);
}
/* **************************************************** */
@ -238,10 +237,10 @@ typedef struct {
} BUFFER_STREAMFILE;
static size_t buffer_read(BUFFER_STREAMFILE *streamfile, uint8_t * dest, off_t offset, size_t length) {
static size_t buffer_read(BUFFER_STREAMFILE *streamfile, uint8_t *dst, off_t offset, size_t length) {
size_t length_read_total = 0;
if (!dest || length <= 0 || offset < 0)
if (!dst || length <= 0 || offset < 0)
return 0;
/* is the part of the requested length in the buffer? */
@ -253,11 +252,11 @@ static size_t buffer_read(BUFFER_STREAMFILE *streamfile, uint8_t * dest, off_t o
if (length_to_read > length)
length_to_read = length;
memcpy(dest,streamfile->buffer + offset_into_buffer,length_to_read);
memcpy(dst, streamfile->buffer + offset_into_buffer, length_to_read);
length_read_total += length_to_read;
length -= length_to_read;
offset += length_to_read;
dest += length_to_read;
dst += length_to_read;
}
#ifdef VGM_DEBUG_OUTPUT
@ -289,27 +288,27 @@ static size_t buffer_read(BUFFER_STREAMFILE *streamfile, uint8_t * dest, off_t o
/* give up on partial reads (EOF) */
if (streamfile->validsize < length_to_read) {
memcpy(dest,streamfile->buffer,streamfile->validsize);
memcpy(dst, streamfile->buffer, streamfile->validsize);
offset += streamfile->validsize;
length_read_total += streamfile->validsize;
break;
}
/* use the new buffer */
memcpy(dest,streamfile->buffer,length_to_read);
memcpy(dst, streamfile->buffer, length_to_read);
offset += length_to_read;
length_read_total += length_to_read;
length -= length_to_read;
dest += length_to_read;
dst += length_to_read;
}
streamfile->offset = offset; /* last fread offset */
return length_read_total;
}
static size_t buffer_get_size(BUFFER_STREAMFILE * streamfile) {
static size_t buffer_get_size(BUFFER_STREAMFILE *streamfile) {
return streamfile->filesize; /* cache */
}
static size_t buffer_get_offset(BUFFER_STREAMFILE * streamfile) {
static size_t buffer_get_offset(BUFFER_STREAMFILE *streamfile) {
return streamfile->offset; /* cache */
}
static void buffer_get_name(BUFFER_STREAMFILE *streamfile, char *buffer, size_t length) {
@ -325,12 +324,12 @@ static void buffer_close(BUFFER_STREAMFILE *streamfile) {
free(streamfile);
}
STREAMFILE *open_buffer_streamfile(STREAMFILE *streamfile, size_t buffer_size) {
STREAMFILE* open_buffer_streamfile(STREAMFILE *streamfile, size_t buffer_size) {
BUFFER_STREAMFILE *this_sf = NULL;
if (!streamfile) goto fail;
this_sf = calloc(1,sizeof(BUFFER_STREAMFILE));
this_sf = calloc(1, sizeof(BUFFER_STREAMFILE));
if (!this_sf) goto fail;
this_sf->buffersize = buffer_size;
@ -360,6 +359,12 @@ fail:
free(this_sf);
return NULL;
}
STREAMFILE* open_buffer_streamfile_f(STREAMFILE *streamfile, size_t buffer_size) {
STREAMFILE *new_sf = open_buffer_streamfile(streamfile, buffer_size);
if (!new_sf)
close_streamfile(streamfile);
return new_sf;
}
/* **************************************************** */
@ -373,13 +378,13 @@ typedef struct {
STREAMFILE *inner_sf;
} WRAP_STREAMFILE;
static size_t wrap_read(WRAP_STREAMFILE *streamfile, uint8_t * dest, off_t offset, size_t length) {
return streamfile->inner_sf->read(streamfile->inner_sf, dest, offset, length); /* default */
static size_t wrap_read(WRAP_STREAMFILE *streamfile, uint8_t *dst, off_t offset, size_t length) {
return streamfile->inner_sf->read(streamfile->inner_sf, dst, offset, length); /* default */
}
static size_t wrap_get_size(WRAP_STREAMFILE * streamfile) {
static size_t wrap_get_size(WRAP_STREAMFILE *streamfile) {
return streamfile->inner_sf->get_size(streamfile->inner_sf); /* default */
}
static size_t wrap_get_offset(WRAP_STREAMFILE * streamfile) {
static size_t wrap_get_offset(WRAP_STREAMFILE *streamfile) {
return streamfile->inner_sf->get_offset(streamfile->inner_sf); /* default */
}
static void wrap_get_name(WRAP_STREAMFILE *streamfile, char *buffer, size_t length) {
@ -393,8 +398,8 @@ static void wrap_close(WRAP_STREAMFILE *streamfile) {
free(streamfile);
}
STREAMFILE *open_wrap_streamfile(STREAMFILE *streamfile) {
WRAP_STREAMFILE *this_sf;
STREAMFILE* open_wrap_streamfile(STREAMFILE *streamfile) {
WRAP_STREAMFILE *this_sf = NULL;
if (!streamfile) return NULL;
@ -414,6 +419,12 @@ STREAMFILE *open_wrap_streamfile(STREAMFILE *streamfile) {
return &this_sf->sf;
}
STREAMFILE* open_wrap_streamfile_f(STREAMFILE *streamfile) {
STREAMFILE *new_sf = open_wrap_streamfile(streamfile);
if (!new_sf)
close_streamfile(streamfile);
return new_sf;
}
/* **************************************************** */
@ -425,10 +436,10 @@ typedef struct {
size_t size;
} CLAMP_STREAMFILE;
static size_t clamp_read(CLAMP_STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length) {
static size_t clamp_read(CLAMP_STREAMFILE *streamfile, uint8_t *dst, off_t offset, size_t length) {
off_t inner_offset = streamfile->start + offset;
size_t clamp_length = length > (streamfile->size - offset) ? (streamfile->size - offset) : length;
return streamfile->inner_sf->read(streamfile->inner_sf, dest, inner_offset, clamp_length);
return streamfile->inner_sf->read(streamfile->inner_sf, dst, inner_offset, clamp_length);
}
static size_t clamp_get_size(CLAMP_STREAMFILE *streamfile) {
return streamfile->size;
@ -441,7 +452,7 @@ static void clamp_get_name(CLAMP_STREAMFILE *streamfile, char *buffer, size_t le
}
static STREAMFILE *clamp_open(CLAMP_STREAMFILE *streamfile, const char * const filename, size_t buffersize) {
char original_filename[PATH_LIMIT];
STREAMFILE *new_inner_sf;
STREAMFILE *new_inner_sf = NULL;
new_inner_sf = streamfile->inner_sf->open(streamfile->inner_sf,filename,buffersize);
streamfile->inner_sf->get_name(streamfile->inner_sf, original_filename, PATH_LIMIT);
@ -450,7 +461,7 @@ static STREAMFILE *clamp_open(CLAMP_STREAMFILE *streamfile, const char * const f
if (strcmp(filename, original_filename) == 0) {
return open_clamp_streamfile(new_inner_sf, streamfile->start, streamfile->size); /* clamp again */
} else {
return new_inner_sf; /**/
return new_inner_sf;
}
}
static void clamp_close(CLAMP_STREAMFILE *streamfile) {
@ -458,10 +469,10 @@ static void clamp_close(CLAMP_STREAMFILE *streamfile) {
free(streamfile);
}
STREAMFILE *open_clamp_streamfile(STREAMFILE *streamfile, off_t start, size_t size) {
CLAMP_STREAMFILE *this_sf;
STREAMFILE* open_clamp_streamfile(STREAMFILE *streamfile, off_t start, size_t size) {
CLAMP_STREAMFILE *this_sf = NULL;
if (!streamfile || !size) return NULL;
if (!streamfile || size == 0) return NULL;
if (start + size > get_streamfile_size(streamfile)) return NULL;
this_sf = calloc(1,sizeof(CLAMP_STREAMFILE));
@ -482,6 +493,12 @@ STREAMFILE *open_clamp_streamfile(STREAMFILE *streamfile, off_t start, size_t si
return &this_sf->sf;
}
STREAMFILE* open_clamp_streamfile_f(STREAMFILE *streamfile, off_t start, size_t size) {
STREAMFILE *new_sf = open_clamp_streamfile(streamfile, start, size);
if (!new_sf)
close_streamfile(streamfile);
return new_sf;
}
/* **************************************************** */
@ -497,8 +514,8 @@ typedef struct {
//size_t (*close_data_callback)(STREAMFILE *, void*); /* called during close, allows to free stuff in data */
} IO_STREAMFILE;
static size_t io_read(IO_STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length) {
return streamfile->read_callback(streamfile->inner_sf, dest, offset, length, streamfile->data);
static size_t io_read(IO_STREAMFILE *streamfile, uint8_t *dst, off_t offset, size_t length) {
return streamfile->read_callback(streamfile->inner_sf, dst, offset, length, streamfile->data);
}
static size_t io_get_size(IO_STREAMFILE *streamfile) {
if (streamfile->size_callback)
@ -512,7 +529,7 @@ static off_t io_get_offset(IO_STREAMFILE *streamfile) {
static void io_get_name(IO_STREAMFILE *streamfile, char *buffer, size_t length) {
streamfile->inner_sf->get_name(streamfile->inner_sf, buffer, length); /* default */
}
static STREAMFILE *io_open(IO_STREAMFILE *streamfile, const char * const filename, size_t buffersize) {
static STREAMFILE* io_open(IO_STREAMFILE *streamfile, const char * const filename, size_t buffersize) {
//todo should have some flag to decide if opening other files with IO
STREAMFILE *new_inner_sf = streamfile->inner_sf->open(streamfile->inner_sf,filename,buffersize);
return open_io_streamfile(new_inner_sf, streamfile->data, streamfile->data_size, streamfile->read_callback, streamfile->size_callback);
@ -523,8 +540,8 @@ static void io_close(IO_STREAMFILE *streamfile) {
free(streamfile);
}
STREAMFILE *open_io_streamfile(STREAMFILE *streamfile, void* data, size_t data_size, void* read_callback, void* size_callback) {
IO_STREAMFILE *this_sf;
STREAMFILE* open_io_streamfile(STREAMFILE *streamfile, void *data, size_t data_size, void *read_callback, void *size_callback) {
IO_STREAMFILE *this_sf = NULL;
if (!streamfile) return NULL;
if ((data && !data_size) || (!data && data_size)) return NULL;
@ -556,6 +573,12 @@ STREAMFILE *open_io_streamfile(STREAMFILE *streamfile, void* data, size_t data_s
return &this_sf->sf;
}
STREAMFILE* open_io_streamfile_f(STREAMFILE *streamfile, void *data, size_t data_size, void *read_callback, void *size_callback) {
STREAMFILE *new_sf = open_io_streamfile(streamfile, data, data_size, read_callback, size_callback);
if (!new_sf)
close_streamfile(streamfile);
return new_sf;
}
/* **************************************************** */
@ -566,20 +589,20 @@ typedef struct {
char fakename[PATH_LIMIT];
} FAKENAME_STREAMFILE;
static size_t fakename_read(FAKENAME_STREAMFILE *streamfile, uint8_t * dest, off_t offset, size_t length) {
return streamfile->inner_sf->read(streamfile->inner_sf, dest, offset, length); /* default */
static size_t fakename_read(FAKENAME_STREAMFILE *streamfile, uint8_t *dst, off_t offset, size_t length) {
return streamfile->inner_sf->read(streamfile->inner_sf, dst, offset, length); /* default */
}
static size_t fakename_get_size(FAKENAME_STREAMFILE * streamfile) {
static size_t fakename_get_size(FAKENAME_STREAMFILE *streamfile) {
return streamfile->inner_sf->get_size(streamfile->inner_sf); /* default */
}
static size_t fakename_get_offset(FAKENAME_STREAMFILE * streamfile) {
static size_t fakename_get_offset(FAKENAME_STREAMFILE *streamfile) {
return streamfile->inner_sf->get_offset(streamfile->inner_sf); /* default */
}
static void fakename_get_name(FAKENAME_STREAMFILE *streamfile, char *buffer, size_t length) {
strncpy(buffer,streamfile->fakename,length);
buffer[length-1]='\0';
}
static STREAMFILE *fakename_open(FAKENAME_STREAMFILE *streamfile, const char * const filename, size_t buffersize) {
static STREAMFILE* fakename_open(FAKENAME_STREAMFILE *streamfile, const char * const filename, size_t buffersize) {
/* detect re-opening the file */
if (strcmp(filename, streamfile->fakename) == 0) {
STREAMFILE *new_inner_sf;
@ -598,8 +621,8 @@ static void fakename_close(FAKENAME_STREAMFILE *streamfile) {
free(streamfile);
}
STREAMFILE *open_fakename_streamfile(STREAMFILE *streamfile, const char * fakename, const char* fakeext) {
FAKENAME_STREAMFILE *this_sf;
STREAMFILE* open_fakename_streamfile(STREAMFILE *streamfile, const char *fakename, const char *fakeext) {
FAKENAME_STREAMFILE *this_sf = NULL;
if (!streamfile || (!fakename && !fakeext)) return NULL;
@ -623,6 +646,7 @@ STREAMFILE *open_fakename_streamfile(STREAMFILE *streamfile, const char * fakena
} else {
streamfile->get_name(streamfile, this_sf->fakename, PATH_LIMIT);
}
if (fakeext) {
char * ext = strrchr(this_sf->fakename,'.');
if (ext != NULL)
@ -632,10 +656,15 @@ STREAMFILE *open_fakename_streamfile(STREAMFILE *streamfile, const char * fakena
return &this_sf->sf;
}
STREAMFILE* open_fakename_streamfile_f(STREAMFILE *streamfile, const char *fakename, const char *fakeext) {
STREAMFILE *new_sf = open_fakename_streamfile(streamfile, fakename, fakeext);
if (!new_sf)
close_streamfile(streamfile);
return new_sf;
}
/* **************************************************** */
typedef struct {
STREAMFILE sf;
@ -646,7 +675,7 @@ typedef struct {
off_t offset;
} MULTIFILE_STREAMFILE;
static size_t multifile_read(MULTIFILE_STREAMFILE *streamfile, uint8_t * dest, off_t offset, size_t length) {
static size_t multifile_read(MULTIFILE_STREAMFILE *streamfile, uint8_t *dst, off_t offset, size_t length) {
int i, segment = 0;
off_t segment_offset = 0;
size_t done = 0;
@ -674,7 +703,7 @@ static size_t multifile_read(MULTIFILE_STREAMFILE *streamfile, uint8_t * dest, o
if (segment >= streamfile->inner_sfs_size) /* over last segment, not fully done */
break;
/* reads over segment size are ok, will return smaller value and continue next segment */
done += streamfile->inner_sfs[segment]->read(streamfile->inner_sfs[segment], dest+done, segment_offset, length - done);
done += streamfile->inner_sfs[segment]->read(streamfile->inner_sfs[segment], dst + done, segment_offset, length - done);
segment++;
segment_offset = 0;
}
@ -691,7 +720,7 @@ static size_t multifile_get_offset(MULTIFILE_STREAMFILE * streamfile) {
static void multifile_get_name(MULTIFILE_STREAMFILE *streamfile, char *buffer, size_t length) {
streamfile->inner_sfs[0]->get_name(streamfile->inner_sfs[0], buffer, length);
}
static STREAMFILE *multifile_open(MULTIFILE_STREAMFILE *streamfile, const char * const filename, size_t buffersize) {
static STREAMFILE* multifile_open(MULTIFILE_STREAMFILE *streamfile, const char * const filename, size_t buffersize) {
char original_filename[PATH_LIMIT];
STREAMFILE *new_sf = NULL;
STREAMFILE **new_inner_sfs = NULL;
@ -740,11 +769,12 @@ static void multifile_close(MULTIFILE_STREAMFILE *streamfile) {
free(streamfile);
}
STREAMFILE *open_multifile_streamfile(STREAMFILE **streamfiles, size_t streamfiles_size) {
MULTIFILE_STREAMFILE *this_sf;
STREAMFILE* open_multifile_streamfile(STREAMFILE **streamfiles, size_t streamfiles_size) {
MULTIFILE_STREAMFILE *this_sf = NULL;
int i;
if (!streamfiles || !streamfiles_size) return NULL;
for (i = 0; i < streamfiles_size; i++) {
if (!streamfiles[i]) return NULL;
}
@ -783,18 +813,28 @@ fail:
free(this_sf);
return NULL;
}
STREAMFILE* open_multifile_streamfile_f(STREAMFILE **streamfiles, size_t streamfiles_size) {
STREAMFILE *new_sf = open_multifile_streamfile(streamfiles, streamfiles_size);
if (!new_sf) {
int i;
for (i = 0; i < streamfiles_size; i++) {
close_streamfile(streamfiles[i]);
}
}
return new_sf;
}
/* **************************************************** */
STREAMFILE * open_streamfile(STREAMFILE *streamFile, const char * pathname) {
return streamFile->open(streamFile,pathname,STREAMFILE_DEFAULT_BUFFER_SIZE);
STREAMFILE* open_streamfile(STREAMFILE *streamfile, const char *pathname) {
return streamfile->open(streamfile, pathname, STREAMFILE_DEFAULT_BUFFER_SIZE);
}
STREAMFILE * open_streamfile_by_ext(STREAMFILE *streamFile, const char * ext) {
STREAMFILE* open_streamfile_by_ext(STREAMFILE *streamfile, const char *ext) {
char filename[PATH_LIMIT];
int filename_len, fileext_len;
streamFile->get_name(streamFile,filename,sizeof(filename));
streamfile->get_name(streamfile, filename, sizeof(filename));
filename_len = strlen(filename);
fileext_len = strlen(filename_extension(filename));
@ -807,15 +847,17 @@ STREAMFILE * open_streamfile_by_ext(STREAMFILE *streamFile, const char * ext) {
strcpy(filename + filename_len - fileext_len, ext);
}
return streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE);
return streamfile->open(streamfile, filename, STREAMFILE_DEFAULT_BUFFER_SIZE);
}
STREAMFILE * open_streamfile_by_filename(STREAMFILE *streamFile, const char * filename) {
STREAMFILE* open_streamfile_by_filename(STREAMFILE *streamfile, const char * filename) {
char fullname[PATH_LIMIT];
char partname[PATH_LIMIT];
char *path, *name;
streamFile->get_name(streamFile, fullname, sizeof(fullname));
if (!streamfile || !filename) return NULL;
streamfile->get_name(streamfile, fullname, sizeof(fullname));
//todo normalize separators in a better way, safeops, improve copying
path = strrchr(fullname,DIR_SEPARATOR);
@ -854,87 +896,82 @@ STREAMFILE * open_streamfile_by_filename(STREAMFILE *streamFile, const char * fi
strcpy(fullname, filename);
}
return streamFile->open(streamFile, fullname, STREAMFILE_DEFAULT_BUFFER_SIZE);
return streamfile->open(streamfile, fullname, STREAMFILE_DEFAULT_BUFFER_SIZE);
}
STREAMFILE * reopen_streamfile(STREAMFILE *streamFile, size_t buffer_size) {
STREAMFILE* reopen_streamfile(STREAMFILE *streamfile, size_t buffer_size) {
char pathname[PATH_LIMIT];
if (!streamfile) return NULL;
if (buffer_size == 0)
buffer_size = STREAMFILE_DEFAULT_BUFFER_SIZE;
streamFile->get_name(streamFile,pathname,sizeof(pathname));
return streamFile->open(streamFile,pathname,buffer_size);
streamfile->get_name(streamfile,pathname,sizeof(pathname));
return streamfile->open(streamfile,pathname,buffer_size);
}
/* **************************************************** */
/* Read a line into dst. The source files are lines separated by CRLF (Windows) / LF (Unux) / CR (Mac).
* The line will be null-terminated and CR/LF removed if found.
*
* Returns the number of bytes read (including CR/LF), note that this is not the string length.
* line_done_ptr is set to 1 if the complete line was read into dst; NULL can be passed to ignore.
*/
size_t get_streamfile_text_line(int dst_length, char * dst, off_t offset, STREAMFILE * streamfile, int *line_done_ptr) {
size_t read_line(char *buf, int buf_size, off_t offset, STREAMFILE *sf, int *p_line_ok) {
int i;
off_t file_length = get_streamfile_size(streamfile);
off_t file_size = get_streamfile_size(sf);
int extra_bytes = 0; /* how many bytes over those put in the buffer were read */
if (line_done_ptr) *line_done_ptr = 0;
if (p_line_ok) *p_line_ok = 0;
for (i = 0; i < dst_length-1 && offset+i < file_length; i++) {
char in_char = read_8bit(offset+i,streamfile);
for (i = 0; i < buf_size-1 && offset+i < file_size; i++) {
char in_char = read_8bit(offset+i, sf);
/* check for end of line */
if (in_char == 0x0d && read_8bit(offset+i+1,streamfile) == 0x0a) { /* CRLF */
if (in_char == 0x0d && read_8bit(offset+i+1, sf) == 0x0a) { /* CRLF */
extra_bytes = 2;
if (line_done_ptr) *line_done_ptr = 1;
if (p_line_ok) *p_line_ok = 1;
break;
}
else if (in_char == 0x0d || in_char == 0x0a) { /* CR or LF */
extra_bytes = 1;
if (line_done_ptr) *line_done_ptr = 1;
if (p_line_ok) *p_line_ok = 1;
break;
}
dst[i] = in_char;
buf[i] = in_char;
}
dst[i] = '\0';
buf[i] = '\0';
/* did we fill the buffer? */
if (i == dst_length) {
char in_char = read_8bit(offset+i,streamfile);
if (i == buf_size) {
char in_char = read_8bit(offset+i, sf);
/* did the bytes we missed just happen to be the end of the line? */
if (in_char == 0x0d && read_8bit(offset+i+1,streamfile) == 0x0a) { /* CRLF */
if (in_char == 0x0d && read_8bit(offset+i+1, sf) == 0x0a) { /* CRLF */
extra_bytes = 2;
if (line_done_ptr) *line_done_ptr = 1;
if (p_line_ok) *p_line_ok = 1;
}
else if (in_char == 0x0d || in_char == 0x0a) { /* CR or LF */
extra_bytes = 1;
if (line_done_ptr) *line_done_ptr = 1;
if (p_line_ok) *p_line_ok = 1;
}
}
/* did we hit the file end? */
if (offset+i == file_length) {
if (offset+i == file_size) {
/* then we did in fact finish reading the last line */
if (line_done_ptr) *line_done_ptr = 1;
if (p_line_ok) *p_line_ok = 1;
}
return i + extra_bytes;
}
/* reads a c-string (ANSI only), up to maxsize or NULL, returning size. buf is optional (works as get_string_size). */
size_t read_string(char * buf, size_t maxsize, off_t offset, STREAMFILE *streamFile) {
size_t read_string(char *buf, size_t buf_size, off_t offset, STREAMFILE *sf) {
size_t pos;
for (pos = 0; pos < maxsize; pos++) {
char c = read_8bit(offset + pos, streamFile);
for (pos = 0; pos < buf_size; pos++) {
char c = read_8bit(offset + pos, sf);
if (buf) buf[pos] = c;
if (c == '\0')
return pos;
if (pos+1 == maxsize) { /* null at maxsize and don't validate (expected to be garbage) */
if (pos+1 == buf_size) { /* null at maxsize and don't validate (expected to be garbage) */
if (buf) buf[pos] = '\0';
return maxsize;
return buf_size;
}
if (c < 0x20 || (uint8_t)c > 0xA5)
goto fail;
@ -946,22 +983,18 @@ fail:
}
/* Opens a file containing decryption keys and copies to buffer.
* Tries combinations of keynames based on the original filename.
* returns size of key if found and copied */
size_t read_key_file(uint8_t * buf, size_t bufsize, STREAMFILE *streamFile) {
size_t read_key_file(uint8_t *buf, size_t buf_size, STREAMFILE *sf) {
char keyname[PATH_LIMIT];
char filename[PATH_LIMIT];
const char *path, *ext;
STREAMFILE * streamFileKey = NULL;
size_t keysize;
streamFile->get_name(streamFile,filename,sizeof(filename));
sf->get_name(sf,filename,sizeof(filename));
if (strlen(filename)+4 > sizeof(keyname)) goto fail;
/* try to open a keyfile using variations:
* "(name.ext)key" (per song), "(.ext)key" (per folder) */
/* try to open a keyfile using variations */
{
ext = strrchr(filename,'.');
if (ext!=NULL) ext = ext+1;
@ -972,7 +1005,7 @@ size_t read_key_file(uint8_t * buf, size_t bufsize, STREAMFILE *streamFile) {
/* "(name.ext)key" */
strcpy(keyname, filename);
strcat(keyname, "key");
streamFileKey = streamFile->open(streamFile,keyname,STREAMFILE_DEFAULT_BUFFER_SIZE);
streamFileKey = sf->open(sf,keyname,STREAMFILE_DEFAULT_BUFFER_SIZE);
if (streamFileKey) goto found;
/* "(name.ext)KEY" */
@ -993,7 +1026,7 @@ size_t read_key_file(uint8_t * buf, size_t bufsize, STREAMFILE *streamFile) {
}
if (ext) strcat(keyname, ext);
strcat(keyname, "key");
streamFileKey = streamFile->open(streamFile,keyname,STREAMFILE_DEFAULT_BUFFER_SIZE);
streamFileKey = sf->open(sf,keyname,STREAMFILE_DEFAULT_BUFFER_SIZE);
if (streamFileKey) goto found;
/* "(.ext)KEY" */
@ -1008,7 +1041,7 @@ size_t read_key_file(uint8_t * buf, size_t bufsize, STREAMFILE *streamFile) {
found:
keysize = get_streamfile_size(streamFileKey);
if (keysize > bufsize) goto fail;
if (keysize > buf_size) goto fail;
if (read_streamfile(buf, 0, keysize, streamFileKey) != keysize)
goto fail;
@ -1021,7 +1054,6 @@ fail:
return 0;
}
/* hack to allow relative paths in various OSs */
void fix_dir_separators(char * filename) {
char c;
int i = 0;
@ -1032,19 +1064,14 @@ void fix_dir_separators(char * filename) {
}
}
/**
* Checks if the stream filename is one of the extensions (comma-separated, ex. "adx" or "adx,aix").
* Empty is ok to accept files without extension ("", "adx,,aix"). Returns 0 on failure
*/
int check_extensions(STREAMFILE *streamFile, const char * cmp_exts) {
int check_extensions(STREAMFILE *sf, const char * cmp_exts) {
char filename[PATH_LIMIT];
const char * ext = NULL;
const char * cmp_ext = NULL;
const char * ststr_res = NULL;
size_t ext_len, cmp_len;
streamFile->get_name(streamFile,filename,sizeof(filename));
sf->get_name(sf,filename,sizeof(filename));
ext = filename_extension(filename);
ext_len = strlen(ext);

View File

@ -59,12 +59,12 @@
* to do file operations, as plugins may need to provide their own callbacks.
* Reads from arbitrary offsets, meaning internally may need fseek equivalents during reads. */
typedef struct _STREAMFILE {
size_t (*read)(struct _STREAMFILE *,uint8_t * dest, off_t offset, size_t length);
size_t (*read)(struct _STREAMFILE *, uint8_t * dst, off_t offset, size_t length);
size_t (*get_size)(struct _STREAMFILE *);
off_t (*get_offset)(struct _STREAMFILE *); //todo: DO NOT USE, NOT RESET PROPERLY (remove?)
/* for dual-file support */
void (*get_name)(struct _STREAMFILE *,char *name,size_t length);
struct _STREAMFILE * (*open)(struct _STREAMFILE *,const char * const filename,size_t buffersize);
void (*get_name)(struct _STREAMFILE * ,char *name, size_t length);
struct _STREAMFILE * (*open)(struct _STREAMFILE *, const char * const filename, size_t buffersize);
void (*close)(struct _STREAMFILE *);
@ -74,59 +74,68 @@ typedef struct _STREAMFILE {
} STREAMFILE;
/* All open_ fuctions should be safe to call with wrong/null parameters.
* _f versions are the same but free the passed streamfile on failure and return NULL,
* to ease chaining by avoiding realloc-style temp ptr verbosity */
/* Opens a standard STREAMFILE, opening from path.
* Uses stdio (FILE) for operations, thus plugins may not want to use it. */
STREAMFILE *open_stdio_streamfile(const char * filename);
STREAMFILE* open_stdio_streamfile(const char *filename);
/* Opens a standard STREAMFILE from a pre-opened FILE. */
STREAMFILE *open_stdio_streamfile_by_file(FILE * file, const char * filename);
STREAMFILE* open_stdio_streamfile_by_file(FILE *file, const char *filename);
/* Opens a STREAMFILE that does buffered IO.
* Can be used when the underlying IO may be slow (like when using custom IO).
* Buffer size is optional. */
STREAMFILE *open_buffer_streamfile(STREAMFILE *streamfile, size_t buffer_size);
STREAMFILE* open_buffer_streamfile(STREAMFILE *streamfile, size_t buffer_size);
STREAMFILE* open_buffer_streamfile_f(STREAMFILE *streamfile, size_t buffer_size);
/* Opens a STREAMFILE that doesn't close the underlying streamfile.
* Calls to open won't wrap the new SF (assumes it needs to be closed).
* Can be used in metas to test custom IO without closing the external SF. */
STREAMFILE *open_wrap_streamfile(STREAMFILE *streamfile);
STREAMFILE* open_wrap_streamfile(STREAMFILE *streamfile);
STREAMFILE* open_wrap_streamfile_f(STREAMFILE *streamfile);
/* Opens a STREAMFILE that clamps reads to a section of a larger streamfile.
* Can be used with subfiles inside a bigger file (to fool metas, or to simplify custom IO). */
STREAMFILE *open_clamp_streamfile(STREAMFILE *streamfile, off_t start, size_t size);
STREAMFILE* open_clamp_streamfile(STREAMFILE *streamfile, off_t start, size_t size);
STREAMFILE* open_clamp_streamfile_f(STREAMFILE *streamfile, off_t start, size_t size);
/* Opens a STREAMFILE that uses custom IO for streamfile reads.
* Can be used to modify data on the fly (ex. decryption), or even transform it from a format to another. */
STREAMFILE *open_io_streamfile(STREAMFILE *streamfile, void* data, size_t data_size, void* read_callback, void* size_callback);
STREAMFILE* open_io_streamfile(STREAMFILE *streamfile, void *data, size_t data_size, void *read_callback, void *size_callback);
STREAMFILE* open_io_streamfile_f(STREAMFILE *streamfile, void *data, size_t data_size, void *read_callback, void *size_callback);
/* Opens a STREAMFILE that reports a fake name, but still re-opens itself properly.
* Can be used to trick a meta's extension check (to call from another, with a modified SF).
* When fakename isn't supplied it's read from the streamfile, and the extension swapped with fakeext.
* If the fakename is an existing file, open won't work on it as it'll reopen the fake-named streamfile. */
STREAMFILE *open_fakename_streamfile(STREAMFILE *streamfile, const char * fakename, const char * fakeext);
STREAMFILE* open_fakename_streamfile(STREAMFILE *streamfile, const char * fakename, const char * fakeext);
STREAMFILE* open_fakename_streamfile_f(STREAMFILE *streamfile, const char * fakename, const char * fakeext);
//todo probably could simply use custom IO
/* Opens streamfile formed from multiple streamfiles, their data joined during reads.
* Can be used when data is segmented in multiple separate files.
* The first streamfile is used to get names, stream index and so on. */
STREAMFILE *open_multifile_streamfile(STREAMFILE **streamfiles, size_t streamfiles_size);
STREAMFILE* open_multifile_streamfile(STREAMFILE **streamfiles, size_t streamfiles_size);
STREAMFILE* open_multifile_streamfile_f(STREAMFILE **streamfiles, size_t streamfiles_size);
/* Opens a STREAMFILE from a (path)+filename.
* Just a wrapper, to avoid having to access the STREAMFILE's callbacks directly. */
STREAMFILE * open_streamfile(STREAMFILE *streamFile, const char * pathname);
STREAMFILE* open_streamfile(STREAMFILE *streamfile, const char * pathname);
/* Opens a STREAMFILE from a base pathname + new extension
* Can be used to get companion headers. */
STREAMFILE * open_streamfile_by_ext(STREAMFILE *streamFile, const char * ext);
STREAMFILE* open_streamfile_by_ext(STREAMFILE *streamfile, const char * ext);
/* Opens a STREAMFILE from a base path + new filename.
* Can be used to get companion files. Relative paths like
* './filename', '../filename', 'dir/filename' also work. */
STREAMFILE * open_streamfile_by_filename(STREAMFILE *streamFile, const char * filename);
STREAMFILE* open_streamfile_by_filename(STREAMFILE *streamfile, const char * filename);
/* Reopen a STREAMFILE with a different buffer size, for fine-tuned bigfile parsing.
* Uses default buffer size when buffer_size is 0 */
STREAMFILE * reopen_streamfile(STREAMFILE *streamFile, size_t buffer_size);
STREAMFILE * reopen_streamfile(STREAMFILE *streamfile, size_t buffer_size);
/* close a file, destroy the STREAMFILE object */
@ -136,8 +145,8 @@ static inline void close_streamfile(STREAMFILE * streamfile) {
}
/* read from a file, returns number of bytes read */
static inline size_t read_streamfile(uint8_t * dest, off_t offset, size_t length, STREAMFILE * streamfile) {
return streamfile->read(streamfile,dest,offset,length);
static inline size_t read_streamfile(uint8_t *dst, off_t offset, size_t length, STREAMFILE *streamfile) {
return streamfile->read(streamfile, dst, offset,length);
}
/* return file size */
@ -256,16 +265,27 @@ static inline size_t align_size_to_block(size_t value, size_t block_align) {
/* various STREAMFILE helpers functions */
size_t get_streamfile_text_line(int dst_length, char * dst, off_t offset, STREAMFILE * streamfile, int *line_done_ptr);
/* Read into dst a line delimited by CRLF (Windows) / LF (Unux) / CR (Mac) / EOF, null-terminated
* and without line feeds. Returns bytes read (including CR/LF), *not* the same as string length.
* p_line_ok is set to 1 if the complete line was read; pass NULL to ignore. */
size_t read_line(char *buf, int buf_size, off_t offset, STREAMFILE *sf, int *p_line_ok);
size_t read_string(char * buf, size_t bufsize, off_t offset, STREAMFILE *streamFile);
/* reads a c-string (ANSI only), up to bufsize or NULL, returning size. buf is optional (works as get_string_size). */
size_t read_string(char *buf, size_t buf_size, off_t offset, STREAMFILE *sf);
size_t read_key_file(uint8_t * buf, size_t bufsize, STREAMFILE *streamFile);
/* Opens a file containing decryption keys and copies to buffer.
* Tries "(name.ext)key" (per song), "(.ext)key" (per folder) keynames.
* returns size of key if found and copied */
size_t read_key_file(uint8_t *buf, size_t buf_size, STREAMFILE *sf);
void fix_dir_separators(char * filename);
/* hack to allow relative paths in various OSs */
void fix_dir_separators(char *filename);
/* Checks if the stream filename is one of the extensions (comma-separated, ex. "adx" or "adx,aix").
* Empty is ok to accept files without extension ("", "adx,,aix"). Returns 0 on failure */
int check_extensions(STREAMFILE *streamFile, const char * cmp_exts);
/* chunk-style file helpers */
int find_chunk_be(STREAMFILE *streamFile, uint32_t chunk_id, off_t start_offset, int full_chunk_size, off_t *out_chunk_offset, size_t *out_chunk_size);
int find_chunk_le(STREAMFILE *streamFile, uint32_t chunk_id, off_t start_offset, int full_chunk_size, off_t *out_chunk_offset, size_t *out_chunk_size);
int find_chunk(STREAMFILE *streamFile, uint32_t chunk_id, off_t start_offset, int full_chunk_size, off_t *out_chunk_offset, size_t *out_chunk_size, int big_endian_size, int zero_size_end);
@ -275,7 +295,7 @@ int find_chunk_riff_be(STREAMFILE *streamFile, uint32_t chunk_id, off_t start_of
/* same with chunk ids in variable endianess (so instead of "fmt " has " tmf" */
int find_chunk_riff_ve(STREAMFILE *streamFile, uint32_t chunk_id, off_t start_offset, size_t max_size, off_t *out_chunk_offset, size_t *out_chunk_size, int big_endian);
/* filename helpers */
void get_streamfile_name(STREAMFILE *streamFile, char * buffer, size_t size);
void get_streamfile_filename(STREAMFILE *streamFile, char * buffer, size_t size);
void get_streamfile_basename(STREAMFILE *streamFile, char * buffer, size_t size);

View File

@ -7,12 +7,20 @@
#define _STREAMTYPES_H
#ifdef _MSC_VER
/* Common versions:
* - 1500: VS2008
* - 1600: VS2010
* - 1700: VS2012
* - 1800: VS2013
* - 1900: VS2015
* - 1920: VS2019 */
#if (_MSC_VER >= 1600)
#include <stdint.h>
#else
#include <pstdint.h>
#endif /* (_MSC_VER >= 1600) */
#endif
#ifndef inline /* (_MSC_VER < 1900)? */
#define inline _inline
@ -23,10 +31,11 @@
#if (_MSC_VER < 1900)
#define snprintf _snprintf
#endif /* (_MSC_VER < 1900) */
#endif
#else
#include <stdint.h>
#endif /* _MSC_VER */
typedef int16_t sample; //TODO: deprecated, remove

View File

@ -33,6 +33,22 @@ static inline int64_t get_64bitLE(uint8_t * p) {
return (uint64_t)(((uint64_t)p[0]) | ((uint64_t)p[1]<<8) | ((uint64_t)p[2]<<16) | ((uint64_t)p[3]<<24) | ((uint64_t)p[4]<<32) | ((uint64_t)p[5]<<40) | ((uint64_t)p[6]<<48) | ((uint64_t)p[7]<<56));
}
/* alias of the above */
static inline int8_t get_s8 (uint8_t *p) { return ( int8_t)p[0]; }
static inline uint8_t get_u8 (uint8_t *p) { return (uint8_t)p[0]; }
static inline int16_t get_s16le(uint8_t *p) { return ( int16_t)get_16bitLE(p); }
static inline uint16_t get_u16le(uint8_t *p) { return (uint16_t)get_16bitLE(p); }
static inline int16_t get_s16be(uint8_t *p) { return ( int16_t)get_16bitBE(p); }
static inline uint16_t get_u16be(uint8_t *p) { return (uint16_t)get_16bitBE(p); }
static inline int32_t get_s32le(uint8_t *p) { return ( int32_t)get_32bitLE(p); }
static inline uint32_t get_u32le(uint8_t *p) { return (uint32_t)get_32bitLE(p); }
static inline int32_t get_s32be(uint8_t *p) { return ( int32_t)get_32bitBE(p); }
static inline uint32_t get_u32be(uint8_t *p) { return (uint32_t)get_32bitBE(p); }
static inline int64_t get_s64be(uint8_t *p) { return ( int64_t)get_64bitLE(p); }
static inline uint64_t get_u64be(uint8_t *p) { return (uint64_t)get_64bitLE(p); }
static inline int64_t get_s64le(uint8_t *p) { return ( int64_t)get_64bitBE(p); }
static inline uint64_t get_u64le(uint8_t *p) { return (uint64_t)get_64bitBE(p); }
void put_8bit(uint8_t * buf, int8_t i);
void put_16bitLE(uint8_t * buf, int16_t i);

View File

@ -1215,10 +1215,10 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) {
return 128;
case coding_MSADPCM:
return (vgmstream->interleave_block_size - 0x07*vgmstream->channels)*2 / vgmstream->channels + 2;
return (vgmstream->frame_size - 0x07*vgmstream->channels)*2 / vgmstream->channels + 2;
case coding_MSADPCM_int:
case coding_MSADPCM_ck:
return (vgmstream->interleave_block_size - 0x07)*2 + 2;
return (vgmstream->frame_size - 0x07)*2 + 2;
case coding_WS: /* only works if output sample size is 8 bit, which always is for WS ADPCM */
return vgmstream->ws_output_size;
case coding_AICA:
@ -1407,7 +1407,7 @@ int get_vgmstream_frame_size(VGMSTREAM * vgmstream) {
case coding_MSADPCM:
case coding_MSADPCM_int:
case coding_MSADPCM_ck:
return vgmstream->interleave_block_size;
return vgmstream->frame_size;
case coding_WS:
return vgmstream->current_block_size;
case coding_AICA:
@ -2393,7 +2393,8 @@ void describe_vgmstream(VGMSTREAM * vgmstream, char * desc, int length) {
}
/* codecs with configurable frame size */
if (vgmstream->interleave_block_size > 0) {
if (vgmstream->frame_size > 0 || vgmstream->interleave_block_size > 0) {
int32_t frame_size = vgmstream->frame_size > 0 ? vgmstream->frame_size : vgmstream->interleave_block_size;
switch (vgmstream->coding_type) {
case coding_MSADPCM:
case coding_MSADPCM_int:
@ -2403,7 +2404,7 @@ void describe_vgmstream(VGMSTREAM * vgmstream, char * desc, int length) {
case coding_WWISE_IMA:
case coding_REF_IMA:
case coding_PSX_cfg:
snprintf(temp,TEMPSIZE, "frame size: %#x bytes\n", (int32_t)vgmstream->interleave_block_size);
snprintf(temp,TEMPSIZE, "frame size: %#x bytes\n", frame_size);
concatn(length,desc,temp);
break;
default:
@ -2628,8 +2629,7 @@ static STREAMFILE * get_vgmstream_average_bitrate_channel_streamfile(VGMSTREAM *
#ifdef VGM_USE_VORBIS
if (vgmstream->coding_type == coding_OGG_VORBIS) {
ogg_vorbis_codec_data *data = vgmstream->codec_data;
return data ? data->ov_streamfile.streamfile : NULL;
return ogg_vorbis_get_streamfile(vgmstream->codec_data);
}
#endif
if (vgmstream->coding_type == coding_CRI_HCA) {
@ -2793,7 +2793,7 @@ int vgmstream_open_stream(VGMSTREAM * vgmstream, STREAMFILE *streamFile, off_t s
vgmstream->coding_type == coding_PSX_pivotal) &&
(vgmstream->interleave_block_size == 0 || vgmstream->interleave_block_size > 0x50)) {
VGM_LOG("VGMSTREAM: PSX-cfg decoder with wrong frame size %x\n", vgmstream->interleave_block_size);
return 0;
goto fail;
}
if ((vgmstream->coding_type == coding_CRI_ADX ||
@ -2803,7 +2803,14 @@ int vgmstream_open_stream(VGMSTREAM * vgmstream, STREAMFILE *streamFile, off_t s
vgmstream->coding_type == coding_CRI_ADX_fixed) &&
(vgmstream->interleave_block_size == 0 || vgmstream->interleave_block_size > 0x12)) {
VGM_LOG("VGMSTREAM: ADX decoder with wrong frame size %x\n", vgmstream->interleave_block_size);
return 0;
goto fail;
}
if ((vgmstream->coding_type == coding_MSADPCM ||
vgmstream->coding_type == coding_MSADPCM_ck ||
vgmstream->coding_type == coding_MSADPCM_int) &&
vgmstream->frame_size == 0) {
vgmstream->frame_size = vgmstream->interleave_block_size;
}
/* if interleave is big enough keep a buffer per channel */

View File

@ -840,6 +840,7 @@ typedef struct {
size_t interleave_first_block_size; /* different interleave for first block */
size_t interleave_first_skip; /* data skipped before interleave first (needed to skip other channels) */
size_t interleave_last_block_size; /* smaller interleave for last block */
size_t frame_size; /* for codecs with configurable size */
/* subsong config */
int num_streams; /* for multi-stream formats (0=not set/one stream, 1=one stream) */
@ -909,7 +910,8 @@ typedef struct {
} VGMSTREAM;
#ifdef VGM_USE_VORBIS
/* Ogg with Vorbis */
/* standard Ogg Vorbis */
typedef struct {
STREAMFILE *streamfile;
ogg_int64_t start; /* file offset where the Ogg starts */
@ -922,15 +924,9 @@ typedef struct {
off_t scd_xor_length;
uint32_t xor_value;
} ogg_vorbis_streamfile;
} ogg_vorbis_io;
typedef struct {
OggVorbis_File ogg_vorbis_file;
int bitstream;
ogg_vorbis_streamfile ov_streamfile;
int disable_reordering; /* Xiph reorder channels on output, except for some devs */
} ogg_vorbis_codec_data;
typedef struct ogg_vorbis_codec_data ogg_vorbis_codec_data;
/* custom Vorbis modes */