Merge pull request #492 from bnnm/ogg-msadpcm-xa

ogg msadpcm xa
This commit is contained in:
bnnm 2019-10-21 00:00:52 +02:00 committed by GitHub
commit 246222dd52
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
59 changed files with 1496 additions and 1062 deletions

View File

@ -18,7 +18,7 @@ ifeq ($(TARGET_OS),Windows_NT)
CFLAGS += -DWIN32
endif
CFLAGS += -Wall -Werror=format-security -Wdeclaration-after-statement -Wvla -O3 -DVAR_ARRAYS -I../ext_includes $(EXTRA_CFLAGS)
CFLAGS += -ffast-math -O3 -Wall -Werror=format-security -Wdeclaration-after-statement -Wvla -DVAR_ARRAYS -I../ext_includes $(EXTRA_CFLAGS)
LDFLAGS += -L../src -L../ext_libs -lvgmstream $(EXTRA_LDFLAGS) -lm
TARGET_EXT_LIBS =

View File

@ -283,13 +283,13 @@ static void apply_config(VGMSTREAM * vgmstream, cli_config *cfg) {
/* honor suggested config, if any (defined order matters)
* note that ignore_fade and play_forever should take priority */
if (vgmstream->config_loop_count) {
if (vgmstream->config_loop_count > 0.0) {
cfg->loop_count = vgmstream->config_loop_count;
}
if (vgmstream->config_fade_delay) {
if (vgmstream->config_fade_delay > 0.0) {
cfg->fade_delay = vgmstream->config_fade_delay;
}
if (vgmstream->config_fade_time) {
if (vgmstream->config_fade_time > 0.0) {
cfg->fade_time = vgmstream->config_fade_time;
}
if (vgmstream->config_force_loop) {

View File

@ -49,7 +49,7 @@ The following can be used in place of `(value)` for `(key) = (value)` commands.
* `$1|2|3|4`: value has size of 8/16/24/32 bit (optional, defaults to 4)
* Example: `@0x10:BE$2` means `get big endian 16b value at 0x10`
- `(field)`: uses current value of some fields. Accepted strings:
- `interleave, interleave_last, channels, sample_rate, start_offset, data_size, num_samples, loop_start_sample, loop_end_sample, subsong_count, subsong_offset`
- `interleave, interleave_last, channels, sample_rate, start_offset, data_size, num_samples, loop_start_sample, loop_end_sample, subsong_count, subsong_offset, subfile_offset, subfile_size, name_valueX`
- `(other)`: other special values for certain keys, described per key

View File

@ -25,7 +25,7 @@ sounds/file#12
### Segments mode
Some games clumsily loop audio by using multiple full file "segments", so you can play separate intro + loop files together as a single track. Channel number must be equal, mixing sample rates is ok (uses first).
Some games clumsily loop audio by using multiple full file "segments", so you can play separate intro + loop files together as a single track.
**Ratchet & Clank (PS2)**: *bgm01.txtp*
```
@ -57,6 +57,8 @@ loop_start_segment = 2
loop_end_segment = 3
loop_mode = keep # loops in 2nd file's loop_start to 3rd file's loop_end
```
Mixing sample rates is ok (uses first) but channel number must be equal for all files. You can use mixing (explained later) to join segments of different channels though.
### Layers mode
Some games layer channels or dynamic parts that must play at the same time, for example main melody + vocal track.

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -308,6 +308,10 @@
RelativePath=".\meta\xavs_streamfile.h"
>
</File>
<File
RelativePath=".\meta\riff_ogg_streamfile.h"
>
</File>
<File
RelativePath=".\meta\sfh_streamfile.h"
>

View File

@ -119,6 +119,7 @@
<ClInclude Include="meta\mzrt_streamfile.h" />
<ClInclude Include="meta\ogg_vorbis_streamfile.h" />
<ClInclude Include="meta\opus_interleave_streamfile.h" />
<ClInclude Include="meta\riff_ogg_streamfile.h" />
<ClInclude Include="meta\sfh_streamfile.h" />
<ClInclude Include="meta\sqex_scd_streamfile.h" />
<ClInclude Include="meta\sqex_sead_streamfile.h" />

View File

@ -128,6 +128,9 @@
<ClInclude Include="meta\xavs_streamfile.h">
<Filter>meta\Header Files</Filter>
</ClInclude>
<ClInclude Include="meta\riff_ogg_streamfile.h">
<Filter>meta\Header Files</Filter>
</ClInclude>
<ClInclude Include="meta\sfh_streamfile.h">
<Filter>meta\Header Files</Filter>
</ClInclude>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -828,6 +828,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) */
@ -897,7 +898,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 */
@ -910,15 +912,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 */

View File

@ -15,7 +15,7 @@ endif
### main defs
OUTPUT_WINAMP = in_vgmstream.dll
CFLAGS += -Wall -Werror=format-security -Wdeclaration-after-statement -Wvla -O3 -DUSE_ALLOCA -DWIN32 -I../ext_includes $(EXTRA_CFLAGS)
CFLAGS += -ffast-math -O3 -Wall -Werror=format-security -Wdeclaration-after-statement -Wvla -DUSE_ALLOCA -DWIN32 -I../ext_includes $(EXTRA_CFLAGS)
LDFLAGS += -L../src -L../ext_libs -lm -lvgmstream $(EXTRA_LDFLAGS)
TARGET_EXT_LIBS =

View File

@ -15,7 +15,7 @@ endif
### main defs
OUTPUT_XMPLAY = xmp-vgmstream.dll
CFLAGS += -Wall -Werror=format-security -Wdeclaration-after-statement -Wvla -O3 -DUSE_ALLOCA -DWIN32 -I../ext_includes $(EXTRA_CFLAGS)
CFLAGS += -ffast-math -O3 -Wall -Werror=format-security -Wdeclaration-after-statement -Wvla -DUSE_ALLOCA -DWIN32 -I../ext_includes $(EXTRA_CFLAGS)
LDFLAGS += -L../src -L../ext_libs -lm -lvgmstream $(EXTRA_LDFLAGS)
TARGET_EXT_LIBS =