Updated VGMStream to r1050-2574-g246222dd
parent
163a75f566
commit
242c0b9476
|
@ -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 */,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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_ */
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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_ */
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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" */
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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 */
|
||||
|
|
Loading…
Reference in New Issue