Add .bigrp [BM Zero 2 (SW), Gunvolt 3 (SW)]

This commit is contained in:
bnnm 2022-09-23 22:53:45 +02:00
parent 791a7de02a
commit cbd831e658
13 changed files with 1807 additions and 0 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

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

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

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

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

View File

@ -185,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) */
@ -765,6 +767,7 @@ typedef enum {
meta_TT_AD,
meta_SNDZ,
meta_VAB,
meta_BIGRP,
} meta_t;