vgmstream/src/coding/ice_decoder_icelib.c

1434 lines
44 KiB
C

/* Decodes Inti Creates' BIGRP files with custom codecs, found in games using their current
* "Inti Creates Engine" ("ICE") / "Imperial Engine" ('IMP'?). Engine's name is said to be
* the latter (ICE being the earlier iteration) but debug info still shows the former.
* Reverse engineered from various exes (if you use this as a base credit it, please).
*
* This code tries to follow the original closely for documentation purposes, with some extra
* error control (original doesn't check zlib errors or buf sizes) plus a few extra structs/functions
* that were likely inline'd (such as bitreaders). */
//TODO change to streaming decoder
// Currently lib expects most data in memory. Due to how format is designed it's not the
// easiest thing to change, to be fixed it later:
// - data is divided into 2 blocks (intro+body) that are decoded separatedly
// (streaming should read up to block max)
// - code data isn't divided into frames, just keeps reading from the file buf
// - "range" decoder has linear data, and should be easy enough to stream, but it's rarely used.
// - "dct" decoder has a big chunk (+30%) of codebook data at the beginning of each block, then
// code data *but* decoder reads simultaneously from both places. Files can be rather big
// (2 mins = 6mb, codebooks = ~1.5mb). Would need to pre-read all the codebooks (still big) then
// stream data, or seek around to codebooks (thrashes FILE buffers).
#include <math.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include "ice_decoder_icelib.h"
/* use miniz (API-compatible) to avoid adding external zlib just for this codec
* - https://github.com/richgel999/miniz */
#include "../util/miniz.h"
//#include "zlib.h"
#define ICESND_MAX_CHANNELS 2
/* ************************************************************ */
/* COMMON */
/* ************************************************************ */
static inline uint8_t get_u8(const uint8_t* p) {
uint8_t ret;
ret = ((uint16_t)(const uint8_t)p[0]) << 0;
return ret;
}
static inline uint16_t get_u16le(const uint8_t* p) {
uint16_t ret;
ret = ((uint16_t)(const uint8_t)p[0]) << 0;
ret |= ((uint16_t)(const uint8_t)p[1]) << 8;
return ret;
}
static inline uint32_t get_u32le(const uint8_t* p) {
uint32_t ret;
ret = ((uint32_t)(const uint8_t)p[0]) << 0;
ret |= ((uint32_t)(const uint8_t)p[1]) << 8;
ret |= ((uint32_t)(const uint8_t)p[2]) << 16;
ret |= ((uint32_t)(const uint8_t)p[3]) << 24;
return ret;
}
/* bigrp entry info as read from header */
typedef struct {
uint32_t hash1; /* usually matches filename, different files vary on bytes, seems internally used to identify files */
uint32_t hash2; /* group id? repeated in several files, doesn't seem used */
uint32_t codec; /* 00: range, 01: metadata, 02: midi, 03: DCT */
/* rest of header varies per codec (padded until ~0x40 for all) */
/* codec 01 entry: */
/* - offset */
/* - size */
/* codec 02 entry: */
/* - config? (big value, same for all entries) */
/* - midi offset */
/* - midi size */
/* - midi config? (~0x30C0, possibly some size) */
/* - midi config? (~0x4036, then divided by 180.0) */
/* codec 00/03 entry */
int sample_rate;
int channels;
int spf; /* always 16, internally used for pcm size calculations */
int unknown; /* some kind of low-ish value, volume? (seen 0x40~0x7F) */
int loop_flag;
int frame_codes; /* 0x64 in codec 00, 0x00 in codec 03 (possibly "RangeBlockSize") */
/* has one "intro" block then one "body" block; intro block may be zero in full loops/no loops */
uint32_t intro_samples;
uint32_t intro_zsize;
uint32_t intro_offset;
uint32_t body_samples;
uint32_t body_zsize;
uint32_t body_offset;
/* rest: padding until entry_size */
} bigrp_entry_t;
/* base bigrp header and extra config */
typedef struct {
uint32_t head_size;
uint32_t entry_size;
int total_subsongs;
uint32_t dummy;
} bigrp_header_t;
/* block/data format (handled later):
*
* codec 00 (range):
* - zlib block (32b deflated size + zlib data)
*
* codec 03 (dct)
* - codeinfo block (see codeinfo_parse)
* - zlib tables
* - data
*
* codec 01 (data?)
* - ?
* Not parsed as audio in the IcePlayer init. Usually paired with midis, has several sections and some
* point to entries using hash1, so probably soundfont config, but not all bigrp with midis have this.
*
* codec 02 (midi)
* - standard midi (MThd)
*/
/* OG code casts buffer to this struct, read in a more portable fashion */
static int bigrp_entry_parse(bigrp_entry_t* entry, const uint8_t* buf, int buf_size) {
if (buf_size < 0x34)
goto fail;
entry->hash1 = get_u32le(buf + 0x00);
entry->hash2 = get_u32le(buf + 0x04);
entry->codec = get_u32le(buf + 0x08); /* u8 in decoder, read fully to validate */
switch (entry->codec) {
case ICESND_CODEC_RANGE:
case ICESND_CODEC_DCT:
entry->sample_rate = get_u32le(buf + 0x0c);
entry->channels = get_u8 (buf + 0x10);
entry->spf = get_u8 (buf + 0x11);
entry->unknown = get_u16le(buf + 0x12);
entry->loop_flag = get_u32le(buf + 0x14); /* u8 in decoder, read fully to validate */
entry->frame_codes = get_u32le(buf + 0x18); /* codec 00 only (includes N channels) */
entry->intro_samples = get_u32le(buf + 0x1c);
entry->intro_zsize = get_u32le(buf + 0x20);
entry->intro_offset = get_u32le(buf + 0x24);
entry->body_samples = get_u32le(buf + 0x28);
entry->body_zsize = get_u32le(buf + 0x2c);
entry->body_offset = get_u32le(buf + 0x30);
if (entry->sample_rate < 2000 || entry->sample_rate > 48000) /* just in case */
goto fail;
if (entry->channels < 1 || entry->channels > 2 || entry->spf != 16) /* not seen */
goto fail;
if (entry->frame_codes != 0 && entry->frame_codes != 0x64)
goto fail;
if (entry->frame_codes % entry->channels != 0) /* assumed that N codes = N/chs samples */
goto fail;
if (entry->loop_flag != 0 && entry->loop_flag != 1)
goto fail;
/* probably wouldn't matter, same with other sizes */
if (entry->intro_samples == 0 && entry->body_samples == 0)
goto fail;
if (entry->channels > 1 && entry->codec == ICESND_CODEC_RANGE) /* not seen */
goto fail;
break;
case ICESND_CODEC_DATA:
case ICESND_CODEC_MIDI:
default:
goto fail;
}
return ICESND_RESULT_OK;
fail:
return ICESND_ERROR_HEADER;
}
/* read main .bigrp header. Earlier games used standard Nintendo's BFGRP and BCGRP,
* and this format seems kind of inspired by it, so presumably BIGRP = Binary Inti Group */
static int bigrp_header_parse(bigrp_header_t* hdr, const uint8_t* buf, int buf_size, int subsong) {
if (buf_size < 0x0c)
goto fail;
/* read base header */
hdr->head_size = get_u32le(buf + 0x00);
hdr->entry_size = get_u32le(buf + 0x04);
hdr->total_subsongs = get_u32le(buf + 0x08);
if (hdr->head_size > buf_size)
goto fail;
if (hdr->head_size >= 0x10)
hdr->dummy = get_u32le(buf + 0x0c);
else
hdr->dummy = 0x00;
/* 0x0c: Bloodstained COTM (Vita/3DS), Mighty Gunvolt Burst (PC); 0x10: rest */
if (hdr->head_size != 0x0c && hdr->head_size != 0x10)
goto fail;
/* same (no changes, after 0x34 is padding) */
if (hdr->entry_size != 0x34 && hdr->entry_size != 0x40)
goto fail;
if (hdr->dummy != 0x00)
goto fail;
if (subsong < 1 || subsong > hdr->total_subsongs)
goto fail;
return ICESND_RESULT_OK;
fail:
return ICESND_ERROR_HEADER;
}
/* original also tries to call's zlib free is needed (not set though) */
static void zlib_end(z_stream* strm, int* p_zlib_init) {
if (*p_zlib_init) {
inflateEnd(strm);
/* original also tries to call's zlib free if needed (not set though) */
*p_zlib_init = 0;
}
}
static int zlib_init(z_stream* strm, int* p_zlib_init, const uint8_t* buf, int buf_size) {
int err;
zlib_end(strm, p_zlib_init);
strm->zalloc = 0;
strm->zfree = 0;
strm->opaque = 0;
strm->next_in = 0;
strm->avail_in = 0;
err = inflateInit(strm);
if (err < 0) return ICESND_ERROR_SETUP;
*p_zlib_init = 1;
/* zlib data starts with the decompressed size */
strm->next_in = buf + 0x04;
strm->avail_in = buf_size - 0x04;
return ICESND_RESULT_OK;
}
/* ************************************************************ */
/* RANGE */
/* ************************************************************ */
/* Inti Creates's "range" decoder, internally IceSoundCodecDecoderRange class. Seems similar to Sony's
* "adaptive dynamic range coding" (ADRC) compression (not what's usually called "range coding").
*
* Data is zlibbed (though doesn't save much) then divided into VBR frames. Each frame has a 24-bit LE
* header that defines a "range" (min..max) and quantized codes' bits (often 6 or 7), then 100 codes.
* Unsigned codes just map to a relative value within the range, and final sample is range-min + range-value.
* Stereo alternates L-R header then L R L R ... samples
*
* For example, if a range = [0, 6000], and bits = 6:
* - code=0 > value=0 (min), code=63 > value=6000 (max), code=31 > value=2952 (in-between), ...
*/
#define RANGE_DECODE_BUFFER 0x800
typedef struct {
const uint8_t* inbuf;
int inbuf_size;
int max_samples;
int frame_codes;
int samples_done;
uint32_t outbuf_pos;
uint8_t outbuf[RANGE_DECODE_BUFFER];
z_stream strm;
int codes_left;
int bitpos; /* within curr_byte */
int16_t range_min[ICESND_MAX_CHANNELS];
int16_t range_max[ICESND_MAX_CHANNELS];
uint16_t range_bits[ICESND_MAX_CHANNELS];
uint16_t range_mask[ICESND_MAX_CHANNELS];
uint8_t curr_byte;
uint8_t spf; /* not needed to decode though */
uint8_t channels;
int zlib_init;
/* extra */
uint32_t outbuf_max; /* original has all data in memory, but we read zlib in chunks that may be smaller */
} range_handle_t;
static range_handle_t* range_decoder_open() {
range_handle_t* ctx = NULL;
//ctx = calloc(1, sizeof(range_handle_t));
ctx = malloc(sizeof(range_handle_t));
if (!ctx) goto fail;
ctx->inbuf = NULL;
ctx->inbuf_size = 0;
ctx->max_samples = 0;
ctx->frame_codes = 0;
ctx->spf = 0;
ctx->zlib_init = 0;
return ctx;
fail:
return NULL;
}
static void range_decoder_close(range_handle_t* ctx) {
if (!ctx)
return;
zlib_end(&ctx->strm, &ctx->zlib_init);
free(ctx);
}
static int range_decoder_reset(range_handle_t* ctx) {
int err;
err = zlib_init(&ctx->strm, &ctx->zlib_init, ctx->inbuf, ctx->inbuf_size);
if (err < ICESND_RESULT_OK) return err;
ctx->outbuf_pos = 0xFFFFFFFF; //sizeof(ctx->outbuf); /* force init */
ctx->samples_done = 0;
ctx->codes_left = 0;
ctx->bitpos = 0;
ctx->outbuf_max = 0;
return ICESND_RESULT_OK;
}
static int range_decoder_block_setup(range_handle_t* ctx, const uint8_t* buf, int buf_size, bigrp_entry_t* etr, int max_samples) {
int err;
ctx->inbuf = buf;
ctx->inbuf_size = buf_size;
ctx->max_samples = max_samples;
ctx->frame_codes = etr->frame_codes;
ctx->spf = etr->spf;
ctx->channels = etr->channels;
err = range_decoder_reset(ctx);
if (err < ICESND_RESULT_OK) return err;
return ICESND_RESULT_OK;
}
#if 0
static void fill_zlib(z_stream* strm, data_t* data) {
/* zlib sets this to 0 once consumed */
if (strm->avail_in > 0)
return;
/* buffer is smaller than size, so this will be called N times */
if (data->data_left) {
int bytes = data->buf_size;
if (bytes > data->data_left)
bytes = data->data_left;
strm->next_in = data->buf;
strm->avail_in = data->cb.read(data->buf, 1, bytes, data->cb.arg);
data->data_left -= strm->avail_in;
}
}
#endif
/* get next byte from the zlib stream */
static void range_load_byte(range_handle_t* ctx) {
if (ctx->outbuf_pos >= ctx->outbuf_max) { /* OG: >= sizeof(ctx->outbuf) */
//fill_zlib(&ctx->strm, ctx->data);
ctx->strm.avail_out = sizeof(ctx->outbuf);
ctx->strm.next_out = ctx->outbuf;
inflate(&ctx->strm, Z_NO_FLUSH);
//if (err < Z_OK) return ICESND_ERROR_DECODER; /* OG: no error control (shouldn't matter) */
ctx->outbuf_pos = 0;
ctx->outbuf_max = sizeof(ctx->outbuf) - ctx->strm.avail_out;
}
ctx->curr_byte = ctx->outbuf[ctx->outbuf_pos];
ctx->outbuf_pos++;
}
/* read 24-bit header, decompressing bytes if necessary */
static void range_load_header(range_handle_t* ctx, int ch) {
uint32_t frame_header;
range_load_byte(ctx);
frame_header = ctx->curr_byte;
range_load_byte(ctx);
frame_header = (ctx->curr_byte << 8) | frame_header;
range_load_byte(ctx);
frame_header = (ctx->curr_byte << 16) | frame_header;
/* get signed range and quantized bits for this frame */
ctx->range_min[ch] = (frame_header >> 3) << 5; /* & 0xFFE0, upper 11 bits (signed) */
ctx->range_max[ch] = (frame_header >> 14) << 6; /* & 0xFFC0, upper 10 bits (signed) */ //(frame_header >> 8) & 0xffc0;
ctx->range_bits[ch] = (frame_header & 7) + 1;
ctx->range_mask[ch] = (1 << ctx->range_bits[ch]) - 1;
}
/* decode next bitstream's code, decompressing bytes if necessary */
static int16_t range_get_sample(range_handle_t* ctx, int ch) {
int32_t code;
int16_t delta;
uint16_t mask = ctx->range_mask[ch];
/* get next code of N-bits (doesn't seem to use an actual bitstream class) */
if (ctx->bitpos == 0) {
range_load_byte(ctx);
}
code = (ctx->curr_byte >> (ctx->bitpos)) & mask;
if (ctx->bitpos + ctx->range_bits[ch] > 8) {
range_load_byte(ctx);
code |= (ctx->curr_byte << (8 - ctx->bitpos)) & mask;
ctx->bitpos = (ctx->bitpos + ctx->range_bits[ch]) - 8;
}
else {
ctx->bitpos = (ctx->bitpos + ctx->range_bits[ch]) & 7;
}
/* calculate code's range value and final sample */
delta = code * (ctx->range_max[ch] - ctx->range_min[ch]) / mask;
return ctx->range_min[ch] + delta; /* no clamp */
}
/* decode N samples and copy to sbuf.
* Internally decodes N samples at a time, and if asked for non-multiple number of samples it'll
* stop and resume properly from last copied sample of those 16. Return 1 if no more samples left. */
static int range_decoder_decode(range_handle_t* ctx, int16_t* sbuf, const int max_done, int* p_done) {
int ch;
*p_done = 0;
while (ctx->samples_done < ctx->max_samples) {
/* read frame header */
if (ctx->codes_left == 0) {
for (ch = 0; ch < ctx->channels; ch++) {
range_load_header(ctx, ch);
}
ctx->codes_left = ctx->frame_codes;
if (ctx->samples_done + ctx->frame_codes > ctx->max_samples)
ctx->codes_left = ctx->max_samples - ctx->samples_done;
ctx->bitpos = 0;
}
/* decode frame samples */
while (ctx->codes_left) {
for (ch = 0; ch < ctx->channels; ch++) {
*sbuf++ = range_get_sample(ctx, ch);
}
ctx->samples_done++;
ctx->codes_left--;
(*p_done)++;
if (*p_done >= max_done) /* samples left */
return ctx->samples_done >= ctx->max_samples; /* block done */
}
}
return ctx->samples_done >= ctx->max_samples; /* block done */
}
/* ************************************************************ */
/* DCT */
/* ************************************************************ */
/* Inti Creates's "dct" decoder, internally IceSoundCodecDecoderDCT class, a pretty simple DCT codec.
*
* Header stores one codebook per band (max 16 * channels) of quantized bits, in zlibbed chunks of 4-bit nibbles.
* Data is just a bitstream of up to 16 bands (L then R) of variable bit codes (max 16-bit) that depend on previous
* 16 bands, dequantized using iDCT. Uses mid-side stereo but otherwise no other features like frames, scalefactors
* or overlaps/delay. Samples are encoded directly as +-32768, 16 at a time.
*/
#define DCT_MAX_BANDS 16
#define DCT_MAX_TRANSFORM 8
#define DCT_MAX_PREV 4
#define DCT_MAX_PREV_MASK 0x3
#define DCT_CODEBOOK_BUFFER 0x100
typedef struct {
const uint8_t* buf;
int bitpos;
int bitstart;
int max_bits;
} dct_bitreader_t;
typedef struct {
uint32_t table_size;
uint8_t init_scale;
uint8_t bands;
uint8_t channels;
uint8_t unused;
uint32_t max_samples;
uint32_t cbk_offset[ICESND_MAX_CHANNELS][DCT_MAX_BANDS];
uint32_t cbk_size[ICESND_MAX_CHANNELS][DCT_MAX_BANDS];
uint32_t data_start;
uint32_t data_size;
} dct_codeinfo_t;
typedef struct {
z_stream strm;
uint8_t outbuf[DCT_CODEBOOK_BUFFER];
dct_bitreader_t br;
int zlib_init;
} dct_codebook_t;
typedef struct {
dct_codeinfo_t codeinfo_mem; /* OG code just casts a pointer to this, keep struct around */
dct_codeinfo_t* codeinfo;
int samples_done;
dct_codebook_t codebook[ICESND_MAX_CHANNELS][DCT_MAX_BANDS];
dct_bitreader_t br;
float transform[DCT_MAX_TRANSFORM][DCT_MAX_BANDS];
float unused[DCT_MAX_TRANSFORM][DCT_MAX_BANDS]; /* ? */
int16_t spectra[ICESND_MAX_CHANNELS][DCT_MAX_PREV][DCT_MAX_BANDS];
int spectra_curr;
int16_t sbuf_tmp[DCT_MAX_BANDS * ICESND_MAX_CHANNELS]; /* interleaved */
float scales[16];
} dct_handle_t;
/* ****************************** */
/* IceSoundCodecDecoder(Bitreader) */
/* OG bitreader uses u32 buf and reads u32le at a time (to simplify shifting), and aligns for 32-bit,
* since blocks pointer can start in the middle (whole file loaded in memory + non-padded blocks) */
static void dct_bitreader_init(dct_bitreader_t* ctx) {
ctx->buf = NULL;
ctx->bitpos = 0;
ctx->bitstart = 0;
ctx->max_bits = 0;
}
static void dct_bitreader_set(dct_bitreader_t* ctx, const uint8_t* buf, int buf_size) {
//unsigned int* buf32 = (unsigned int *)(buf);
//ctx->buf32 = (uin32_t*)((uint32_t)buf32 & 0xFFFFFFFC); /* align to 32-bit boundary */
//ctx->bitstart = 8 * ((uint8_t)buf32 & 3); /* align to pointer start */
ctx->buf = buf;
ctx->bitstart = 8 * 0; /* non-aligned ok */
ctx->bitpos = ctx->bitstart;
ctx->max_bits = 8 * buf_size;
}
static int dct_bitreader_is_over(dct_bitreader_t* ctx) {
return ctx->bitpos >= ctx->bitstart + ctx->max_bits;
}
static uint32_t dct_bitreader_get(dct_bitreader_t* ctx, int bits) {
uint32_t code32;
uint8_t shift;
int pos;
uint32_t mask;
if (ctx->bitpos + bits > ctx->bitstart + ctx->max_bits) /* ? */
return 0;
pos = ctx->bitpos >> 3;
shift = ctx->bitpos & 0x7; /* within u8 */
code32 = ctx->buf[pos] >> shift;
if (bits + shift > 8) {
code32 |= ctx->buf[pos+1] << (8u - shift);
if (bits + shift > 16) {
code32 |= ctx->buf[pos+2] << (16u - shift);
if (bits + shift > 24) {
code32 |= ctx->buf[pos+3] << (24u - shift);
if (bits + shift > 32) {
code32 |= ctx->buf[pos+4] << (32u - shift);
}
}
}
}
//pos = ctx->br.bitpos >> 5;
//shift = ctx->br.bitpos & 0x1f; /* within u32 */
//code32 = ctx->buf32[pos] >> shift;
//if (qbits + shift > 32) {
// code32 |= ctx->buf32[pos+1] << (32u - shift);
//}
ctx->bitpos += bits;
mask = (((1 << bits)) - 1);
return code32 & mask;
}
/* ****************************** */
/* IceSoundCodecDecoder(Codebook) */
static void dct_codebook_init(dct_codebook_t* ctx) {
/* no alloc needed (part of dct_handle_t) */
dct_bitreader_init(&ctx->br);
ctx->zlib_init = 0;
}
static void dct_codebook_close(dct_codebook_t* ctx) {
if (!ctx)
return;
zlib_end(&ctx->strm, &ctx->zlib_init);
/* no free needed */
}
static int dct_codebook_reset(dct_codebook_t* ctx, const uint8_t* buf, int buf_size) {
int err;
err = zlib_init(&ctx->strm, &ctx->zlib_init, buf, buf_size);
if (err < ICESND_RESULT_OK) return err;
dct_bitreader_init(&ctx->br);
return ICESND_RESULT_OK;
}
/* Read next quantized value's bits from zlibbed codebook. Data is in LSB order and codes 4-bits, like:
* 0x10,0x32,0x54,0x76,0x98... = 0 1 2 3 4 5 6 7 8 9... */
static uint8_t dct_codebook_get_qbits(dct_codebook_t* ctx) {
uint32_t qbits;
if (dct_bitreader_is_over(&ctx->br)) {
ctx->strm.avail_out = sizeof(ctx->outbuf);
ctx->strm.next_out = ctx->outbuf;
inflate(&ctx->strm, Z_NO_FLUSH);
//if (err < Z_OK) return ICESND_ERROR_DECODER; /* OG: no error control (shouldn't matter) */
dct_bitreader_set(&ctx->br, ctx->outbuf, sizeof(ctx->outbuf) - ctx->strm.avail_out);
}
qbits = dct_bitreader_get(&ctx->br, 4);
return qbits;
}
/* *********************** */
/* IceSoundCodecDecoderDCT */
static dct_handle_t* dct_decoder_open() {
int i, ch, band;
dct_handle_t* ctx = NULL;
//ctx = calloc(1, sizeof(dct_handle_t));
ctx = malloc(sizeof(dct_handle_t));
if (!ctx) goto fail;
ctx->codeinfo = NULL;
/* init all codebook's base values */
for (ch = 0; ch < ICESND_MAX_CHANNELS; ch++) {
for (band = 0; band < DCT_MAX_BANDS; band++) {
dct_codebook_t* codebook = &ctx->codebook[ch][band];
dct_codebook_init(codebook);
}
}
dct_bitreader_init(&ctx->br);
for (i = 0; i < DCT_MAX_BANDS; i++) {
ctx->scales[i] = 1.0f;
}
return ctx;
fail:
return NULL;
}
static void dct_decoder_close(dct_handle_t* ctx) {
int ch, band;
/* setup all codebook's zlib streams (even if not used, since we can close before anything is set) */
for (ch = 0; ch < ICESND_MAX_CHANNELS; ch++) {
for (band = 0; band < DCT_MAX_BANDS; band++) {
dct_codebook_t* codebook = &ctx->codebook[ch][band];
dct_codebook_close(codebook);
}
}
free(ctx);
}
static int dct_decoder_reset(dct_handle_t* ctx, const uint8_t* buf) {
int err;
int ch, band;
dct_codeinfo_t* ci = ctx->codeinfo;
/* OG code doesn't pass buf (since reset is a virtual method), but rather codeinfo doubles as a pointer to data start */
/* close all codebook's zlib streams */
for (ch = 0; ch < ci->channels; ch++) {
for (band = 0; band < ci->bands; band++) {
dct_codebook_t* codebook = &ctx->codebook[ch][band];
const uint8_t* cbk_start = buf + ci->cbk_offset[ch][band];
int cbk_size = ci->cbk_size[ch][band];
err = dct_codebook_reset(codebook, cbk_start, cbk_size);
if (err < ICESND_RESULT_OK) return err;
}
}
dct_bitreader_set(&ctx->br, buf + ci->data_start, ci->data_size);
memset(ctx->spectra, 0, sizeof(ctx->spectra));
ctx->samples_done = 0;
ctx->spectra_curr = 0;
return ICESND_RESULT_OK;
}
/* transform spectrum into samples (iDCT) */
static void dct_decoder_transform(dct_handle_t* ctx, int16_t* sbuf_tmp, int channel, int pos) {
int i, band;
float fbuf[16] = {0}; /* no need to init as it's written in band 0 but gcc complains */
float f_curr;
dct_codeinfo_t* ci = ctx->codeinfo;
for (band = 0; band < ci->bands; band++) {
/* scales seems fixed to 1.0, maybe a remnant */
float coef = (float)ctx->spectra[channel][pos][band] * ctx->scales[band];
/* optimized butterfly ops? */
switch (band) {
case 0: /* bits 0000 */
f_curr = ctx->transform[0][band] * coef;
fbuf[0] = f_curr;
fbuf[1] = f_curr;
fbuf[2] = f_curr;
fbuf[3] = f_curr;
fbuf[4] = f_curr;
fbuf[5] = f_curr;
fbuf[6] = f_curr;
fbuf[7] = f_curr;
fbuf[8] = f_curr;
fbuf[9] = f_curr;
fbuf[10] = f_curr;
fbuf[11] = f_curr;
fbuf[12] = f_curr;
fbuf[13] = f_curr;
fbuf[14] = f_curr;
fbuf[15] = f_curr;
break;
case 1:
case 3:
case 5:
case 7:
case 9:
case 11:
case 13:
case 15: /* bits xxx1 */
f_curr = ctx->transform[0][band] * coef;
fbuf[0] += f_curr;
fbuf[15] -= f_curr;
f_curr = ctx->transform[1][band] * coef;
fbuf[1] += f_curr;
fbuf[14] -= f_curr;
f_curr = ctx->transform[2][band] * coef;
fbuf[2] += f_curr;
fbuf[13] -= f_curr;
f_curr = ctx->transform[3][band] * coef;
fbuf[3] += f_curr;
fbuf[12] -= f_curr;
f_curr = ctx->transform[4][band] * coef;
fbuf[4] += f_curr;
fbuf[11] -= f_curr;
f_curr = ctx->transform[5][band] * coef;
fbuf[5] += f_curr;
fbuf[10] -= f_curr;
f_curr = ctx->transform[6][band] * coef;
fbuf[6] += f_curr;
fbuf[9] -= f_curr;
f_curr = ctx->transform[7][band] * coef;
fbuf[7] += f_curr;
fbuf[8] -= f_curr;
break;
case 2u:
case 6u:
case 10:
case 14: /* bits xx10 */
f_curr = ctx->transform[0][band] * coef;
fbuf[0] += f_curr;
fbuf[7] -= f_curr;
fbuf[8] -= f_curr;
fbuf[15] += f_curr;
f_curr = ctx->transform[1][band] * coef;
fbuf[1] += f_curr;
fbuf[6] -= f_curr;
fbuf[9] -= f_curr;
fbuf[14] += f_curr;
f_curr = ctx->transform[2][band] * coef;
fbuf[2] += f_curr;
fbuf[5] -= f_curr;
fbuf[10] -= f_curr;
fbuf[13] += f_curr;
f_curr = ctx->transform[3][band] * coef;
fbuf[3] += f_curr;
fbuf[4] -= f_curr;
fbuf[11] -= f_curr;
fbuf[12] += f_curr;
break;
case 4:
case 12: /* bits x100 */
f_curr = ctx->transform[0][band] * coef;
fbuf[0] += f_curr;
fbuf[3] -= f_curr;
fbuf[4] -= f_curr;
fbuf[7] += f_curr;
fbuf[8] += f_curr;
fbuf[11] -= f_curr;
fbuf[12] -= f_curr;
fbuf[15] += f_curr;
f_curr = ctx->transform[1][band] * coef;
fbuf[1] += f_curr;
fbuf[2] -= f_curr;
fbuf[5] -= f_curr;
fbuf[6] += f_curr;
fbuf[9] += f_curr;
fbuf[10] -= f_curr;
fbuf[13] -= f_curr;
fbuf[14] += f_curr;
break;
case 8: /* bits 1000 */
f_curr = ctx->transform[0][band] * coef;
fbuf[0] += f_curr;
fbuf[1] -= f_curr;
fbuf[2] -= f_curr;
fbuf[3] += f_curr;
fbuf[4] += f_curr;
fbuf[5] -= f_curr;
fbuf[6] -= f_curr;
fbuf[7] += f_curr;
fbuf[8] += f_curr;
fbuf[9] -= f_curr;
fbuf[10] -= f_curr;
fbuf[11] += f_curr;
fbuf[12] += f_curr;
fbuf[13] -= f_curr;
fbuf[14] -= f_curr;
fbuf[15] += f_curr;
break;
default:
break;
}
}
/* copy float samples to sbuf samples */
for (i = 0; i < DCT_MAX_BANDS; i++) {
float sample = roundf(fbuf[i]);
/* interleaved (L: 0,2,4,6,8... R:1,3,5,7...) */
sbuf_tmp[channel + ci->channels * i] = (int16_t)sample; /* no clamp */
}
}
/* read current code from the bitstream, in LE byte order */
static int16_t dct_decoder_get_code(dct_handle_t* ctx, uint8_t qbits) {
uint32_t code32;
int16_t code16; /* also ok as int32 */
/* get code from bitstream */
if (qbits <= 0) { /* no resolution: 1-bit where 0 = 0 and 1 = -1 */
code32 = dct_bitreader_get(&ctx->br, 1);
code16 = (int16_t)((int16_t)code32 << 15) >> 15; /* sign extend */
}
else {
code32 = dct_bitreader_get(&ctx->br, qbits);
code16 = code32; /* qbits max 0..15 */
if (code16 < (1 << (qbits - 1))) /* negative encoding */
code16 = code16 - (1 << qbits);
}
return code16;
}
/* read codes for this channel */
static void dct_decoder_dequantize(dct_handle_t* ctx, int channel, int pos) {
int band;
dct_codeinfo_t* ci = ctx->codeinfo;
int16_t* spectra = ctx->spectra[channel][pos];
int16_t* spectra_prev1 = ctx->spectra[channel][(pos - 1) & DCT_MAX_PREV_MASK];
int16_t* spectra_prev2 = ctx->spectra[channel][(pos - 2) & DCT_MAX_PREV_MASK];
for (band = 0; band < ci->bands; band++) {
uint8_t qbits; /* common 7~10 bits, sometimes 12 too */
int16_t code; /* also ok as int32 */
/* get next code's resolution and code */
qbits = dct_codebook_get_qbits(&ctx->codebook[channel][band]);
code = dct_decoder_get_code(ctx, qbits);
/* calc final value based on previous */
spectra[band] = code + (int16_t)(2 * spectra_prev1[band]) - spectra_prev2[band];
}
}
/* restore L/R bands based on mid channel + side differences, ratio 1.0 + copy to final buffer */
static void dct_decoder_ms_stereo(dct_handle_t* ctx, int16_t* sbuf_tmp) {
int i;
dct_codeinfo_t* ci = ctx->codeinfo;
for (i = 0; i < DCT_MAX_BANDS; i++) {
int16_t sample_l = sbuf_tmp[0 + ci->channels * i];
int16_t sample_r = sbuf_tmp[1 + ci->channels * i];
ctx->sbuf_tmp[0 + ci->channels * i] = sample_l + sample_r;
ctx->sbuf_tmp[1 + ci->channels * i] = sample_l - sample_r;
}
}
/* decode N samples and copy to sbuf.
* Internally decodes 16 samples at a time, and if asked for non-multiple number of samples it'll
* stop and resume properly from last copied sample of those 16. Return 1 if no more samples left. */
static int dct_decoder_decode(dct_handle_t* ctx, int16_t* sbuf, const int max_done, int* p_done) {
int ch;
int16_t sbuf_loc[DCT_MAX_BANDS * ICESND_MAX_CHANNELS]; /* interleaved */
int16_t* sbuf_tmp;
dct_codeinfo_t* ci = ctx->codeinfo;
int samples_left;
*p_done = 0;
samples_left = max_done;
if (samples_left > ci->max_samples - ctx->samples_done)
samples_left = ci->max_samples - ctx->samples_done;
/* 2ch uses a tmp buffer to handle MS stereo */
if (ci->channels == 1)
sbuf_tmp = ctx->sbuf_tmp;
else
sbuf_tmp = sbuf_loc;
while (ctx->samples_done < ci->max_samples) {
if (!samples_left)
return ctx->samples_done >= ci->max_samples;
/* decode 16 samples (every 16 samples) */
if ((ctx->samples_done & 0xF) == 0) {
for (ch = 0; ch < ci->channels; ch++) {
dct_decoder_dequantize(ctx, ch, ctx->spectra_curr);
dct_decoder_transform(ctx, sbuf_tmp, ch, ctx->spectra_curr);
}
ctx->spectra_curr = (ctx->spectra_curr + 1) & DCT_MAX_PREV_MASK; /* 0..3 and back to 0 */
if (ci->channels == 2)
dct_decoder_ms_stereo(ctx, sbuf_tmp);
}
/* copy to output sbuf */
{
int sample_start;
int samples_copied;
/* start could be non-zero if max_done is non-multiple of 16 */
sample_start = ctx->samples_done & 0xF;
samples_copied = 16 - sample_start;
if (samples_copied > samples_left)
samples_copied = samples_left;
/* copy to output sbuf */
memcpy(sbuf, &ctx->sbuf_tmp[sample_start * ci->channels], sizeof(int16_t) * ci->channels * samples_copied);
sbuf += samples_copied * ci->channels;
ctx->samples_done += samples_copied;
samples_left -= samples_copied;
*p_done += samples_copied;
}
}
return ctx->samples_done >= ci->max_samples; /* block done */
}
/* OG code casts buffer to this struct, read in a more portable fashion */
static int dct_codeinfo_parse(dct_codeinfo_t* ci, const uint8_t* buf, int buf_size) {
int ch, i, pos;
if (buf_size < 0x114)
goto fail;
ci->table_size = get_u32le(buf + 0x00);
ci->init_scale = get_u8 (buf + 0x04);
ci->bands = get_u8 (buf + 0x05);
ci->channels = get_u8 (buf + 0x06);
ci->unused = get_u8 (buf + 0x07);
ci->max_samples = get_u32le(buf + 0x08);
pos = 0x0c;
for (ch = 0; ch < ICESND_MAX_CHANNELS; ch++) {
for (i = 0; i < DCT_MAX_BANDS; i++) {
ci->cbk_offset[ch][i] = get_u32le(buf + pos);
pos += 0x04;
}
}
for (ch = 0; ch < ICESND_MAX_CHANNELS; ch++) {
for (i = 0; i < DCT_MAX_BANDS; i++) {
ci->cbk_size[ch][i] = get_u32le(buf + pos);
pos += 0x04;
}
}
ci->data_start = get_u32le(buf + 0x10c);
ci->data_size = get_u32le(buf + 0x110);
if (ci->table_size > 0x114)
goto fail;
if (ci->bands < 1 || ci->bands > DCT_MAX_BANDS)
goto fail;
if (ci->channels < 1 || ci->channels > ICESND_MAX_CHANNELS)
goto fail;
if (ci->unused != 0x00)
goto fail;
if (buf_size < ci->data_start + ci->data_size)
goto fail;
return ICESND_RESULT_OK;
fail:
return ICESND_ERROR_SETUP;
}
/* base DCT unique coefs, used below to init the full table (see opus' analysis.c) */
static const float DCT_TRANSFORM_COEFS[16] = {
0.25f, 0.35185099f, 0.34676f, 0.33832899f,
0.32664099f, 0.31180599f, 0.29396901f, 0.27329999f,
0.25f, 0.224292f, 0.19642401f, 0.166664f,
0.135299f, 0.102631f, 0.068975002f, 0.034653999f,
};
static const float DCT_TRANSFORM_SCALES[16] = {
4.0, 6.0, 8.0, 10.0, 12.0, 12.0, 13.0, 15.0,
16.0, 16.0, 20.0, 24.0, 28.0, 35.0, 41.0, 41.0
};
static const int DCT_TRANSFORM_STEPS[16] = {
1, 8, 4, 8, 2, 8, 4, 8,
1, 8, 4, 8, 2, 8, 4, 8,
};
/* re-calculate DCT table, that depends on a current intro/body chunk's scale value */
static int dct_decoder_block_setup(dct_handle_t* ctx, const uint8_t* buf, int buf_size, bigrp_entry_t* etr) {
int i;
int err;
float scale;
float dct_coefs[DCT_MAX_BANDS];
dct_codeinfo_t* ci = &ctx->codeinfo_mem;
/* portable init */
err = dct_codeinfo_parse(ci, buf, buf_size);
if (err < ICESND_RESULT_OK) return err;
/* pre-calculate scaled coefs (mini optimization?) */
scale = ci->init_scale;
for (i = 0; i < DCT_MAX_BANDS; i++) {
dct_coefs[i] = DCT_TRANSFORM_COEFS[i] * scale;
}
/* transform for N=16, k=0..8? */
for (i = 0; i < DCT_MAX_BANDS; i++) {
int steps = DCT_TRANSFORM_STEPS[i];
int step;
int pos = i;
for (step = 0; step < steps; step++) {
float coef;
switch ((pos >> 4) & 3) {
case 1:
coef = -dct_coefs[16 - (pos & 0xF)];
break;
case 2:
coef = -dct_coefs[(pos & 0xF)];
break;
case 3:
coef = +dct_coefs[16 - (pos & 0xF)];
break;
default:
coef = +dct_coefs[(pos & 0xF)];
break;
}
pos += 2 * i;
//ctx->transform[step][i] = coef; /* somehow assigned twice originally? */
ctx->transform[step][i] = DCT_TRANSFORM_SCALES[i] * coef;
}
}
/* rest of setup */
ctx->codeinfo = ci;
err = dct_decoder_reset(ctx, buf);
if (err < ICESND_RESULT_OK) return err;
return ICESND_RESULT_OK;
}
/* ************************************************************ */
/* API */
/* ************************************************************ */
/* (not part of original code (but partially inspired by IceSSoundEng::IcePlayer) */
#define ICESND_BIGRP_SIZE 0x10
#define ICESND_ENTRY_SIZE 0x34
#define ICESND_BUF_SIZE 0x10000
struct icesnd_handle_t {
/* config*/
int target_subsong;
icesnd_callback_t cb;
/* state */
bigrp_header_t hdr;
bigrp_entry_t etr;
void* decoder;
int is_range;
int intro_init;
int body_init;
int intro_done;
/* absolute offset */
int intro_offset;
int body_offset;
uint8_t* blkbuf;
int blkbuf_size;
};
static int parse_header(icesnd_handle_t* ctx) {
int err;
uint8_t tmp[0x40];
const uint8_t* buf;
int buf_size;
uint32_t offset;
/* read common header size */
offset = 0x00;
if (ctx->cb.read) {
ctx->cb.seek(ctx->cb.arg, offset, SEEK_SET);
buf_size = ctx->cb.read(tmp, 1, 0x10, ctx->cb.arg);
buf = tmp;
}
else {
buf_size = ctx->cb.filebuf_size;
buf = ctx->cb.filebuf + offset;
}
err = bigrp_header_parse(&ctx->hdr, buf, buf_size, ctx->target_subsong);
if (err < ICESND_RESULT_OK) goto fail;
/* read target entry */
offset = ctx->hdr.head_size + ctx->hdr.entry_size * (ctx->target_subsong - 1);
if (ctx->cb.read) {
ctx->cb.seek(ctx->cb.arg, offset, SEEK_SET);
buf_size = ctx->cb.read(tmp, 1, ctx->hdr.entry_size, ctx->cb.arg);
buf = tmp;
}
else {
if (offset > ctx->cb.filebuf_size) goto fail;
buf = ctx->cb.filebuf + offset;
buf_size = ctx->cb.filebuf_size - offset;
}
err = bigrp_entry_parse(&ctx->etr, buf, buf_size);
if (err < ICESND_RESULT_OK) goto fail;
if (ctx->etr.codec == ICESND_CODEC_RANGE || ctx->etr.codec == ICESND_CODEC_DCT) {
ctx->intro_offset = offset + ctx->etr.intro_offset;
ctx->body_offset = offset + ctx->etr.body_offset;
}
//TODO fix library later
// see comment at top, but basically format is rather annoying to adapt as a streaming
// decoder, and ran out of time. For now it reads a whole blocks (intro/body) at once. Sorry!
/* prepare buf */
if (ctx->cb.read) {
int block_size = ctx->etr.body_zsize;
if (block_size < ctx->etr.intro_zsize)
block_size = ctx->etr.intro_zsize;
if (block_size % 0x10 != 0) /* pad just in case */
block_size = block_size + (0x10 - (block_size % 0x10));
ctx->blkbuf_size = block_size;
ctx->blkbuf = malloc(block_size);
if (!ctx->blkbuf) goto fail;
}
return ICESND_RESULT_OK;
fail:
return ICESND_ERROR_SETUP;
}
icesnd_handle_t* icesnd_init(int target_subsong, icesnd_callback_t* cb) {
icesnd_handle_t* ctx = NULL;
int err;
ctx = calloc(1, sizeof(icesnd_handle_t));
if (!ctx) goto fail;
ctx->target_subsong = target_subsong;
ctx->cb = *cb; /* memcpy */
if (!cb->filebuf && !(cb->read && cb->seek))
goto fail;
err = parse_header(ctx);
if (err < ICESND_RESULT_OK) goto fail;
ctx->is_range = ctx->etr.codec == 0x00;
if (ctx->is_range)
ctx->decoder = range_decoder_open();
else
ctx->decoder = dct_decoder_open();
if (!ctx->decoder) goto fail;
icesnd_reset(ctx, 0);
return ctx;
fail:
icesnd_free(ctx);
return NULL;
//return ICESND_ERROR_SETUP;
}
void icesnd_free(icesnd_handle_t* ctx) {
if (!ctx)
return;
if (ctx->decoder) {
if (ctx->is_range)
range_decoder_close(ctx->decoder);
else
dct_decoder_close(ctx->decoder);
}
free(ctx->blkbuf);
free(ctx);
}
int icesnd_info(icesnd_handle_t* ctx, icesnd_info_t* info) {
if (!ctx)
goto fail;
info->total_subsongs = ctx->hdr.total_subsongs;
info->codec = ctx->etr.codec;
info->sample_rate = ctx->etr.sample_rate;
info->channels = ctx->etr.channels;
info->loop_start = ctx->etr.intro_samples;
info->num_samples = ctx->etr.intro_samples + ctx->etr.body_samples;
info->loop_flag = ctx->etr.loop_flag;
return ICESND_RESULT_OK;
fail:
return ICESND_ERROR_DECODE;
}
void icesnd_reset(icesnd_handle_t* ctx, int loop_start) {
if (!ctx || !ctx->decoder)
return;
ctx->intro_init = 0;
ctx->body_init = 0;
ctx->intro_done = 0;
/* skip intro block in some cases */
if (ctx->etr.intro_samples == 0 || loop_start != 0)
ctx->intro_done = 1;
/* no need to reset decoder as will be done when block is set, plus
* only reset properly when doing that */
}
static int setup_block(icesnd_handle_t* ctx, int intro) {
int err;
const uint8_t* buf;
int buf_size;
int block_offset = (intro ? ctx->intro_offset : ctx->body_offset);
int block_size = (intro ? ctx->etr.intro_zsize : ctx->etr.body_zsize);
int block_samples = (intro ? ctx->etr.intro_samples : ctx->etr.body_samples);
if (ctx->cb.read) {
/* could optimize by ignoring calls (intro > body > body...) but this kinda simulates streamings */
if (block_size > ctx->blkbuf_size) /* can't happen but anyway */
return ICESND_ERROR_DECODE;
ctx->cb.seek(ctx->cb.arg, block_offset, SEEK_SET);
buf_size = ctx->cb.read(ctx->blkbuf, 1, block_size, ctx->cb.arg);
buf = ctx->blkbuf;
}
else {
if (ctx->cb.filebuf_size < block_offset + block_size)
return ICESND_ERROR_DECODE;
buf = ctx->cb.filebuf + block_offset;
buf_size = ctx->cb.filebuf_size - block_offset;
}
if (ctx->is_range) {
err = range_decoder_block_setup(ctx->decoder, buf, buf_size, &ctx->etr, block_samples);
}
else {
err = dct_decoder_block_setup(ctx->decoder, buf, buf_size, &ctx->etr); /* max_samples info is in codebook table */
}
if (err < ICESND_RESULT_OK) return err;
return ICESND_RESULT_OK;
}
int icesnd_decode(icesnd_handle_t* ctx, int16_t* sbuf, int max_samples) {
int err;
int samples_done, block_end;
int samples_decoded = 0;
if (!ctx)
goto fail;
while (max_samples > 0) {
if (!ctx->intro_done) {
if (!ctx->intro_init) {
err = setup_block(ctx, 1);
if (err < ICESND_RESULT_OK) return err;
ctx->intro_init = 1;
}
}
else {
if (!ctx->body_init) {
err = setup_block(ctx, 0);
ctx->body_init = 1;
}
}
if (ctx->is_range)
block_end = range_decoder_decode(ctx->decoder, sbuf, max_samples, &samples_done);
else
block_end = dct_decoder_decode(ctx->decoder, sbuf, max_samples, &samples_done);
max_samples -= samples_done;
samples_decoded += samples_done;
sbuf += samples_done * ctx->etr.channels;
//ctx->curr_sample += samples_done; /* original keeps this around to test if intro block is done */
if (block_end) {
/* after first block (could check if this is the first block but whatevs) */
ctx->intro_done = 1;
/* intro end, or after body end to allow loops on next calls */
if (ctx->etr.loop_flag)
ctx->body_init = 0;
}
/* could be possible on block end if not reset */
if (samples_done == 0)
break;
/* stop on on block boundary to ensure external caller may stop on loop end (could go on otherwise) */
if (block_end)
break;
}
return samples_decoded;
fail:
return ICESND_ERROR_DECODE;
}