mirror of
https://github.com/vgmstream/vgmstream.git
synced 2024-11-28 08:20:54 +01:00
Merge pull request #1231 from bnnm/bigrp-misc
- Fix some .ccc - Show multiple cue names in SQEX .sab - Reduce internal path limit for more common cases - Add .bigrp [Blaster Master Zero 2 (SW), Gunvolt 3 (SW)]
This commit is contained in:
commit
24f5ee6500
@ -315,6 +315,7 @@ are used in few games.
|
||||
- Xiph CELT (FSB)
|
||||
- Musepack
|
||||
- FLAC
|
||||
- Inti Creates' Range / DCT codecs
|
||||
- Others
|
||||
|
||||
Sometimes standard codecs come in non-standard layouts that aren't normally
|
||||
|
19
doc/USAGE.md
19
doc/USAGE.md
@ -630,6 +630,25 @@ If your main motivation for extracting is to rename or have loose files, remembe
|
||||
you can simply use TXTP to point to a subsong, and name that `.txtp` whatever you
|
||||
want, without having to touch original data or needing custom extractors.
|
||||
|
||||
### Cue formats
|
||||
Some formats that vgmstream supports (SQEX's .sab, CRI's .acb+awb, Wwise's .bnk+wem,
|
||||
Microsoft's .xss+.xwb....) are "cue" formats. The way these work is (more or less),
|
||||
they have a bunch of named audio "cues"/"events" in a section of the file, that are
|
||||
called to play one or multiple audio "waves"/"materials" in another section.
|
||||
|
||||
Rather than handling cues, vgmstream shows and plays waves, then assigns cue names
|
||||
that point to the wave if possible, since vgmstream mainly deals with streamed/wave
|
||||
audio and simulating cues is out of scope. Figuring out a whole cue format can be a
|
||||
*huge* time investment, so handling waves only is often enough.
|
||||
|
||||
Cues can be *very* complex, like N cues pointing to 1 wave with varying pitch, or
|
||||
1 cue playing one random wave out of 3. Sometimes not all waves are referenced by
|
||||
cues, or cues do undesirable effects that make only playing waves a good compromise.
|
||||
Simulating cues is better handled with external tools that allow more flexibility
|
||||
(for example, this project simulates Wwise's extremely complex cues/events by creating
|
||||
.TXTP telling vgmstream which config and waves to play, and one can filter desired
|
||||
cues/TXTP: https://github.com/bnnm/wwiser).
|
||||
|
||||
## Logged errors and unplayable supported files
|
||||
Some formats should normally play, but somehow don't. In those cases plugins
|
||||
can print vgmstream's error info to console (for example, `.fsb` with an unknown
|
||||
|
@ -353,6 +353,16 @@ void seek_tac(tac_codec_data* data, int32_t num_sample);
|
||||
void free_tac(tac_codec_data* data);
|
||||
|
||||
|
||||
/* ice_decoder */
|
||||
typedef struct ice_codec_data ice_codec_data;
|
||||
|
||||
ice_codec_data* init_ice(STREAMFILE* sf, int subsong);
|
||||
void decode_ice(ice_codec_data* data, sample_t* outbuf, int32_t samples_to_do);
|
||||
void reset_ice(ice_codec_data* data);
|
||||
void seek_ice(ice_codec_data* data, int32_t num_sample);
|
||||
void free_ice(ice_codec_data* data);
|
||||
|
||||
|
||||
#ifdef VGM_USE_VORBIS
|
||||
/* ogg_vorbis_decoder */
|
||||
typedef struct ogg_vorbis_codec_data ogg_vorbis_codec_data;
|
||||
|
138
src/coding/ice_decoder.c
Normal file
138
src/coding/ice_decoder.c
Normal file
@ -0,0 +1,138 @@
|
||||
#include "coding.h"
|
||||
#include "ice_decoder_icelib.h"
|
||||
|
||||
|
||||
typedef struct {
|
||||
STREAMFILE* sf;
|
||||
int offset;
|
||||
} icelib_io_t;
|
||||
|
||||
struct ice_codec_data {
|
||||
STREAMFILE* sf;
|
||||
int channels;
|
||||
icesnd_handle_t* ctx;
|
||||
icelib_io_t io;
|
||||
};
|
||||
|
||||
static void icelib_set_callbacks(icesnd_callback_t* cb, STREAMFILE* sf, icelib_io_t* io);
|
||||
|
||||
ice_codec_data* init_ice(STREAMFILE* sf, int subsong) {
|
||||
ice_codec_data* data = NULL;
|
||||
|
||||
data = calloc(1, sizeof(ice_codec_data));
|
||||
if (!data) goto fail;
|
||||
|
||||
data->sf = reopen_streamfile(sf, 0);
|
||||
if (!data->sf) goto fail;
|
||||
|
||||
{
|
||||
icesnd_callback_t cb = {0};
|
||||
icesnd_info_t info = {0};
|
||||
int err;
|
||||
|
||||
icelib_set_callbacks(&cb, data->sf, &data->io);
|
||||
|
||||
data->ctx = icesnd_init(subsong, &cb);
|
||||
if (!data->ctx) goto fail;
|
||||
|
||||
err = icesnd_info(data->ctx, &info);
|
||||
if (err < ICESND_RESULT_OK) goto fail;
|
||||
|
||||
data->channels = info.channels;
|
||||
}
|
||||
|
||||
return data;
|
||||
fail:
|
||||
free_ice(data);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void decode_ice(ice_codec_data* data, sample_t* outbuf, int32_t samples_to_do) {
|
||||
int channels = data->channels;
|
||||
|
||||
while (samples_to_do > 0) {
|
||||
int done = icesnd_decode(data->ctx, outbuf, samples_to_do);
|
||||
if (done <= 0) goto decode_fail;
|
||||
|
||||
outbuf += done * channels;
|
||||
samples_to_do -= done;
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
decode_fail:
|
||||
VGM_LOG("ICE: decode error\n");
|
||||
memset(outbuf, 0, samples_to_do * channels * sizeof(sample_t));
|
||||
}
|
||||
|
||||
void reset_ice(ice_codec_data* data) {
|
||||
if (!data) return;
|
||||
|
||||
icesnd_reset(data->ctx, 0);
|
||||
}
|
||||
|
||||
void seek_ice(ice_codec_data* data, int32_t num_sample) {
|
||||
if (!data) return;
|
||||
|
||||
//todo discard (this should only be called when looping)
|
||||
icesnd_reset(data->ctx, 1);
|
||||
}
|
||||
|
||||
void free_ice(ice_codec_data* data) {
|
||||
if (!data) return;
|
||||
|
||||
close_streamfile(data->sf);
|
||||
icesnd_free(data->ctx);
|
||||
free(data);
|
||||
}
|
||||
|
||||
/* ************************* */
|
||||
|
||||
static int icelib_read(void* dst, int size, int n, void* arg) {
|
||||
icelib_io_t* io = arg;
|
||||
int bytes_read, items_read;
|
||||
|
||||
bytes_read = read_streamfile(dst, io->offset, size * n, io->sf);
|
||||
items_read = bytes_read / size;
|
||||
io->offset += bytes_read;
|
||||
|
||||
return items_read;
|
||||
}
|
||||
|
||||
static int icelib_seek(void* arg, int offset, int whence) {
|
||||
icelib_io_t* io = arg;
|
||||
int base_offset, new_offset;
|
||||
|
||||
switch (whence) {
|
||||
case ICESND_SEEK_SET:
|
||||
base_offset = 0;
|
||||
break;
|
||||
case ICESND_SEEK_CUR:
|
||||
base_offset = io->offset;
|
||||
break;
|
||||
case ICESND_SEEK_END:
|
||||
base_offset = get_streamfile_size(io->sf);
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
break;
|
||||
}
|
||||
|
||||
new_offset = base_offset + offset;
|
||||
if (new_offset < 0 /*|| new_offset > get_streamfile_size(config->sf)*/) {
|
||||
return -1; /* unseekable */
|
||||
}
|
||||
else {
|
||||
io->offset = new_offset;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void icelib_set_callbacks(icesnd_callback_t* cb, STREAMFILE* sf, icelib_io_t* io) {
|
||||
io->offset = 0;
|
||||
io->sf = sf;
|
||||
|
||||
cb->arg = io;
|
||||
cb->read = icelib_read;
|
||||
cb->seek = icelib_seek;
|
||||
}
|
1433
src/coding/ice_decoder_icelib.c
Normal file
1433
src/coding/ice_decoder_icelib.c
Normal file
File diff suppressed because it is too large
Load Diff
67
src/coding/ice_decoder_icelib.h
Normal file
67
src/coding/ice_decoder_icelib.h
Normal file
@ -0,0 +1,67 @@
|
||||
#ifndef _ICELIB_H_
|
||||
#define _ICELIB_H_
|
||||
#include <stdint.h>
|
||||
|
||||
/* Decodes Inti Creates's "ICE" engine BIGRP sounds. */
|
||||
|
||||
#define ICESND_CODEC_RANGE 0x00
|
||||
#define ICESND_CODEC_DATA 0x01
|
||||
#define ICESND_CODEC_MIDI 0x02
|
||||
#define ICESND_CODEC_DCT 0x03
|
||||
|
||||
#define ICESND_RESULT_OK 0
|
||||
#define ICESND_ERROR_HEADER -1
|
||||
#define ICESND_ERROR_SETUP -2
|
||||
#define ICESND_ERROR_DECODE -3
|
||||
|
||||
typedef struct icesnd_handle_t icesnd_handle_t;
|
||||
|
||||
#define ICESND_SEEK_SET 0
|
||||
#define ICESND_SEEK_CUR 1
|
||||
#define ICESND_SEEK_END 2
|
||||
|
||||
typedef struct {
|
||||
/* whole file in memory (for testing) */
|
||||
const uint8_t* filebuf;
|
||||
int filebuf_size;
|
||||
|
||||
/* custom IO */
|
||||
void* arg;
|
||||
int (*read)(void* dst, int size, int n, void* arg);
|
||||
int (*seek)(void* arg, int offset, int whence);
|
||||
|
||||
} icesnd_callback_t;
|
||||
|
||||
/* Inits ICE lib with config.
|
||||
* Original code expects all data in memory, but this allows setting read callbacks
|
||||
* (making it feed-style was a bit complex due to how data is laid out) */
|
||||
icesnd_handle_t* icesnd_init(int target_subsong, icesnd_callback_t* cb);
|
||||
|
||||
void icesnd_free(icesnd_handle_t* handle);
|
||||
|
||||
/* resets the decoder. If loop_starts and file loops and
|
||||
* (format is not seekable but separated into intro+body blocks) */
|
||||
void icesnd_reset(icesnd_handle_t* handle, int loop_start);
|
||||
|
||||
/* Decodes up to samples for N channels into sbuf (interleaved). Returns samples done,
|
||||
* 0 if not possible (non-looped files past end) or negative on error.
|
||||
* May return less than wanted samples on block boundaries.
|
||||
*
|
||||
* It's designed to decode an arbitrary number of samples, as data isn't divided into frames (original
|
||||
* player does sample_rate/60.0 at a time). Codec 0 is aligned to 100 samples and codec 3 to 16 though. */
|
||||
int icesnd_decode(icesnd_handle_t* handle, int16_t* sbuf, int max_samples);
|
||||
|
||||
typedef struct {
|
||||
int total_subsongs;
|
||||
int codec;
|
||||
int sample_rate;
|
||||
int channels;
|
||||
int loop_start;
|
||||
int num_samples;
|
||||
int loop_flag;
|
||||
} icesnd_info_t;
|
||||
|
||||
/* loads info */
|
||||
int icesnd_info(icesnd_handle_t* handle, icesnd_info_t* info);
|
||||
|
||||
#endif
|
@ -17,8 +17,9 @@ static int build_header_identification(uint8_t* buf, size_t bufsize, int channel
|
||||
static int build_header_comment(uint8_t* buf, size_t bufsize);
|
||||
static int build_header_setup(uint8_t* buf, size_t bufsize, uint32_t setup_id, STREAMFILE* sf);
|
||||
|
||||
static int load_fvs_file_single(uint8_t* buf, size_t bufsize, uint32_t setup_id, STREAMFILE* sf);
|
||||
static int load_fvs_file_multi(uint8_t* buf, size_t bufsize, uint32_t setup_id, STREAMFILE* sf);
|
||||
#if !(FSB_VORBIS_USE_PRECOMPILED_FVS)
|
||||
static int load_fvs_file(uint8_t* buf, size_t bufsize, uint32_t setup_id, STREAMFILE* sf);
|
||||
#endif
|
||||
static int load_fvs_array(uint8_t* buf, size_t bufsize, uint32_t setup_id, STREAMFILE* sf);
|
||||
|
||||
|
||||
@ -58,7 +59,7 @@ int vorbis_custom_parse_packet_fsb(VGMSTREAMCHANNEL* stream, vorbis_custom_codec
|
||||
size_t bytes;
|
||||
|
||||
/* get next packet size from the FSB 16b header (doesn't count this 16b) */
|
||||
data->op.bytes = (uint16_t)read_16bitLE(stream->offset, stream->streamfile);
|
||||
data->op.bytes = read_u16le(stream->offset, stream->streamfile);
|
||||
stream->offset += 2;
|
||||
if (data->op.bytes == 0 || data->op.bytes == 0xFFFF || data->op.bytes > data->buffer_size) goto fail; /* EOF or end padding */
|
||||
|
||||
@ -108,16 +109,16 @@ static int build_header_identification(uint8_t* buf, size_t bufsize, int channel
|
||||
}
|
||||
blocksizes = (exp_blocksize_0 << 4) | (exp_blocksize_1);
|
||||
|
||||
put_8bit (buf+0x00, 0x01); /* packet_type (id) */
|
||||
put_u8 (buf+0x00, 0x01); /* packet_type (id) */
|
||||
memcpy (buf+0x01, "vorbis", 6); /* id */
|
||||
put_32bitLE(buf+0x07, 0x00); /* vorbis_version (fixed) */
|
||||
put_8bit (buf+0x0b, channels); /* audio_channels */
|
||||
put_32bitLE(buf+0x0c, sample_rate); /* audio_sample_rate */
|
||||
put_32bitLE(buf+0x10, 0x00); /* bitrate_maximum (optional hint) */
|
||||
put_32bitLE(buf+0x14, 0x00); /* bitrate_nominal (optional hint) */
|
||||
put_32bitLE(buf+0x18, 0x00); /* bitrate_minimum (optional hint) */
|
||||
put_8bit (buf+0x1c, blocksizes); /* blocksize_0 + blocksize_1 nibbles */
|
||||
put_8bit (buf+0x1d, 0x01); /* framing_flag (fixed) */
|
||||
put_u32le(buf+0x07, 0x00); /* vorbis_version (fixed) */
|
||||
put_u8 (buf+0x0b, channels); /* audio_channels */
|
||||
put_s32le(buf+0x0c, sample_rate); /* audio_sample_rate */
|
||||
put_u32le(buf+0x10, 0x00); /* bitrate_maximum (optional hint) */
|
||||
put_u32le(buf+0x14, 0x00); /* bitrate_nominal (optional hint) */
|
||||
put_u32le(buf+0x18, 0x00); /* bitrate_minimum (optional hint) */
|
||||
put_u8 (buf+0x1c, blocksizes); /* blocksize_0 + blocksize_1 nibbles */
|
||||
put_u8 (buf+0x1d, 0x01); /* framing_flag (fixed) */
|
||||
|
||||
return bytes;
|
||||
}
|
||||
@ -127,12 +128,12 @@ static int build_header_comment(uint8_t* buf, size_t bufsize) {
|
||||
|
||||
if (bytes > bufsize) return 0;
|
||||
|
||||
put_8bit (buf+0x00, 0x03); /* packet_type (comments) */
|
||||
memcpy (buf+0x01, "vorbis", 6); /* id */
|
||||
put_32bitLE(buf+0x07, 0x09); /* vendor_length */
|
||||
memcpy (buf+0x0b, "vgmstream", 9); /* vendor_string */
|
||||
put_32bitLE(buf+0x14, 0x00); /* user_comment_list_length */
|
||||
put_8bit (buf+0x18, 0x01); /* framing_flag (fixed) */
|
||||
put_u8 (buf+0x00, 0x03); /* packet_type (comments) */
|
||||
memcpy (buf+0x01, "vorbis", 6); /* id */
|
||||
put_u32le(buf+0x07, 0x09); /* vendor_length */
|
||||
memcpy (buf+0x0b, "vgmstream", 9); /* vendor_string */
|
||||
put_u32le(buf+0x14, 0x00); /* user_comment_list_length */
|
||||
put_u8 (buf+0x18, 0x01); /* framing_flag (fixed) */
|
||||
|
||||
return bytes;
|
||||
}
|
||||
@ -145,49 +146,20 @@ static int build_header_setup(uint8_t* buf, size_t bufsize, uint32_t setup_id, S
|
||||
if (bytes)
|
||||
return bytes;
|
||||
|
||||
#if !(FSB_VORBIS_USE_PRECOMPILED_FVS)
|
||||
/* try to load from external files */
|
||||
bytes = load_fvs_file_single(buf, bufsize, setup_id, sf);
|
||||
if (bytes)
|
||||
return bytes;
|
||||
|
||||
bytes = load_fvs_file_multi(buf, bufsize, setup_id, sf);
|
||||
bytes = load_fvs_file(buf, bufsize, setup_id, sf);
|
||||
if (bytes)
|
||||
return bytes;
|
||||
#endif
|
||||
|
||||
/* not found */
|
||||
VGM_LOG("FSB Vorbis: setup_id %08x not found\n", setup_id);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int load_fvs_file_single(uint8_t* buf, size_t bufsize, uint32_t setup_id, STREAMFILE* sf) {
|
||||
STREAMFILE* sf_setup = NULL;
|
||||
|
||||
/* get from artificial external file (used if compiled without codebooks) */
|
||||
{
|
||||
char setupname[0x20];
|
||||
|
||||
snprintf(setupname, sizeof(setupname), ".fvs_%08x", setup_id);
|
||||
sf_setup = open_streamfile_by_filename(sf, setupname);
|
||||
}
|
||||
|
||||
/* get codebook and copy to buffer */
|
||||
if (sf_setup) {
|
||||
size_t bytes = sf_setup->get_size(sf_setup);
|
||||
if (bytes > bufsize) goto fail;
|
||||
|
||||
if (read_streamfile(buf, 0, bytes, sf_setup) != bytes)
|
||||
goto fail;
|
||||
|
||||
close_streamfile(sf_setup);
|
||||
return bytes;
|
||||
}
|
||||
|
||||
fail:
|
||||
close_streamfile(sf_setup);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int load_fvs_file_multi(uint8_t* buf, size_t bufsize, uint32_t setup_id, STREAMFILE* sf) {
|
||||
#if !(FSB_VORBIS_USE_PRECOMPILED_FVS)
|
||||
static int load_fvs_file(uint8_t* buf, size_t bufsize, uint32_t setup_id, STREAMFILE* sf) {
|
||||
STREAMFILE* sf_setup = NULL;
|
||||
|
||||
/* from to get from artificial external file (used if compiled without codebooks) */
|
||||
@ -230,6 +202,7 @@ fail:
|
||||
if (sf_setup) sf_setup->close(sf_setup);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int load_fvs_array(uint8_t* buf, size_t bufsize, uint32_t setup_id, STREAMFILE* sf) {
|
||||
#if FSB_VORBIS_USE_PRECOMPILED_FVS
|
||||
|
@ -34,7 +34,9 @@ static int ww2ogg_generate_vorbis_packet(bitstream_t* ow, bitstream_t* iw, wpack
|
||||
static int ww2ogg_generate_vorbis_setup(bitstream_t* ow, bitstream_t* iw, vorbis_custom_codec_data* data, size_t packet_size, STREAMFILE* sf);
|
||||
|
||||
static int load_wvc(uint8_t* ibuf, size_t ibufsize, uint32_t codebook_id, wwise_setup_t setup_type, STREAMFILE* sf);
|
||||
#if !(WWISE_VORBIS_USE_PRECOMPILED_WVC)
|
||||
static int load_wvc_file(uint8_t* buf, size_t bufsize, uint32_t codebook_id, STREAMFILE* sf);
|
||||
#endif
|
||||
static int load_wvc_array(uint8_t* buf, size_t bufsize, uint32_t codebook_id, wwise_setup_t setup_type);
|
||||
|
||||
|
||||
@ -1133,16 +1135,19 @@ static int load_wvc(uint8_t* ibuf, size_t ibufsize, uint32_t codebook_id, wwise_
|
||||
if (bytes)
|
||||
return bytes;
|
||||
|
||||
#if !(WWISE_VORBIS_USE_PRECOMPILED_WVC)
|
||||
/* try to load from external file (ignoring type, just use file if found) */
|
||||
bytes = load_wvc_file(ibuf, ibufsize, codebook_id, sf);
|
||||
if (bytes)
|
||||
return bytes;
|
||||
#endif
|
||||
|
||||
/* not found */
|
||||
VGM_LOG("Wwise Vorbis: codebook_id %04x not found\n", codebook_id);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if !(WWISE_VORBIS_USE_PRECOMPILED_WVC)
|
||||
static int load_wvc_file(uint8_t* buf, size_t bufsize, uint32_t codebook_id, STREAMFILE* sf) {
|
||||
STREAMFILE* sf_setup = NULL;
|
||||
size_t wvc_size = 0;
|
||||
@ -1185,6 +1190,7 @@ fail:
|
||||
close_streamfile(sf_setup);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int load_wvc_array(uint8_t* buf, size_t bufsize, uint32_t codebook_id, wwise_setup_t setup_type) {
|
||||
#if WWISE_VORBIS_USE_PRECOMPILED_WVC
|
||||
@ -1217,7 +1223,7 @@ static int load_wvc_array(uint8_t* buf, size_t bufsize, uint32_t codebook_id, ww
|
||||
}
|
||||
}
|
||||
|
||||
// this can be used if the lists contained a 1:1 dump of the codebook files
|
||||
// this can be used with 1:1 dump of the codebook file
|
||||
#if 0
|
||||
if (wvc == NULL) goto fail;
|
||||
/* find codebook and copy to buffer */
|
||||
|
22
src/decode.c
22
src/decode.c
@ -39,6 +39,11 @@ void free_codec(VGMSTREAM* vgmstream) {
|
||||
free_tac(vgmstream->codec_data);
|
||||
}
|
||||
|
||||
if (vgmstream->coding_type == coding_ICE_RANGE ||
|
||||
vgmstream->coding_type == coding_ICE_DCT) {
|
||||
free_ice(vgmstream->codec_data);
|
||||
}
|
||||
|
||||
if (vgmstream->coding_type == coding_UBI_ADPCM) {
|
||||
free_ubi_adpcm(vgmstream->codec_data);
|
||||
}
|
||||
@ -140,6 +145,11 @@ void seek_codec(VGMSTREAM* vgmstream) {
|
||||
seek_tac(vgmstream->codec_data, vgmstream->loop_current_sample);
|
||||
}
|
||||
|
||||
if (vgmstream->coding_type == coding_ICE_RANGE ||
|
||||
vgmstream->coding_type == coding_ICE_DCT) {
|
||||
seek_ice(vgmstream->codec_data, vgmstream->loop_current_sample);
|
||||
}
|
||||
|
||||
if (vgmstream->coding_type == coding_UBI_ADPCM) {
|
||||
seek_ubi_adpcm(vgmstream->codec_data, vgmstream->loop_current_sample);
|
||||
}
|
||||
@ -246,6 +256,11 @@ void reset_codec(VGMSTREAM* vgmstream) {
|
||||
reset_tac(vgmstream->codec_data);
|
||||
}
|
||||
|
||||
if (vgmstream->coding_type == coding_ICE_RANGE ||
|
||||
vgmstream->coding_type == coding_ICE_DCT) {
|
||||
reset_ice(vgmstream->codec_data);
|
||||
}
|
||||
|
||||
if (vgmstream->coding_type == coding_UBI_ADPCM) {
|
||||
reset_ubi_adpcm(vgmstream->codec_data);
|
||||
}
|
||||
@ -535,6 +550,9 @@ int get_vgmstream_samples_per_frame(VGMSTREAM* vgmstream) {
|
||||
return 0; /* 1024 - delay/padding (which can be bigger than 1024) */
|
||||
case coding_TAC:
|
||||
return 0; /* 1024 - delay/padding */
|
||||
case coding_ICE_RANGE:
|
||||
case coding_ICE_DCT:
|
||||
return 0; /* ~100 (range), ~16 (DCT) */
|
||||
#if defined(VGM_USE_MP4V2) && defined(VGM_USE_FDKAAC)
|
||||
case coding_MP4_AAC:
|
||||
return ((mp4_aac_codec_data*)vgmstream->codec_data)->samples_per_frame;
|
||||
@ -1075,6 +1093,10 @@ void decode_vgmstream(VGMSTREAM* vgmstream, int samples_written, int samples_to_
|
||||
case coding_TAC:
|
||||
decode_tac(vgmstream, buffer, samples_to_do);
|
||||
break;
|
||||
case coding_ICE_RANGE:
|
||||
case coding_ICE_DCT:
|
||||
decode_ice(vgmstream->codec_data, buffer, samples_to_do);
|
||||
break;
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
case coding_FFmpeg:
|
||||
decode_ffmpeg(vgmstream, buffer, samples_to_do, vgmstream->channels);
|
||||
|
@ -105,6 +105,7 @@ static const char* extension_list[] = {
|
||||
"bgm",
|
||||
"bgw",
|
||||
"bh2pcm",
|
||||
"bigrp",
|
||||
"bik",
|
||||
"bika", //fake extension for .bik (to be removed)
|
||||
"bik2",
|
||||
@ -130,7 +131,7 @@ static const char* extension_list[] = {
|
||||
|
||||
"caf",
|
||||
"cbd2",
|
||||
"ccc",
|
||||
"ccc", //fake extension (to be removed)
|
||||
"cd",
|
||||
"cfn", //fake extension for CAF (renamed, to be removed?)
|
||||
"chd", //txth/reserved [Donkey Konga (GC), Star Fox Assault (GC)]
|
||||
@ -861,6 +862,8 @@ static const coding_info coding_info_list[] = {
|
||||
{coding_RELIC, "Relic Codec"},
|
||||
{coding_CRI_HCA, "CRI HCA"},
|
||||
{coding_TAC, "tri-Ace Codec"},
|
||||
{coding_ICE_RANGE, "Inti Creates Range Codec"},
|
||||
{coding_ICE_DCT, "Inti Creates DCT Codec"},
|
||||
|
||||
#ifdef VGM_USE_VORBIS
|
||||
{coding_OGG_VORBIS, "Ogg Vorbis"},
|
||||
@ -1415,6 +1418,7 @@ static const meta_info meta_info_list[] = {
|
||||
{meta_TT_AD, "Traveller's Tales AUDIO_DATA header"},
|
||||
{meta_SNDZ, "Sony SNDZ header"},
|
||||
{meta_VAB, "Sony VAB header"},
|
||||
{meta_BIGRP, "Inti Creates .BIGRP header"},
|
||||
};
|
||||
|
||||
void get_vgmstream_coding_description(VGMSTREAM* vgmstream, char* out, size_t out_size) {
|
||||
|
@ -83,6 +83,7 @@
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="coding\hca_decoder_clhca.h" />
|
||||
<ClInclude Include="coding\ice_decoder_icelib.h" />
|
||||
<ClInclude Include="coding\mpeg_bitreader.h" />
|
||||
<ClInclude Include="coding\mpeg_decoder.h" />
|
||||
<ClInclude Include="coding\vorbis_bitreader.h" />
|
||||
@ -328,6 +329,7 @@
|
||||
<ClCompile Include="meta\baf.c" />
|
||||
<ClCompile Include="meta\bgw.c" />
|
||||
<ClCompile Include="meta\bik.c" />
|
||||
<ClCompile Include="meta\bigrp.c" />
|
||||
<ClCompile Include="meta\bkhd.c" />
|
||||
<ClCompile Include="meta\bmp_konami.c" />
|
||||
<ClCompile Include="meta\bnk_relic.c" />
|
||||
@ -643,6 +645,8 @@
|
||||
<ClCompile Include="coding\g7221_decoder_lib.c" />
|
||||
<ClCompile Include="coding\hca_decoder.c" />
|
||||
<ClCompile Include="coding\hca_decoder_clhca.c" />
|
||||
<ClCompile Include="coding\ice_decoder.c" />
|
||||
<ClCompile Include="coding\ice_decoder_icelib.c" />
|
||||
<ClCompile Include="coding\ima_decoder.c" />
|
||||
<ClCompile Include="coding\imuse_decoder.c" />
|
||||
<ClCompile Include="coding\l5_555_decoder.c" />
|
||||
|
@ -266,6 +266,9 @@
|
||||
<ClInclude Include="coding\hca_decoder_clhca.h">
|
||||
<Filter>coding\Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="coding\hca_decoder_icelib.h">
|
||||
<Filter>coding\Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="coding\mpeg_bitreader.h">
|
||||
<Filter>coding\Header Files</Filter>
|
||||
</ClInclude>
|
||||
@ -1369,6 +1372,12 @@
|
||||
<ClCompile Include="coding\hca_decoder_clhca.c">
|
||||
<Filter>coding\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="coding\ice_decoder.c">
|
||||
<Filter>coding\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="coding\ice_decoder_icelib.c">
|
||||
<Filter>coding\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="coding\ima_decoder.c">
|
||||
<Filter>coding\Source Files</Filter>
|
||||
</ClCompile>
|
||||
@ -1879,6 +1888,9 @@
|
||||
<ClCompile Include="meta\bik.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\bigrp.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\bkhd.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
|
@ -138,10 +138,8 @@ VGMSTREAM* init_vgmstream_akb(STREAMFILE* sf) {
|
||||
|
||||
/* enable encryption */
|
||||
if (version >= 3 && (flags & 8)) {
|
||||
VGM_LOG("temp1\n");
|
||||
temp_sf = setup_sqex_streamfile(sf, start_offset, stream_size, 1, 0x00, 0x00, "ogg");
|
||||
if (!temp_sf) goto fail;
|
||||
VGM_LOG("temp2\n");
|
||||
|
||||
ogg_vgmstream = init_vgmstream_ogg_vorbis_config(temp_sf, 0x00, &ovmi);
|
||||
close_streamfile(temp_sf);
|
||||
|
@ -78,7 +78,7 @@ VGMSTREAM* init_vgmstream_awb_memory(STREAMFILE* sf, STREAMFILE* sf_acb) {
|
||||
subfile_size = subfile_next - subfile_offset;
|
||||
}
|
||||
|
||||
;VGM_LOG("awb: subfile offset=%x + %x\n", subfile_offset, subfile_size);
|
||||
//;VGM_LOG("awb: subfile offset=%x + %x\n", subfile_offset, subfile_size);
|
||||
|
||||
/* autodetect as there isn't anything, plus can mix types
|
||||
* (waveid<>codec info is usually in the companion .acb) */
|
||||
|
110
src/meta/bigrp.c
Normal file
110
src/meta/bigrp.c
Normal file
@ -0,0 +1,110 @@
|
||||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
#include "../coding/ice_decoder_icelib.h"
|
||||
|
||||
|
||||
/* .BIGRP - from Inti Creates "ICE"/"Imperial" engine [Blaster Master Zero 2 (SW), Gunvolt 3 (SW)] */
|
||||
VGMSTREAM* init_vgmstream_bigrp(STREAMFILE* sf) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
uint32_t header_size, entry_size, stream_size;
|
||||
int total_subsongs, target_subsong = sf->stream_index;
|
||||
int codec, channels, loop_flag, sample_rate;
|
||||
int32_t loop_start, num_samples;
|
||||
|
||||
|
||||
/* checks */
|
||||
|
||||
/* simple checks to avoid opening the lib if possible
|
||||
* early games use smaller sizes [Bloodstained COTM (Vita), Mighty Gunvolt Burst (PC)] */
|
||||
header_size = read_u32le(0x00,sf);
|
||||
if (read_u32le(0x00,sf) != 0x0c && read_u32le(0x00,sf) != 0x10)
|
||||
goto fail;
|
||||
entry_size = read_u32le(0x04,sf);
|
||||
if (entry_size != 0x34 && entry_size != 0x40)
|
||||
goto fail;
|
||||
|
||||
if (!check_extensions(sf, "bigrp"))
|
||||
goto fail;
|
||||
|
||||
if (target_subsong == 0) target_subsong = 1;
|
||||
total_subsongs = read_s32le(0x08,sf);
|
||||
if (target_subsong > total_subsongs || total_subsongs <= 0) goto fail;
|
||||
|
||||
|
||||
/* could read all this from the lib, meh */
|
||||
{
|
||||
uint32_t offset = header_size + entry_size * (target_subsong - 1);
|
||||
|
||||
/* 00: hash */
|
||||
/* 04: group? */
|
||||
codec = read_u32le(offset + 0x08, sf);
|
||||
|
||||
switch(codec) {
|
||||
case 0x00: /* range [BMZ2 (SW), Bloodstained COTM (Vita), BMZ1 (PS4)] */
|
||||
case 0x03:
|
||||
sample_rate = read_s32le(offset + 0x0c, sf);
|
||||
channels = read_u8 (offset + 0x10, sf);
|
||||
/* 0x11: spf */
|
||||
/* 0x12: unknown (volume?) */
|
||||
loop_flag = read_u32le(offset + 0x14, sf);
|
||||
/* 0x18: frame codes */
|
||||
loop_start = read_s32le(offset + 0x1c, sf);
|
||||
stream_size = read_u32le(offset + 0x20, sf); /* intro block */
|
||||
/* 0x24: intro offset */
|
||||
num_samples = read_s32le(offset + 0x28, sf) + loop_start;
|
||||
stream_size = read_u32le(offset + 0x2c, sf) + stream_size; /* body block */
|
||||
/* 0x30: body offset */
|
||||
break;
|
||||
|
||||
default:
|
||||
/* dummy to avoid breaking some files that mix codecs with midi */
|
||||
channels = 1;
|
||||
sample_rate = 48000;
|
||||
loop_flag = 0;
|
||||
loop_start = 0;
|
||||
num_samples = sample_rate;
|
||||
stream_size = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channels, loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->meta_type = meta_BIGRP;
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
vgmstream->num_samples = num_samples;
|
||||
vgmstream->loop_start_sample = loop_start;
|
||||
vgmstream->loop_end_sample = num_samples;
|
||||
vgmstream->num_streams = total_subsongs;
|
||||
vgmstream->stream_size = stream_size;
|
||||
|
||||
switch(codec) {
|
||||
case 0x00:
|
||||
case 0x03:
|
||||
vgmstream->codec_data = init_ice(sf, target_subsong);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->coding_type = codec == 0x00 ? coding_ICE_RANGE : coding_ICE_DCT;
|
||||
vgmstream->layout_type = layout_none;
|
||||
break;
|
||||
|
||||
case 0x01:
|
||||
case 0x02:
|
||||
vgmstream->coding_type = coding_SILENCE;
|
||||
snprintf(vgmstream->stream_name, STREAM_NAME_SIZE, "[%s]", (codec == 0x01 ? "data" : "midi"));
|
||||
break;
|
||||
|
||||
default:
|
||||
goto fail;
|
||||
}
|
||||
|
||||
//if (!vgmstream_open_stream(vgmstream, sf, 0x00))
|
||||
// goto fail;
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
@ -991,4 +991,6 @@ VGMSTREAM* init_vgmstream_sndz(STREAMFILE* sf);
|
||||
|
||||
VGMSTREAM* init_vgmstream_vab(STREAMFILE* sf);
|
||||
|
||||
VGMSTREAM* init_vgmstream_bigrp(STREAMFILE* sf);
|
||||
|
||||
#endif /*_META_H*/
|
||||
|
@ -33,10 +33,10 @@ VGMSTREAM * init_vgmstream_ps2_ccc(STREAMFILE *streamFile) {
|
||||
vgmstream->channels = channel_count;
|
||||
vgmstream->sample_rate = read_32bitLE(0x04,streamFile);
|
||||
vgmstream->coding_type = coding_PSX;
|
||||
vgmstream->num_samples = (read_32bitLE(0x08,streamFile))/channel_count/32*28;
|
||||
vgmstream->num_samples = (read_32bitLE(0x08,streamFile))/channel_count/0x10*28;
|
||||
if (loop_flag) {
|
||||
vgmstream->loop_start_sample = 0;
|
||||
vgmstream->loop_end_sample = (read_32bitLE(0x08,streamFile))/channel_count/32*28;
|
||||
vgmstream->loop_end_sample = (read_32bitLE(0x08,streamFile))/channel_count/0x10*28;
|
||||
}
|
||||
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
|
@ -1,5 +1,6 @@
|
||||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
#include "../util/endianness.h"
|
||||
#include "sqex_streamfile.h"
|
||||
|
||||
|
||||
@ -20,65 +21,67 @@ typedef struct {
|
||||
int sample_rate;
|
||||
int loop_start;
|
||||
int loop_end;
|
||||
off_t mtrl_offset;
|
||||
off_t extradata_offset;
|
||||
size_t extradata_size;
|
||||
size_t stream_size;
|
||||
uint32_t mtrl_offset;
|
||||
uint32_t extradata_offset;
|
||||
uint32_t extradata_size;
|
||||
uint32_t stream_size;
|
||||
uint16_t extradata_id;
|
||||
|
||||
off_t filename_offset;
|
||||
size_t filename_size;
|
||||
off_t muscname_offset;
|
||||
size_t muscname_size;
|
||||
off_t sectname_offset;
|
||||
size_t sectname_size;
|
||||
off_t modename_offset;
|
||||
size_t modename_size;
|
||||
off_t instname_offset;
|
||||
size_t instname_size;
|
||||
off_t sndname_offset;
|
||||
size_t sndname_size;
|
||||
uint32_t filename_offset;
|
||||
uint32_t filename_size;
|
||||
uint32_t muscname_offset;
|
||||
uint32_t muscname_size;
|
||||
uint32_t sectname_offset;
|
||||
uint32_t sectname_size;
|
||||
uint32_t modename_offset;
|
||||
uint32_t modename_size;
|
||||
uint32_t instname_offset;
|
||||
uint32_t instname_size;
|
||||
//uint32_t sndname_offset;
|
||||
//uint32_t sndname_size;
|
||||
|
||||
off_t sections_offset;
|
||||
off_t snd_section_offset;
|
||||
off_t seq_section_offset;
|
||||
off_t trk_section_offset;
|
||||
off_t musc_section_offset;
|
||||
off_t inst_section_offset;
|
||||
off_t mtrl_section_offset;
|
||||
uint32_t sections_offset;
|
||||
uint32_t snd_section_offset;
|
||||
uint32_t seq_section_offset;
|
||||
uint32_t trk_section_offset;
|
||||
uint32_t musc_section_offset;
|
||||
uint32_t inst_section_offset;
|
||||
uint32_t mtrl_section_offset;
|
||||
|
||||
char readable_name[STREAM_NAME_SIZE];
|
||||
|
||||
} sead_header;
|
||||
} sead_header_t;
|
||||
|
||||
static int parse_sead(sead_header *sead, STREAMFILE *sf);
|
||||
static int parse_sead(sead_header_t* sead, STREAMFILE* sf);
|
||||
|
||||
|
||||
/* SABF/MABF - Square Enix's "sead" audio games [Dragon Quest Builders (PS3), Dissidia Opera Omnia (mobile), FF XV (PS4)] */
|
||||
VGMSTREAM* init_vgmstream_sqex_sead(STREAMFILE* sf) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
sead_header sead = {0};
|
||||
sead_header_t sead = {0};
|
||||
off_t start_offset;
|
||||
int target_subsong = sf->stream_index;
|
||||
uint32_t (*read_u32)(off_t,STREAMFILE*) = NULL;
|
||||
uint16_t (*read_u16)(off_t,STREAMFILE*) = NULL;
|
||||
read_u32_t read_u32 = NULL;
|
||||
read_u16_t read_u16 = NULL;
|
||||
|
||||
|
||||
/* checks */
|
||||
if (is_id32be(0x00, sf, "sabf")) {
|
||||
sead.is_sab = 1;
|
||||
}
|
||||
else if (is_id32be(0x00, sf, "mabf")) {
|
||||
sead.is_mab = 1;
|
||||
}
|
||||
else {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* .sab: sound/bgm
|
||||
* .mab: music
|
||||
* .sbin: Dissidia Opera Omnia .sab */
|
||||
if (!check_extensions(sf,"sab,mab,sbin"))
|
||||
goto fail;
|
||||
|
||||
if (read_u32be(0x00, sf) == 0x73616266) { /* "sabf" */
|
||||
sead.is_sab = 1;
|
||||
} else if (read_u32be(0x00, sf) == 0x6D616266) { /* "mabf" */
|
||||
sead.is_mab = 1;
|
||||
} else {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* SEAD handles both sab/mab in the same lib (libsead), and other similar files (config, engine, etc).
|
||||
* Has some chunks pointing to sections, and each section entry (usually starting with section
|
||||
* version/reserved/size) is always padded to 0x10. Most values are unsigned.
|
||||
@ -89,7 +92,7 @@ VGMSTREAM* init_vgmstream_sqex_sead(STREAMFILE* sf) {
|
||||
* or is a separate audio lib derived from this "SEAD Engine". */
|
||||
|
||||
|
||||
sead.big_endian = guess_endianness16bit(0x06, sf); /* no flag, use size */
|
||||
sead.big_endian = guess_endian16(0x06, sf); /* no flag, use size */
|
||||
if (sead.big_endian) {
|
||||
read_u32 = read_u32be;
|
||||
read_u16 = read_u16be;
|
||||
@ -115,6 +118,14 @@ VGMSTREAM* init_vgmstream_sqex_sead(STREAMFILE* sf) {
|
||||
strcpy(vgmstream->stream_name, sead.readable_name);
|
||||
|
||||
switch(sead.codec) {
|
||||
case 0x00: /* NONE */
|
||||
vgmstream->coding_type = coding_SILENCE;
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
||||
vgmstream->num_samples = sead.sample_rate;
|
||||
|
||||
start_offset = 0;
|
||||
break;
|
||||
|
||||
case 0x01: { /* PCM [Chrono Trigger (PC) sfx] */
|
||||
start_offset = sead.extradata_offset + sead.extradata_size;
|
||||
@ -157,7 +168,7 @@ VGMSTREAM* init_vgmstream_sqex_sead(STREAMFILE* sf) {
|
||||
|
||||
#ifdef VGM_USE_VORBIS
|
||||
case 0x03: { /* VORBIS (Ogg subfile) [Final Fantasy XV Benchmark sfx (PC)] */
|
||||
VGMSTREAM *ogg_vgmstream = NULL;
|
||||
VGMSTREAM* ogg_vgmstream = NULL;
|
||||
ogg_vorbis_meta_info_t ovmi = {0};
|
||||
off_t subfile_offset = sead.extradata_offset + sead.extradata_size;
|
||||
|
||||
@ -254,8 +265,8 @@ VGMSTREAM* init_vgmstream_sqex_sead(STREAMFILE* sf) {
|
||||
#endif
|
||||
|
||||
case 0x07: { /* HCA (subfile) [Dissidia Opera Omnia (Mobile), Final Fantaxy XV (PS4)] */
|
||||
VGMSTREAM *temp_vgmstream = NULL;
|
||||
STREAMFILE *temp_sf = NULL;
|
||||
VGMSTREAM* temp_vgmstream = NULL;
|
||||
STREAMFILE* temp_sf = NULL;
|
||||
off_t subfile_offset = sead.extradata_offset + 0x10;
|
||||
size_t subfile_size = sead.stream_size + sead.extradata_size - 0x10;
|
||||
|
||||
@ -299,11 +310,10 @@ VGMSTREAM* init_vgmstream_sqex_sead(STREAMFILE* sf) {
|
||||
}
|
||||
}
|
||||
|
||||
case 0x00: /* NONE / dummy entry */
|
||||
case 0x05: /* XMA2 (extradata may be a XMA2 fmt extra chunk) */
|
||||
case 0x08: /* SWITCHOPUS (no extradata?) */
|
||||
default:
|
||||
VGM_LOG("SQEX SEAD: unknown codec %x\n", sead.codec);
|
||||
vgm_logi("SQEX SEAD: unknown codec %x\n", sead.codec);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
@ -319,54 +329,76 @@ fail:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//todo safeops, avoid recalc lens
|
||||
static void sead_cat(char* dst, int dst_max, const char* src) {
|
||||
int dst_len = strlen(dst);
|
||||
int src_len = strlen(dst);
|
||||
if (dst_len + src_len > dst_max - 1)
|
||||
return;
|
||||
strcat(dst, src);
|
||||
}
|
||||
|
||||
static void build_readable_name(char * buf, size_t buf_size, sead_header *sead, STREAMFILE *sf) {
|
||||
static void build_readable_sab_name(sead_header_t* sead, STREAMFILE* sf, uint32_t sndname_offset, uint32_t sndname_size) {
|
||||
char * buf = sead->readable_name;
|
||||
int buf_size = sizeof(sead->readable_name);
|
||||
char descriptor[255], name[255];
|
||||
|
||||
if (sead->is_sab) {
|
||||
char descriptor[255], name[255];
|
||||
if (sead->filename_size > 255 || sndname_size > 255)
|
||||
goto fail;
|
||||
|
||||
if (sead->filename_size > 255 || sead->sndname_size > 255) goto fail;
|
||||
|
||||
read_string(descriptor,sead->filename_size+1,sead->filename_offset, sf);
|
||||
read_string(name,sead->sndname_size+1,sead->sndname_offset, sf);
|
||||
if (buf[0] == '\0') { /* init */
|
||||
read_string(descriptor,sead->filename_size+1, sead->filename_offset, sf);
|
||||
read_string(name, sndname_size+1, sndname_offset, sf);
|
||||
|
||||
snprintf(buf,buf_size, "%s/%s", descriptor, name);
|
||||
}
|
||||
else {
|
||||
char descriptor[255], name[255], mode[255];
|
||||
else { /* add */
|
||||
read_string(name, sndname_size+1, sndname_offset, sf);
|
||||
|
||||
if (sead->filename_size > 255 || sead->muscname_size > 255 || sead->sectname_size > 255 || sead->modename_size > 255) goto fail;
|
||||
|
||||
read_string(descriptor,sead->filename_size+1,sead->filename_offset, sf);
|
||||
//read_string(filename,sead->muscname_size+1,sead->muscname_offset, sf); /* same as filename, not too interesting */
|
||||
if (sead->sectname_offset)
|
||||
read_string(name,sead->sectname_size+1,sead->sectname_offset, sf);
|
||||
else if (sead->instname_offset)
|
||||
read_string(name,sead->instname_size+1,sead->instname_offset, sf);
|
||||
else
|
||||
strcpy(name, "?");
|
||||
if (sead->modename_offset > 0)
|
||||
read_string(mode,sead->modename_size+1,sead->modename_offset, sf);
|
||||
|
||||
/* default mode in most files */
|
||||
if (sead->modename_offset == 0 || strcmp(mode, "Mode") == 0 || strcmp(mode, "Mode0") == 0)
|
||||
snprintf(buf,buf_size, "%s/%s", descriptor, name);
|
||||
else
|
||||
snprintf(buf,buf_size, "%s/%s/%s", descriptor, name, mode);
|
||||
sead_cat(buf, buf_size, "; ");
|
||||
sead_cat(buf, buf_size, name);
|
||||
}
|
||||
return;
|
||||
fail:
|
||||
VGM_LOG("SEAD: bad sab name found\n");
|
||||
}
|
||||
|
||||
static void build_readable_mab_name(sead_header_t* sead, STREAMFILE* sf) {
|
||||
char * buf = sead->readable_name;
|
||||
int buf_size = sizeof(sead->readable_name);
|
||||
char descriptor[255], name[255], mode[255];
|
||||
|
||||
if (sead->filename_size > 255 || sead->muscname_size > 255 || sead->sectname_size > 255 || sead->modename_size > 255)
|
||||
goto fail;
|
||||
|
||||
read_string(descriptor,sead->filename_size+1,sead->filename_offset, sf);
|
||||
//read_string(filename,sead->muscname_size+1,sead->muscname_offset, sf); /* same as filename, not too interesting */
|
||||
if (sead->sectname_offset)
|
||||
read_string(name,sead->sectname_size+1,sead->sectname_offset, sf);
|
||||
else if (sead->instname_offset)
|
||||
read_string(name,sead->instname_size+1,sead->instname_offset, sf);
|
||||
else
|
||||
strcpy(name, "?");
|
||||
if (sead->modename_offset > 0)
|
||||
read_string(mode,sead->modename_size+1,sead->modename_offset, sf);
|
||||
|
||||
/* default mode in most files */
|
||||
if (sead->modename_offset == 0 || strcmp(mode, "Mode") == 0 || strcmp(mode, "Mode0") == 0)
|
||||
snprintf(buf,buf_size, "%s/%s", descriptor, name);
|
||||
else
|
||||
snprintf(buf,buf_size, "%s/%s/%s", descriptor, name, mode);
|
||||
|
||||
return;
|
||||
fail:
|
||||
VGM_LOG("SEAD: bad name found\n");
|
||||
VGM_LOG("SEAD: bad mab name found\n");
|
||||
}
|
||||
|
||||
static void parse_sead_mab_name(sead_header *sead, STREAMFILE *sf) {
|
||||
uint32_t (*read_u32)(off_t,STREAMFILE*) = sead->big_endian ? read_u32be : read_u32le;
|
||||
uint16_t (*read_u16)(off_t,STREAMFILE*) = sead->big_endian ? read_u16be : read_u16le;
|
||||
static void parse_sead_mab_name(sead_header_t* sead, STREAMFILE* sf) {
|
||||
read_u32_t read_u32 = sead->big_endian ? read_u32be : read_u32le;
|
||||
read_u16_t read_u16 = sead->big_endian ? read_u16be : read_u16le;
|
||||
int i, j, k, entries;
|
||||
off_t target_muscname_offset = 0, target_sectname_offset = 0;
|
||||
size_t target_muscname_size = 0, target_sectname_size = 0;
|
||||
|
||||
uint32_t target_muscname_offset = 0, target_sectname_offset = 0;
|
||||
uint32_t target_muscname_size = 0, target_sectname_size = 0;
|
||||
|
||||
/* find names referencing to our material stream, usually:
|
||||
* - music > sections > layers (<> meters) > material index
|
||||
@ -608,9 +640,9 @@ static void parse_sead_mab_name(sead_header *sead, STREAMFILE *sf) {
|
||||
}
|
||||
}
|
||||
|
||||
static void parse_sead_sab_name(sead_header *sead, STREAMFILE *sf) {
|
||||
uint32_t (*read_u32)(off_t,STREAMFILE*) = sead->big_endian ? read_u32be : read_u32le;
|
||||
uint16_t (*read_u16)(off_t,STREAMFILE*) = sead->big_endian ? read_u16be : read_u16le;
|
||||
static void parse_sead_sab_name(sead_header_t* sead, STREAMFILE* sf) {
|
||||
read_u32_t read_u32 = sead->big_endian ? read_u32be : read_u32le;
|
||||
read_u16_t read_u16 = sead->big_endian ? read_u16be : read_u16le;
|
||||
int i, j, entries;
|
||||
|
||||
/* find names referencing to our material stream, usually:
|
||||
@ -627,9 +659,9 @@ static void parse_sead_sab_name(sead_header *sead, STREAMFILE *sf) {
|
||||
/* parse sounds */
|
||||
entries = read_u16(sead->snd_section_offset + 0x04, sf);
|
||||
for (i = 0; i < entries; i++) {
|
||||
off_t snd_offset = sead->snd_section_offset + read_u32(sead->snd_section_offset + 0x10 + i*0x04, sf);
|
||||
size_t snd_size, sndname_size;
|
||||
off_t seqi_start, seqi_offset, sndname_offset;
|
||||
uint32_t snd_offset = sead->snd_section_offset + read_u32(sead->snd_section_offset + 0x10 + i*0x04, sf);
|
||||
uint32_t snd_size, sndname_size;
|
||||
uint32_t seqi_start, seqi_offset, sndname_offset;
|
||||
int snd_version, seqi_count;
|
||||
|
||||
snd_version = read_u8(snd_offset + 0x00, sf);
|
||||
@ -677,8 +709,8 @@ static void parse_sead_sab_name(sead_header *sead, STREAMFILE *sf) {
|
||||
|
||||
/* parse sequence */
|
||||
{
|
||||
off_t seq_offset = sead->seq_section_offset + read_u32(sead->seq_section_offset + 0x10 + seq_index*0x04, sf);
|
||||
off_t cmnd_start, cmnd_offset;
|
||||
uint32_t seq_offset = sead->seq_section_offset + read_u32(sead->seq_section_offset + 0x10 + seq_index*0x04, sf);
|
||||
uint32_t cmnd_start, cmnd_offset;
|
||||
int seq_version;
|
||||
|
||||
seq_version = read_u8(seq_offset + 0x00, sf);
|
||||
@ -724,7 +756,7 @@ static void parse_sead_sab_name(sead_header *sead, STREAMFILE *sf) {
|
||||
|
||||
/* parse track */
|
||||
{
|
||||
off_t trk_offset = sead->trk_section_offset + read_u32(sead->trk_section_offset + 0x10 + trk_index*0x04, sf);
|
||||
uint32_t trk_offset = sead->trk_section_offset + read_u32(sead->trk_section_offset + 0x10 + trk_index*0x04, sf);
|
||||
uint8_t trk_type;
|
||||
uint16_t mtrl_index;
|
||||
|
||||
@ -743,8 +775,9 @@ static void parse_sead_sab_name(sead_header *sead, STREAMFILE *sf) {
|
||||
|
||||
/* assumes same bank, not sure if bank info is even inside .sab */
|
||||
if (mtrl_index == sead->mtrl_index) {
|
||||
sead->sndname_offset = sndname_offset;
|
||||
sead->sndname_size = sndname_size;
|
||||
build_readable_sab_name(sead, sf, sndname_offset, sndname_size);
|
||||
//sead->sndname_offset = sndname_offset;
|
||||
//sead->sndname_size = sndname_size;
|
||||
}
|
||||
}
|
||||
else if (trk_type == 0x02) {
|
||||
@ -760,8 +793,10 @@ static void parse_sead_sab_name(sead_header *sead, STREAMFILE *sf) {
|
||||
/* commands normally end when a type 0=none is found */
|
||||
if (cmnd_type <= 0x00 || cmnd_type > 0x08)
|
||||
break;
|
||||
if (sead->sndname_offset > 0)
|
||||
break;
|
||||
|
||||
/* keep reading names as N sounds may point to current material */
|
||||
//if (sead->sndname_offset > 0)
|
||||
// break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -770,9 +805,9 @@ static void parse_sead_sab_name(sead_header *sead, STREAMFILE *sf) {
|
||||
}
|
||||
|
||||
|
||||
static int parse_sead(sead_header* sead, STREAMFILE* sf) {
|
||||
uint32_t (*read_u32)(off_t,STREAMFILE*) = sead->big_endian ? read_u32be : read_u32le;
|
||||
uint16_t (*read_u16)(off_t,STREAMFILE*) = sead->big_endian ? read_u16be : read_u16le;
|
||||
static int parse_sead(sead_header_t* sead, STREAMFILE* sf) {
|
||||
read_u32_t read_u32 = sead->big_endian ? read_u32be : read_u32le;
|
||||
read_u16_t read_u16 = sead->big_endian ? read_u16be : read_u16le;
|
||||
|
||||
/** base header **/
|
||||
/* 0x00: id */
|
||||
@ -849,9 +884,9 @@ static int parse_sead(sead_header* sead, STREAMFILE* sf) {
|
||||
for (i = 0; i < entries; i++) {
|
||||
off_t entry_offset = sead->mtrl_section_offset + read_u32(sead->mtrl_section_offset + 0x10 + i*0x04, sf);
|
||||
|
||||
if (read_u8(entry_offset + 0x05, sf) == 0) {
|
||||
continue; /* codec 0 when dummy (see stream header) */
|
||||
}
|
||||
//if (read_u8(entry_offset + 0x05, sf) == 0) {
|
||||
// continue; /* codec 0 when dummy (see stream header) */
|
||||
//}
|
||||
|
||||
sead->total_subsongs++;
|
||||
if (!sead->mtrl_offset && sead->total_subsongs == sead->target_subsong) {
|
||||
@ -882,17 +917,24 @@ static int parse_sead(sead_header* sead, STREAMFILE* sf) {
|
||||
sead->extradata_id = read_u16(sead->mtrl_offset + 0x1c, sf);
|
||||
/* 0x1e: reserved */
|
||||
|
||||
if (sead->codec == 0x00) {
|
||||
/* dummy entries have null fields, put defaults to allow playing them */
|
||||
sead->channels = 1;
|
||||
sead->sample_rate = 48000;
|
||||
}
|
||||
|
||||
sead->loop_flag = (sead->loop_end > 0);
|
||||
sead->extradata_offset = sead->mtrl_offset + 0x20;
|
||||
|
||||
if (sead->is_sab) {
|
||||
parse_sead_sab_name(sead, sf);
|
||||
/* sab name is added during process */
|
||||
}
|
||||
else if (sead->is_mab) {
|
||||
parse_sead_mab_name(sead, sf);
|
||||
build_readable_mab_name(sead, sf);
|
||||
}
|
||||
|
||||
build_readable_name(sead->readable_name, sizeof(sead->readable_name), sead, sf);
|
||||
|
||||
return 1;
|
||||
fail:
|
||||
|
@ -406,7 +406,6 @@ int vgmstream_tags_next_tag(VGMSTREAM_TAGS* tags, STREAMFILE* tagfile) {
|
||||
}
|
||||
else if (strncasecmp(tags->key, "EXACTMATCH", key_len) == 0) {
|
||||
tags->exact_match = 1;
|
||||
VGM_LOG("exact\n");
|
||||
}
|
||||
|
||||
continue; /* not an actual tag */
|
||||
|
@ -129,7 +129,6 @@ VGMSTREAM* (*init_vgmstream_functions[])(STREAMFILE* sf) = {
|
||||
init_vgmstream_idsp_ie,
|
||||
init_vgmstream_ngc_ymf,
|
||||
init_vgmstream_sadl,
|
||||
init_vgmstream_ps2_ccc,
|
||||
init_vgmstream_fag,
|
||||
init_vgmstream_ps2_mihb,
|
||||
init_vgmstream_ngc_pdt_split,
|
||||
@ -526,6 +525,7 @@ VGMSTREAM* (*init_vgmstream_functions[])(STREAMFILE* sf) = {
|
||||
init_vgmstream_bw_riff_mp3,
|
||||
init_vgmstream_sndz,
|
||||
init_vgmstream_vab,
|
||||
init_vgmstream_bigrp,
|
||||
|
||||
/* lower priority metas (no clean header identity, somewhat ambiguous, or need extension/companion file to identify) */
|
||||
init_vgmstream_agsc,
|
||||
@ -555,6 +555,7 @@ VGMSTREAM* (*init_vgmstream_functions[])(STREAMFILE* sf) = {
|
||||
init_vgmstream_ngc_ulw, /* .ulw raw u-Law */
|
||||
init_vgmstream_exakt_sc, /* .sc raw PCM */
|
||||
init_vgmstream_zwdsp, /* fake format */
|
||||
init_vgmstream_ps2_ccc, /* fake format, to be removed */
|
||||
init_vgmstream_ps2_adm, /* weird non-constant PSX blocks */
|
||||
init_vgmstream_baf_badrip, /* crap, to be removed */
|
||||
init_vgmstream_rxws_badrip, /* crap, to be removed */
|
||||
|
@ -7,7 +7,10 @@
|
||||
|
||||
/* reasonable limits */
|
||||
enum {
|
||||
PATH_LIMIT = 32768,
|
||||
/* Windows generally only allows 260 chars in path, but other OSs have higher limits, and we handle
|
||||
* UTF-8 (that typically uses 2-bytes for common non-latin codepages) plus player may append protocols
|
||||
* to paths, so it should be a bit higher. Most people wouldn't use huge paths though. */
|
||||
PATH_LIMIT = 4096, /* (256 * 8) * 2 = ~max_path * (other_os+extra) * codepage_bytes */
|
||||
STREAM_NAME_SIZE = 255,
|
||||
VGMSTREAM_MAX_CHANNELS = 64,
|
||||
VGMSTREAM_MIN_SAMPLE_RATE = 300, /* 300 is Wwise min */
|
||||
@ -182,6 +185,8 @@ typedef enum {
|
||||
coding_RELIC, /* Relic Codec (DCT-based) */
|
||||
coding_CRI_HCA, /* CRI High Compression Audio (MDCT-based) */
|
||||
coding_TAC, /* tri-Ace Codec (MDCT-based) */
|
||||
coding_ICE_RANGE, /* Inti Creates "range" codec */
|
||||
coding_ICE_DCT, /* Inti Creates "DCT" codec */
|
||||
|
||||
#ifdef VGM_USE_VORBIS
|
||||
coding_OGG_VORBIS, /* Xiph Vorbis with Ogg layer (MDCT-based) */
|
||||
@ -762,6 +767,7 @@ typedef enum {
|
||||
meta_TT_AD,
|
||||
meta_SNDZ,
|
||||
meta_VAB,
|
||||
meta_BIGRP,
|
||||
|
||||
} meta_t;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user