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:
bnnm 2022-09-24 00:15:30 +02:00 committed by GitHub
commit 24f5ee6500
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 2005 additions and 158 deletions

View File

@ -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

View File

@ -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

View File

@ -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
View 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;
}

File diff suppressed because it is too large Load Diff

View 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

View File

@ -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) */
put_u8 (buf+0x00, 0x03); /* packet_type (comments) */
memcpy (buf+0x01, "vorbis", 6); /* id */
put_32bitLE(buf+0x07, 0x09); /* vendor_length */
put_u32le(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_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

View File

@ -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 */

View File

@ -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);

View File

@ -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) {

View File

@ -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" />

View File

@ -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>

View File

@ -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);

View File

@ -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
View 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;
}

View File

@ -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*/

View File

@ -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;

View File

@ -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;
@ -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,23 +329,47 @@ 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) {
if (sead->is_sab) {
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->filename_size > 255 || sead->sndname_size > 255) goto fail;
if (sead->filename_size > 255 || sndname_size > 255)
goto fail;
if (buf[0] == '\0') { /* init */
read_string(descriptor,sead->filename_size+1, sead->filename_offset, sf);
read_string(name,sead->sndname_size+1,sead->sndname_offset, sf);
read_string(name, sndname_size+1, sndname_offset, sf);
snprintf(buf,buf_size, "%s/%s", descriptor, name);
}
else {
else { /* add */
read_string(name, sndname_size+1, sndname_offset, sf);
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;
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 */
@ -353,20 +387,18 @@ static void build_readable_name(char * buf, size_t buf_size, sead_header *sead,
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:

View File

@ -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 */

View File

@ -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 */

View File

@ -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;