mirror of
https://github.com/vgmstream/vgmstream.git
synced 2024-11-28 08:20:54 +01:00
Merge pull request #1475 from vgmstream/utk-misc
- Improve .dsp validations - Remove fake .2pfs extension (use .sap) - Add .move extension [Pop 'n Music 7 (PS2)] - Fix minor UTK issues - Add .cbx [Lego series (multi)] - cleanup
This commit is contained in:
commit
4e89ab3871
@ -530,16 +530,12 @@ class VrtsApp:
|
||||
|
||||
def _get_performance_args(self, cli):
|
||||
args = [cli, '-O'] #flag to not write files
|
||||
if self._args.looping:
|
||||
if not self._args.looping:
|
||||
args.append('-i')
|
||||
args.extend(self._files.filenames)
|
||||
return args
|
||||
|
||||
def _performance(self):
|
||||
flag_looping = ''
|
||||
if self._args.looping:
|
||||
flag_looping = '-i'
|
||||
|
||||
# pases all files at once, as it's faster than 1 by 1 (that has to init program every time)
|
||||
if self._args.performance_new:
|
||||
self._p.info("testing new performance")
|
||||
@ -587,7 +583,7 @@ class VrtsApp:
|
||||
|
||||
def _get_compare_args(self, cli, outwav, filename):
|
||||
args = [cli, '-o', outwav] #flag to not write files
|
||||
if self._args.looping:
|
||||
if not self._args.looping:
|
||||
args.append('-i')
|
||||
args.append(filename)
|
||||
return args
|
||||
@ -596,10 +592,6 @@ class VrtsApp:
|
||||
ts_st = time.time()
|
||||
self._p.info("comparing files")
|
||||
|
||||
flag_looping = ''
|
||||
if self._args.looping:
|
||||
flag_looping = '-i'
|
||||
|
||||
total_ok = 0
|
||||
total_ko = 0
|
||||
for filename in self._files.filenames:
|
||||
|
@ -380,9 +380,9 @@ different internally (encrypted, different versions, etc) and not always can be
|
||||
- InterPlay MUS ACM header [*MUS_ACM*]
|
||||
- *mus_acm*: `.mus`
|
||||
- Subfiles: *acm ogg_vorbis*
|
||||
- **ps2_kces.c**
|
||||
- Konami KCES Header [*PS2_KCES*]
|
||||
- *ps2_kces*: `.kces`
|
||||
- **vig_kces.c**
|
||||
- Konami .VIG Header [*VIG_KCES*]
|
||||
- *vig_kces*: `.vig`
|
||||
- Codecs: PSX
|
||||
- **hxd.c**
|
||||
- Tecmo HXD Header [*HXD*]
|
||||
@ -587,13 +587,13 @@ different internally (encrypted, different versions, etc) and not always can be
|
||||
- Alfa System .STS header [*STS*]
|
||||
- *sts*: `.sts`
|
||||
- Codecs: NGC_DSP
|
||||
- **ps2_p2bt.c**
|
||||
- Pop'n'Music 7 Header [*PS2_P2BT*]
|
||||
- *ps2_p2bt*: `.p2bt`
|
||||
- **p2bt_move_visa.c**
|
||||
- Konami P2BT/MOVE/VISA header [*P2BT_MOVE_VISA*]
|
||||
- *p2bt_move_visa*: `.p2bt .move .vis`
|
||||
- Codecs: PSX
|
||||
- **ps2_gbts.c**
|
||||
- Pop'n'Music 9 Header [*PS2_GBTS*]
|
||||
- *ps2_gbts*: `.gbts`
|
||||
- **gbts.c**
|
||||
- Konami GBTS header [*GBTS*]
|
||||
- *gbts*: `.gbts`
|
||||
- Codecs: PSX
|
||||
- **wii_sng.c**
|
||||
- SNG DSP Header [*WII_SNG*]
|
||||
@ -652,7 +652,8 @@ different internally (encrypted, different versions, etc) and not always can be
|
||||
- *vgs_ps*: `.vgs`
|
||||
- Codecs: PSX
|
||||
- **redspark.c**
|
||||
- RedSpark Header [*REDSPARK*]
|
||||
- RedSpark header [*REDSPARK*]
|
||||
- *redspark*: `.rsd`
|
||||
- Codecs: NGC_DSP
|
||||
- **ps2_sps.c**
|
||||
- Ape Escape 2 SPS Header [*PS2_SPS*]
|
||||
@ -831,7 +832,7 @@ different internally (encrypted, different versions, etc) and not always can be
|
||||
- Square Enix SCD header [*SQEX_SCD*]
|
||||
- *sqex_scd*: `.scd`
|
||||
- Subfiles: *ogg_vorbis_config*
|
||||
- Codecs: OGG PCM16LE PSX MPEG MSADPCM NGC_DSP XMA ATRAC3 ATRAC9
|
||||
- Codecs: OGG PCM16BE PCM16LE PSX MPEG MSADPCM NGC_DSP XMA ATRAC3 ATRAC9
|
||||
- **ngc_nst_dsp.c**
|
||||
- Animaniacs NST header [*NGC_NST_DSP*]
|
||||
- *ngc_nst_dsp*: `.dsp`
|
||||
@ -881,10 +882,10 @@ different internally (encrypted, different versions, etc) and not always can be
|
||||
- Mattel Hyperscan KVAG [*HYPERSCAN_KVAG*]
|
||||
- *hyperscan_kvag*: `.bvg`
|
||||
- Codecs: DVI_IMA
|
||||
- **ios_psnd.c**
|
||||
- PSND Header [*IOS_PSND*]
|
||||
- *ios_psnd*: `.psnd`
|
||||
- Codecs: PCM16LE
|
||||
- **psnd.c**
|
||||
- Polarbit PSND header [*PSND*]
|
||||
- *psnd*: `.psn`
|
||||
- Codecs: PCM16LE DVI_IMA
|
||||
- **adp_wildfire.c**
|
||||
- Wildfire ADP! header [*ADP_WILDFIRE*]
|
||||
- *adp_wildfire*: `.adp`
|
||||
@ -927,9 +928,9 @@ different internally (encrypted, different versions, etc) and not always can be
|
||||
- Namco IVAG header [*IVAG*]
|
||||
- *ivag*: `.ivag`
|
||||
- Codecs: PSX
|
||||
- **ps2_2pfs.c**
|
||||
- Konami 2PFS header [*PS2_2PFS*]
|
||||
- *ps2_2pfs*: `.sap .2pfs`
|
||||
- **2pfs.c**
|
||||
- Konami 2PFS header [*2PFS*]
|
||||
- *2pfs*: `.sap`
|
||||
- Codecs: PSX
|
||||
- **xnb.c**
|
||||
- Microsoft XNA Game Studio header [*XNB*]
|
||||
@ -1339,10 +1340,6 @@ different internally (encrypted, different versions, etc) and not always can be
|
||||
- High Voltage SVG header [*SVG*]
|
||||
- *svg*: `.svg`
|
||||
- Codecs: PSX
|
||||
- **vis.c**
|
||||
- Konami VIS header [*VIS*]
|
||||
- *vis*: `.vis`
|
||||
- Codecs: PSX
|
||||
- **vai.c**
|
||||
- Asobo Studio .VAI header [*VAI*]
|
||||
- *vai*: `.vai`
|
||||
@ -1590,6 +1587,7 @@ different internally (encrypted, different versions, etc) and not always can be
|
||||
- Koei Tecmo WaveBank header [*KWB*]
|
||||
- *kwb*: `.wbd .wb2 .sed + .wbh .whd .wh2`
|
||||
- *xws*: `.xws`
|
||||
- *snd_koei*: `.snd`
|
||||
- *koei_wavebank*
|
||||
- Subfiles: *msf dsp_apex*
|
||||
- Codecs: PCM16LE MSADPCM NGC_DSP XMA2 ATRAC9
|
||||
@ -1814,6 +1812,10 @@ different internally (encrypted, different versions, etc) and not always can be
|
||||
- Ubisoft GWB+GWD header [*GWB_GWD*]
|
||||
- *gwb_gwd*: `.gwb + .gwd`
|
||||
- Codecs: NGC_DSP
|
||||
- **cbx.c**
|
||||
- Traveller's Tales CBX header [*CBX*]
|
||||
- *cbx*: `.cbx`
|
||||
- Codecs: EA_MT
|
||||
- **scd_pcm.c**
|
||||
- Lunar: Eternal Blue .PCM header [*SCD_PCM*]
|
||||
- *scd_pcm*: `.pcm`
|
||||
|
@ -2,6 +2,8 @@ file(GLOB BASE_HEADERS "${CMAKE_CURRENT_SOURCE_DIR}/base/*.h")
|
||||
file(GLOB BASE_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/base/*.c")
|
||||
file(GLOB CODING_HEADERS "${CMAKE_CURRENT_SOURCE_DIR}/coding/*.h")
|
||||
file(GLOB CODING_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/coding/*.c")
|
||||
file(GLOB CLIBS_HEADERS "${CMAKE_CURRENT_SOURCE_DIR}/coding/libs/*.h")
|
||||
file(GLOB CLIBS_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/coding/libs/*.c")
|
||||
file(GLOB LAYOUT_HEADERS "${CMAKE_CURRENT_SOURCE_DIR}/layout/*.h")
|
||||
file(GLOB LAYOUT_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/layout/*.c")
|
||||
file(GLOB META_HEADERS "${CMAKE_CURRENT_SOURCE_DIR}/meta/*.h")
|
||||
@ -16,6 +18,7 @@ file(GLOB MAIN_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/*.c")
|
||||
# Setup source groups, mainly for Visual Studio
|
||||
source_group("Header Files\\base" FILES ${BASE_HEADERS})
|
||||
source_group("Header Files\\coding" FILES ${CODING_HEADERS})
|
||||
source_group("Header Files\\coding\\libs" FILES ${CLIBS_HEADERS})
|
||||
source_group("Header Files\\layout" FILES ${LAYOUT_HEADERS})
|
||||
source_group("Header Files\\meta" FILES ${META_HEADERS})
|
||||
source_group("Header Files\\util" FILES ${UTIL_HEADERS})
|
||||
@ -23,6 +26,7 @@ source_group("Header Files\\ext" FILES ${EXT_HEADERS})
|
||||
|
||||
source_group("Source Files\\base" FILES ${BASE_SOURCES})
|
||||
source_group("Source Files\\coding" FILES ${CODING_SOURCES})
|
||||
source_group("Source Files\\coding\\libs" FILES ${CLIBS_SOURCES})
|
||||
source_group("Source Files\\layout" FILES ${LAYOUT_SOURCES})
|
||||
source_group("Source Files\\meta" FILES ${META_SOURCES})
|
||||
source_group("Source Files\\util" FILES ${UTIL_SOURCES})
|
||||
@ -32,6 +36,8 @@ set(libvgmstream_sources
|
||||
${BASE_SOURCES}
|
||||
${CODING_HEADERS}
|
||||
${CODING_SOURCES}
|
||||
${CLIBS_HEADERS}
|
||||
${CLIBS_SOURCES}
|
||||
${LAYOUT_HEADERS}
|
||||
${LAYOUT_SOURCES}
|
||||
${META_HEADERS}
|
||||
|
@ -6,7 +6,7 @@
|
||||
OBJECTS =
|
||||
|
||||
#SRCS = $(wildcard **/*.c) #GNUMake 3.81?
|
||||
SRCS = $(wildcard *.c) $(wildcard */*.c)
|
||||
SRCS = $(wildcard *.c) $(wildcard */*.c) $(wildcard */*/*.c)
|
||||
OBJECTS = $(patsubst %.c,%.o,$(SRCS))
|
||||
|
||||
|
||||
|
@ -306,8 +306,9 @@ STREAMFILE* compresswave_get_streamfile(compresswave_codec_data* data);
|
||||
/* ea_mt_decoder*/
|
||||
typedef struct ea_mt_codec_data ea_mt_codec_data;
|
||||
|
||||
ea_mt_codec_data* init_ea_mt(int channels, int type);
|
||||
ea_mt_codec_data* init_ea_mt(int channels, int pcm_blocks);
|
||||
ea_mt_codec_data* init_ea_mt_loops(int channels, int pcm_blocks, int loop_sample, off_t* loop_offsets);
|
||||
ea_mt_codec_data* init_ea_mt_cbx(int channels);
|
||||
void decode_ea_mt(VGMSTREAM* vgmstream, sample * outbuf, int channelspacing, int32_t samples_to_do, int channel);
|
||||
void reset_ea_mt(VGMSTREAM* vgmstream);
|
||||
void flush_ea_mt(VGMSTREAM* vgmstream);
|
||||
|
@ -1,17 +1,8 @@
|
||||
#include "coding.h"
|
||||
#include "libs/utkdec.h"
|
||||
|
||||
#include "ea_mt_decoder_utk.h"
|
||||
/* Decodes EA MicroTalk */
|
||||
|
||||
/* Decodes EA MicroTalk (speech codec) using utkencode lib (slightly modified for vgmstream).
|
||||
* EA separates MT10:1 and MT5:1 (bigger frames), but apparently are the same
|
||||
* with different encoding parameters. Later revisions may have PCM blocks (rare).
|
||||
*
|
||||
* Decoder by Andrew D'Addesio: https://github.com/daddesio/utkencode
|
||||
* Info: http://wiki.niotso.org/UTK
|
||||
*/
|
||||
|
||||
|
||||
//#define UTK_MAKE_U32(a,b,c,d) ((a)|((b)<<8)|((c)<<16)|((d)<<24))
|
||||
#define UTK_ROUND(x) ((x) >= 0.0f ? ((x)+0.5f) : ((x)-0.5f))
|
||||
#define UTK_MIN(x,y) ((x)<(y)?(x):(y))
|
||||
#define UTK_MAX(x,y) ((x)>(y)?(x):(y))
|
||||
@ -26,21 +17,30 @@ struct ea_mt_codec_data {
|
||||
off_t loop_offset;
|
||||
int loop_sample;
|
||||
|
||||
int pcm_blocks;
|
||||
int samples_filled;
|
||||
int samples_used;
|
||||
int samples_done;
|
||||
int samples_discard;
|
||||
void* utk_context;
|
||||
void* ctx;
|
||||
};
|
||||
|
||||
static size_t ea_mt_read_callback(void *dest, int size, void *arg);
|
||||
static ea_mt_codec_data* init_ea_mt_internal(utk_type_t type, int channels, int loop_sample, off_t* loop_offsets);
|
||||
|
||||
|
||||
ea_mt_codec_data* init_ea_mt(int channels, int pcm_blocks) {
|
||||
return init_ea_mt_loops(channels, pcm_blocks, 0, NULL);
|
||||
}
|
||||
|
||||
ea_mt_codec_data* init_ea_mt_loops(int channels, int pcm_blocks, int loop_sample, off_t *loop_offsets) {
|
||||
return init_ea_mt_internal(pcm_blocks ? UTK_EA_PCM : UTK_EA, channels, loop_sample, loop_offsets);
|
||||
}
|
||||
|
||||
ea_mt_codec_data* init_ea_mt_cbx(int channels) {
|
||||
return init_ea_mt_internal(UTK_CBX, channels, 0, NULL);
|
||||
}
|
||||
|
||||
static ea_mt_codec_data* init_ea_mt_internal(utk_type_t type, int channels, int loop_sample, off_t* loop_offsets) {
|
||||
ea_mt_codec_data* data = NULL;
|
||||
int i;
|
||||
|
||||
@ -48,16 +48,14 @@ ea_mt_codec_data* init_ea_mt_loops(int channels, int pcm_blocks, int loop_sample
|
||||
if (!data) goto fail;
|
||||
|
||||
for (i = 0; i < channels; i++) {
|
||||
data[i].utk_context = calloc(1, sizeof(UTKContext));
|
||||
if (!data[i].utk_context) goto fail;
|
||||
utk_init(data[i].utk_context);
|
||||
data[i].ctx = utk_init(type);
|
||||
if (!data[i].ctx) goto fail;
|
||||
|
||||
data[i].pcm_blocks = pcm_blocks;
|
||||
data[i].loop_sample = loop_sample;
|
||||
if (loop_offsets)
|
||||
data[i].loop_offset = loop_offsets[i];
|
||||
|
||||
utk_set_callback(data[i].utk_context, data[i].buffer, UTK_BUFFER_SIZE, &data[i], &ea_mt_read_callback);
|
||||
utk_set_callback(data[i].ctx, data[i].buffer, UTK_BUFFER_SIZE, &data[i], &ea_mt_read_callback);
|
||||
}
|
||||
|
||||
return data;
|
||||
@ -71,10 +69,9 @@ void decode_ea_mt(VGMSTREAM* vgmstream, sample_t* outbuf, int channelspacing, in
|
||||
int i;
|
||||
ea_mt_codec_data* data = vgmstream->codec_data;
|
||||
ea_mt_codec_data* ch_data = &data[channel];
|
||||
UTKContext* ctx = ch_data->utk_context;
|
||||
int samples_done = 0;
|
||||
|
||||
|
||||
float* fbuf = utk_get_samples(ch_data->ctx);
|
||||
while (samples_done < samples_to_do) {
|
||||
|
||||
if (ch_data->samples_filled) {
|
||||
@ -98,7 +95,7 @@ void decode_ea_mt(VGMSTREAM* vgmstream, sample_t* outbuf, int channelspacing, in
|
||||
samples_to_get = samples_to_do - samples_done;
|
||||
|
||||
for (i = ch_data->samples_used; i < ch_data->samples_used + samples_to_get; i++) {
|
||||
int pcm = UTK_ROUND(ctx->decompressed_frame[i]);
|
||||
int pcm = UTK_ROUND(fbuf[i]);
|
||||
outbuf[0] = (int16_t)UTK_CLAMP(pcm, -32768, 32767);
|
||||
outbuf += channelspacing;
|
||||
}
|
||||
@ -119,19 +116,20 @@ void decode_ea_mt(VGMSTREAM* vgmstream, sample_t* outbuf, int channelspacing, in
|
||||
|
||||
/* offset is usually at loop_offset here, but not always (ex. loop_sample < 432) */
|
||||
ch_data->offset = ch_data->loop_offset;
|
||||
utk_set_ptr(ctx, 0, 0); /* reset the buffer reader */
|
||||
utk_reset(ctx); /* decoder init (all fields must be reset, for some edge cases) */
|
||||
utk_set_buffer(ch_data->ctx, 0, 0); /* reset the buffer reader */
|
||||
utk_reset(ch_data->ctx); /* decoder init (all fields must be reset, for some edge cases) */
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* new frame */
|
||||
if (ch_data->pcm_blocks)
|
||||
utk_rev3_decode_frame(ctx);
|
||||
else
|
||||
utk_decode_frame(ctx);
|
||||
int samples = utk_decode_frame(ch_data->ctx);
|
||||
if (samples < 0) {
|
||||
VGM_LOG("wrong decode: %i\n", samples);
|
||||
samples = 432;
|
||||
}
|
||||
|
||||
ch_data->samples_used = 0;
|
||||
ch_data->samples_filled = 432;
|
||||
ch_data->samples_filled = samples;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -143,23 +141,20 @@ static void flush_ea_mt_offsets(VGMSTREAM* vgmstream, int is_start, int samples_
|
||||
if (!data) return;
|
||||
|
||||
|
||||
/* EA-MT frames are VBR (not byte-aligned?), so utk_decoder reads new buffer data automatically.
|
||||
/* EA-MT frames are VBR and not byte-aligned, so utk_decoder reads new buffer data automatically.
|
||||
* When decoding starts or a SCHl block changes, flush_ea_mt must be called to reset the state.
|
||||
* A bit hacky but would need some restructuring otherwise. */
|
||||
|
||||
for (i = 0; i < vgmstream->channels; i++) {
|
||||
UTKContext* ctx = data[i].utk_context;
|
||||
|
||||
data[i].streamfile = vgmstream->ch[i].streamfile; /* maybe should keep its own STREAMFILE? */
|
||||
data[i].streamfile = vgmstream->ch[i].streamfile;
|
||||
if (is_start)
|
||||
data[i].offset = vgmstream->ch[i].channel_start_offset;
|
||||
else
|
||||
data[i].offset = vgmstream->ch[i].offset;
|
||||
utk_set_ptr(ctx, 0, 0); /* reset the buffer reader */
|
||||
utk_set_buffer(data[i].ctx, 0, 0); /* reset the buffer reader */
|
||||
|
||||
if (is_start) {
|
||||
utk_reset(ctx);
|
||||
ctx->parsed_header = 0;
|
||||
utk_reset(data[i].ctx);
|
||||
data[i].samples_done = 0;
|
||||
}
|
||||
|
||||
@ -187,7 +182,7 @@ void free_ea_mt(ea_mt_codec_data* data, int channels) {
|
||||
return;
|
||||
|
||||
for (i = 0; i < channels; i++) {
|
||||
free(data[i].utk_context);
|
||||
utk_free(data[i].ctx);
|
||||
}
|
||||
free(data);
|
||||
}
|
||||
|
@ -1,469 +0,0 @@
|
||||
#ifndef _EA_MT_DECODER_UTK_H_
|
||||
#define _EA_MT_DECODER_UTK_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
/* Note: This struct assumes a member alignment of 4 bytes.
|
||||
** This matters when pitch_lag > 216 on the first subframe of any given frame. */
|
||||
typedef struct UTKContext {
|
||||
uint8_t *buffer;
|
||||
size_t buffer_size;
|
||||
void *arg;
|
||||
size_t (*read_callback)(void *dest, int size, void *arg);
|
||||
const uint8_t *ptr, *end;
|
||||
|
||||
int parsed_header;
|
||||
unsigned int bits_value;
|
||||
int bits_count;
|
||||
int reduced_bw;
|
||||
int multipulse_thresh;
|
||||
float fixed_gains[64];
|
||||
float rc[12];
|
||||
float synth_history[12];
|
||||
float adapt_cb[324];
|
||||
float decompressed_frame[432];
|
||||
} UTKContext;
|
||||
|
||||
enum {
|
||||
MDL_NORMAL = 0,
|
||||
MDL_LARGEPULSE = 1
|
||||
};
|
||||
|
||||
static const float utk_rc_table[64] = {
|
||||
+0.0f,
|
||||
-.99677598476409912109375f, -.99032700061798095703125f, -.983879029750823974609375f, -.977430999279022216796875f,
|
||||
-.970982015132904052734375f, -.964533984661102294921875f, -.958085000514984130859375f, -.9516370296478271484375f,
|
||||
-.930754005908966064453125f, -.904959976673126220703125f, -.879167020320892333984375f, -.853372991085052490234375f,
|
||||
-.827579021453857421875f, -.801786005496978759765625f, -.775991976261138916015625f, -.75019800662994384765625f,
|
||||
-.724404990673065185546875f, -.6986110210418701171875f, -.6706349849700927734375f, -.61904799938201904296875f,
|
||||
-.567460000514984130859375f, -.515873014926910400390625f, -.4642859995365142822265625f, -.4126980006694793701171875f,
|
||||
-.361110985279083251953125f, -.309523999691009521484375f, -.257937014102935791015625f, -.20634900033473968505859375f,
|
||||
-.1547619998455047607421875f, -.10317499935626983642578125f, -.05158700048923492431640625f,
|
||||
+0.0f,
|
||||
+.05158700048923492431640625f, +.10317499935626983642578125f, +.1547619998455047607421875f, +.20634900033473968505859375f,
|
||||
+.257937014102935791015625f, +.309523999691009521484375f, +.361110985279083251953125f, +.4126980006694793701171875f,
|
||||
+.4642859995365142822265625f, +.515873014926910400390625f, +.567460000514984130859375f, +.61904799938201904296875f,
|
||||
+.6706349849700927734375f, +.6986110210418701171875f, +.724404990673065185546875f, +.75019800662994384765625f,
|
||||
+.775991976261138916015625f, +.801786005496978759765625f, +.827579021453857421875f, +.853372991085052490234375f,
|
||||
+.879167020320892333984375f, +.904959976673126220703125f, +.930754005908966064453125f, +.9516370296478271484375f,
|
||||
+.958085000514984130859375f, +.964533984661102294921875f, +.970982015132904052734375f, +.977430999279022216796875f,
|
||||
+.983879029750823974609375f, +.99032700061798095703125f, +.99677598476409912109375f
|
||||
};
|
||||
|
||||
static const uint8_t utk_codebooks[2][256] = {
|
||||
{ /* normal model */
|
||||
4, 6, 5, 9, 4, 6, 5, 13, 4, 6, 5, 10, 4, 6, 5, 17,
|
||||
4, 6, 5, 9, 4, 6, 5, 14, 4, 6, 5, 10, 4, 6, 5, 21,
|
||||
4, 6, 5, 9, 4, 6, 5, 13, 4, 6, 5, 10, 4, 6, 5, 18,
|
||||
4, 6, 5, 9, 4, 6, 5, 14, 4, 6, 5, 10, 4, 6, 5, 25,
|
||||
4, 6, 5, 9, 4, 6, 5, 13, 4, 6, 5, 10, 4, 6, 5, 17,
|
||||
4, 6, 5, 9, 4, 6, 5, 14, 4, 6, 5, 10, 4, 6, 5, 22,
|
||||
4, 6, 5, 9, 4, 6, 5, 13, 4, 6, 5, 10, 4, 6, 5, 18,
|
||||
4, 6, 5, 9, 4, 6, 5, 14, 4, 6, 5, 10, 4, 6, 5, 0,
|
||||
4, 6, 5, 9, 4, 6, 5, 13, 4, 6, 5, 10, 4, 6, 5, 17,
|
||||
4, 6, 5, 9, 4, 6, 5, 14, 4, 6, 5, 10, 4, 6, 5, 21,
|
||||
4, 6, 5, 9, 4, 6, 5, 13, 4, 6, 5, 10, 4, 6, 5, 18,
|
||||
4, 6, 5, 9, 4, 6, 5, 14, 4, 6, 5, 10, 4, 6, 5, 26,
|
||||
4, 6, 5, 9, 4, 6, 5, 13, 4, 6, 5, 10, 4, 6, 5, 17,
|
||||
4, 6, 5, 9, 4, 6, 5, 14, 4, 6, 5, 10, 4, 6, 5, 22,
|
||||
4, 6, 5, 9, 4, 6, 5, 13, 4, 6, 5, 10, 4, 6, 5, 18,
|
||||
4, 6, 5, 9, 4, 6, 5, 14, 4, 6, 5, 10, 4, 6, 5, 2
|
||||
}, { /* large-pulse model */
|
||||
4, 11, 7, 15, 4, 12, 8, 19, 4, 11, 7, 16, 4, 12, 8, 23,
|
||||
4, 11, 7, 15, 4, 12, 8, 20, 4, 11, 7, 16, 4, 12, 8, 27,
|
||||
4, 11, 7, 15, 4, 12, 8, 19, 4, 11, 7, 16, 4, 12, 8, 24,
|
||||
4, 11, 7, 15, 4, 12, 8, 20, 4, 11, 7, 16, 4, 12, 8, 1,
|
||||
4, 11, 7, 15, 4, 12, 8, 19, 4, 11, 7, 16, 4, 12, 8, 23,
|
||||
4, 11, 7, 15, 4, 12, 8, 20, 4, 11, 7, 16, 4, 12, 8, 28,
|
||||
4, 11, 7, 15, 4, 12, 8, 19, 4, 11, 7, 16, 4, 12, 8, 24,
|
||||
4, 11, 7, 15, 4, 12, 8, 20, 4, 11, 7, 16, 4, 12, 8, 3,
|
||||
4, 11, 7, 15, 4, 12, 8, 19, 4, 11, 7, 16, 4, 12, 8, 23,
|
||||
4, 11, 7, 15, 4, 12, 8, 20, 4, 11, 7, 16, 4, 12, 8, 27,
|
||||
4, 11, 7, 15, 4, 12, 8, 19, 4, 11, 7, 16, 4, 12, 8, 24,
|
||||
4, 11, 7, 15, 4, 12, 8, 20, 4, 11, 7, 16, 4, 12, 8, 1,
|
||||
4, 11, 7, 15, 4, 12, 8, 19, 4, 11, 7, 16, 4, 12, 8, 23,
|
||||
4, 11, 7, 15, 4, 12, 8, 20, 4, 11, 7, 16, 4, 12, 8, 28,
|
||||
4, 11, 7, 15, 4, 12, 8, 19, 4, 11, 7, 16, 4, 12, 8, 24,
|
||||
4, 11, 7, 15, 4, 12, 8, 20, 4, 11, 7, 16, 4, 12, 8, 3
|
||||
}
|
||||
};
|
||||
|
||||
static const struct {
|
||||
int next_model;
|
||||
int code_size;
|
||||
float pulse_value;
|
||||
} utk_commands[29] = {
|
||||
{MDL_LARGEPULSE, 8, 0.0f},
|
||||
{MDL_LARGEPULSE, 7, 0.0f},
|
||||
{MDL_NORMAL, 8, 0.0f},
|
||||
{MDL_NORMAL, 7, 0.0f},
|
||||
{MDL_NORMAL, 2, 0.0f},
|
||||
{MDL_NORMAL, 2, -1.0f},
|
||||
{MDL_NORMAL, 2, +1.0f},
|
||||
{MDL_NORMAL, 3, -1.0f},
|
||||
{MDL_NORMAL, 3, +1.0f},
|
||||
{MDL_LARGEPULSE, 4, -2.0f},
|
||||
{MDL_LARGEPULSE, 4, +2.0f},
|
||||
{MDL_LARGEPULSE, 3, -2.0f},
|
||||
{MDL_LARGEPULSE, 3, +2.0f},
|
||||
{MDL_LARGEPULSE, 5, -3.0f},
|
||||
{MDL_LARGEPULSE, 5, +3.0f},
|
||||
{MDL_LARGEPULSE, 4, -3.0f},
|
||||
{MDL_LARGEPULSE, 4, +3.0f},
|
||||
{MDL_LARGEPULSE, 6, -4.0f},
|
||||
{MDL_LARGEPULSE, 6, +4.0f},
|
||||
{MDL_LARGEPULSE, 5, -4.0f},
|
||||
{MDL_LARGEPULSE, 5, +4.0f},
|
||||
{MDL_LARGEPULSE, 7, -5.0f},
|
||||
{MDL_LARGEPULSE, 7, +5.0f},
|
||||
{MDL_LARGEPULSE, 6, -5.0f},
|
||||
{MDL_LARGEPULSE, 6, +5.0f},
|
||||
{MDL_LARGEPULSE, 8, -6.0f},
|
||||
{MDL_LARGEPULSE, 8, +6.0f},
|
||||
{MDL_LARGEPULSE, 7, -6.0f},
|
||||
{MDL_LARGEPULSE, 7, +6.0f}
|
||||
};
|
||||
|
||||
static int utk_read_byte(UTKContext *ctx)
|
||||
{
|
||||
if (ctx->ptr < ctx->end)
|
||||
return *ctx->ptr++;
|
||||
|
||||
if (ctx->read_callback) {
|
||||
size_t bytes_copied = ctx->read_callback(ctx->buffer, ctx->buffer_size, ctx->arg);
|
||||
if (bytes_copied > 0 && bytes_copied <= ctx->buffer_size) {
|
||||
ctx->ptr = ctx->buffer;
|
||||
ctx->end = ctx->buffer + bytes_copied;
|
||||
return *ctx->ptr++;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int16_t utk_read_i16(UTKContext *ctx)
|
||||
{
|
||||
int x = utk_read_byte(ctx);
|
||||
x = (x << 8) | utk_read_byte(ctx);
|
||||
return x;
|
||||
}
|
||||
|
||||
static int utk_read_bits(UTKContext *ctx, int count)
|
||||
{
|
||||
int ret = ctx->bits_value & ((1 << count) - 1);
|
||||
ctx->bits_value >>= count;
|
||||
ctx->bits_count -= count;
|
||||
|
||||
if (ctx->bits_count < 8) {
|
||||
/* read another byte */
|
||||
ctx->bits_value |= utk_read_byte(ctx) << ctx->bits_count;
|
||||
ctx->bits_count += 8;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void utk_parse_header(UTKContext *ctx)
|
||||
{
|
||||
int i;
|
||||
float multiplier;
|
||||
|
||||
ctx->reduced_bw = utk_read_bits(ctx, 1);
|
||||
ctx->multipulse_thresh = 32 - utk_read_bits(ctx, 4);
|
||||
ctx->fixed_gains[0] = 8.0f * (1 + utk_read_bits(ctx, 4));
|
||||
multiplier = 1.04f + utk_read_bits(ctx, 6)*0.001f;
|
||||
|
||||
for (i = 1; i < 64; i++)
|
||||
ctx->fixed_gains[i] = ctx->fixed_gains[i-1] * multiplier;
|
||||
}
|
||||
|
||||
static void utk_decode_excitation(UTKContext *ctx, int use_multipulse, float *out, int stride)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (use_multipulse) {
|
||||
/* multi-pulse model: n pulses are coded explicitly; the rest are zero */
|
||||
int model, cmd;
|
||||
model = 0;
|
||||
i = 0;
|
||||
while (i < 108) {
|
||||
cmd = utk_codebooks[model][ctx->bits_value & 0xff];
|
||||
model = utk_commands[cmd].next_model;
|
||||
utk_read_bits(ctx, utk_commands[cmd].code_size);
|
||||
|
||||
if (cmd > 3) {
|
||||
/* insert a pulse with magnitude <= 6.0f */
|
||||
out[i] = utk_commands[cmd].pulse_value;
|
||||
i += stride;
|
||||
} else if (cmd > 1) {
|
||||
/* insert between 7 and 70 zeros */
|
||||
int count = 7 + utk_read_bits(ctx, 6);
|
||||
if (i + count * stride > 108)
|
||||
count = (108 - i)/stride;
|
||||
|
||||
while (count > 0) {
|
||||
out[i] = 0.0f;
|
||||
i += stride;
|
||||
count--;
|
||||
}
|
||||
} else {
|
||||
/* insert a pulse with magnitude >= 7.0f */
|
||||
int x = 7;
|
||||
|
||||
while (utk_read_bits(ctx, 1))
|
||||
x++;
|
||||
|
||||
if (!utk_read_bits(ctx, 1))
|
||||
x *= -1;
|
||||
|
||||
out[i] = (float)x;
|
||||
i += stride;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* RELP model: entire residual (excitation) signal is coded explicitly */
|
||||
i = 0;
|
||||
while (i < 108) {
|
||||
if (!utk_read_bits(ctx, 1))
|
||||
out[i] = 0.0f;
|
||||
else if (!utk_read_bits(ctx, 1))
|
||||
out[i] = -2.0f;
|
||||
else
|
||||
out[i] = 2.0f;
|
||||
|
||||
i += stride;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void rc_to_lpc(const float *rc, float *lpc)
|
||||
{
|
||||
int i, j;
|
||||
float tmp1[12];
|
||||
float tmp2[12];
|
||||
|
||||
for (i = 10; i >= 0; i--)
|
||||
tmp2[1+i] = rc[i];
|
||||
|
||||
tmp2[0] = 1.0f;
|
||||
|
||||
for (i = 0; i < 12; i++) {
|
||||
float x = -tmp2[11] * rc[11];
|
||||
|
||||
for (j = 10; j >= 0; j--) {
|
||||
x -= tmp2[j] * rc[j];
|
||||
tmp2[j+1] = x * rc[j] + tmp2[j];
|
||||
}
|
||||
|
||||
tmp1[i] = tmp2[0] = x;
|
||||
|
||||
for (j = 0; j < i; j++)
|
||||
x -= tmp1[i-1-j] * lpc[j];
|
||||
|
||||
lpc[i] = x;
|
||||
}
|
||||
}
|
||||
|
||||
static void utk_lp_synthesis_filter(UTKContext *ctx, int offset, int num_blocks)
|
||||
{
|
||||
int i, j, k;
|
||||
float lpc[12];
|
||||
float *ptr = &ctx->decompressed_frame[offset];
|
||||
|
||||
rc_to_lpc(ctx->rc, lpc);
|
||||
|
||||
for (i = 0; i < num_blocks; i++) {
|
||||
for (j = 0; j < 12; j++) {
|
||||
float x = *ptr;
|
||||
|
||||
for (k = 0; k < j; k++)
|
||||
x += lpc[k] * ctx->synth_history[k-j+12];
|
||||
for (; k < 12; k++)
|
||||
x += lpc[k] * ctx->synth_history[k-j];
|
||||
|
||||
ctx->synth_history[11-j] = x;
|
||||
*ptr++ = x;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Public functions.
|
||||
*/
|
||||
|
||||
static int utk_decode_frame(UTKContext *ctx)
|
||||
{
|
||||
int i, j;
|
||||
int use_multipulse = 0;
|
||||
float excitation[5+108+5];
|
||||
float rc_delta[12];
|
||||
|
||||
if (!ctx->bits_count) {
|
||||
ctx->bits_value = utk_read_byte(ctx);
|
||||
ctx->bits_count = 8;
|
||||
}
|
||||
|
||||
if (!ctx->parsed_header) {
|
||||
utk_parse_header(ctx);
|
||||
ctx->parsed_header = 1;
|
||||
}
|
||||
|
||||
memset(&excitation[0], 0, 5*sizeof(float));
|
||||
memset(&excitation[5+108], 0, 5*sizeof(float));
|
||||
|
||||
/* read the reflection coefficients */
|
||||
for (i = 0; i < 12; i++) {
|
||||
int idx;
|
||||
if (i == 0) {
|
||||
idx = utk_read_bits(ctx, 6);
|
||||
if (idx < ctx->multipulse_thresh)
|
||||
use_multipulse = 1;
|
||||
} else if (i < 4) {
|
||||
idx = utk_read_bits(ctx, 6);
|
||||
} else {
|
||||
idx = 16 + utk_read_bits(ctx, 5);
|
||||
}
|
||||
|
||||
rc_delta[i] = (utk_rc_table[idx] - ctx->rc[i])*0.25f;
|
||||
}
|
||||
|
||||
/* decode four subframes */
|
||||
for (i = 0; i < 4; i++) {
|
||||
int pitch_lag = utk_read_bits(ctx, 8);
|
||||
float pitch_gain = (float)utk_read_bits(ctx, 4)/15.0f;
|
||||
float fixed_gain = ctx->fixed_gains[utk_read_bits(ctx, 6)];
|
||||
|
||||
if (!ctx->reduced_bw) {
|
||||
utk_decode_excitation(ctx, use_multipulse, &excitation[5], 1);
|
||||
} else {
|
||||
/* residual (excitation) signal is encoded at reduced bandwidth */
|
||||
int align = utk_read_bits(ctx, 1);
|
||||
int zero = utk_read_bits(ctx, 1);
|
||||
|
||||
utk_decode_excitation(ctx, use_multipulse, &excitation[5+align], 2);
|
||||
|
||||
if (zero) {
|
||||
/* fill the remaining samples with zero
|
||||
** (spectrum is duplicated into high frequencies) */
|
||||
for (j = 0; j < 54; j++)
|
||||
excitation[5+(1-align)+2*j] = 0.0f;
|
||||
} else {
|
||||
/* interpolate the remaining samples
|
||||
** (spectrum is low-pass filtered) */
|
||||
float *ptr = &excitation[5+(1-align)];
|
||||
for (j = 0; j < 108; j += 2)
|
||||
ptr[j] = ptr[j-5] * 0.01803267933428287506103515625f
|
||||
- ptr[j-3] * 0.114591561257839202880859375f
|
||||
+ ptr[j-1] * 0.597385942935943603515625f
|
||||
+ ptr[j+1] * 0.597385942935943603515625f
|
||||
- ptr[j+3] * 0.114591561257839202880859375f
|
||||
+ ptr[j+5] * 0.01803267933428287506103515625f;
|
||||
|
||||
/* scale by 0.5f to give the sinc impulse response unit energy */
|
||||
fixed_gain *= 0.5f;
|
||||
}
|
||||
}
|
||||
|
||||
for (j = 0; j < 108; j++)
|
||||
ctx->decompressed_frame[108*i+j] = fixed_gain * excitation[5+j]
|
||||
+ pitch_gain * ctx->adapt_cb[108*i+216-pitch_lag+j];
|
||||
}
|
||||
|
||||
for (i = 0; i < 324; i++)
|
||||
ctx->adapt_cb[i] = ctx->decompressed_frame[108+i];
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
for (j = 0; j < 12; j++)
|
||||
ctx->rc[j] += rc_delta[j];
|
||||
|
||||
utk_lp_synthesis_filter(ctx, 12*i, i < 3 ? 1 : 33);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void utk_init(UTKContext *ctx)
|
||||
{
|
||||
memset(ctx, 0, sizeof(*ctx));
|
||||
}
|
||||
|
||||
static void utk_reset(UTKContext *ctx)
|
||||
{
|
||||
/* resets the internal state, leaving the external config/buffers
|
||||
* untouched (could be reset externally or using utk_set_x) */
|
||||
ctx->parsed_header = 0;
|
||||
ctx->bits_value = 0;
|
||||
ctx->bits_count = 0;
|
||||
ctx->reduced_bw = 0;
|
||||
ctx->multipulse_thresh = 0;
|
||||
memset(ctx->fixed_gains, 0, sizeof(ctx->fixed_gains));
|
||||
memset(ctx->rc, 0, sizeof(ctx->rc));
|
||||
memset(ctx->synth_history, 0, sizeof(ctx->synth_history));
|
||||
memset(ctx->adapt_cb, 0, sizeof(ctx->adapt_cb));
|
||||
memset(ctx->decompressed_frame, 0, sizeof(ctx->decompressed_frame));
|
||||
}
|
||||
|
||||
static void utk_set_callback(UTKContext *ctx, uint8_t *buffer, size_t buffer_size, void *arg, size_t (*read_callback)(void *, int , void *))
|
||||
{
|
||||
/* prepares for external reading */
|
||||
ctx->buffer = buffer;
|
||||
ctx->buffer_size = buffer_size;
|
||||
ctx->arg = arg;
|
||||
ctx->read_callback = read_callback;
|
||||
|
||||
/* reset the bit reader */
|
||||
ctx->bits_count = 0;
|
||||
}
|
||||
|
||||
static void utk_set_ptr(UTKContext *ctx, const uint8_t *ptr, const uint8_t *end)
|
||||
{
|
||||
/* sets the pointer to an external data buffer (can also be used to
|
||||
* reset the buffered data if set to ptr/end 0) */
|
||||
ctx->ptr = ptr;
|
||||
ctx->end = end;
|
||||
|
||||
/* reset the bit reader */
|
||||
ctx->bits_count = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** MicroTalk Revision 3 decoding function.
|
||||
*/
|
||||
|
||||
static int utk_rev3_decode_frame(UTKContext *ctx)
|
||||
{
|
||||
int pcm_data_present = (utk_read_byte(ctx) == 0xee);
|
||||
int i;
|
||||
|
||||
utk_decode_frame(ctx);
|
||||
|
||||
/* unread the last 8 bits and reset the bit reader */
|
||||
ctx->ptr--;
|
||||
ctx->bits_count = 0;
|
||||
|
||||
if (pcm_data_present) {
|
||||
/* Overwrite n samples at a given offset in the decoded frame with
|
||||
** raw PCM data. */
|
||||
int offset = utk_read_i16(ctx);
|
||||
int count = utk_read_i16(ctx);
|
||||
|
||||
/* sx.exe does not do any bounds checking or clamping of these two
|
||||
** fields (see 004274D1 in sx.exe v3.01.01), which means a specially
|
||||
** crafted MT5:1 file can crash sx.exe.
|
||||
** We will throw an error instead. */
|
||||
if (offset < 0 || offset > 432) {
|
||||
return -1; /* invalid PCM offset */
|
||||
}
|
||||
if (count < 0 || count > 432 - offset) {
|
||||
return -2; /* invalid PCM count */
|
||||
}
|
||||
|
||||
for (i = 0; i < count; i++)
|
||||
ctx->decompressed_frame[offset+i] = (float)utk_read_i16(ctx);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* _EA_MT_DECODER_UTK_H_ */
|
607
src/coding/libs/utkdec.c
Normal file
607
src/coding/libs/utkdec.c
Normal file
@ -0,0 +1,607 @@
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "utkdec.h"
|
||||
|
||||
|
||||
struct utk_context_t {
|
||||
/* config */
|
||||
utk_type_t type;
|
||||
int parsed_header;
|
||||
|
||||
/* state */
|
||||
struct bitreader_t {
|
||||
const uint8_t* ptr;
|
||||
uint32_t bits_value;
|
||||
int bits_count;
|
||||
/* extra (OG MT/CBX just loads ptr memory externally) */
|
||||
const uint8_t* end;
|
||||
void* arg;
|
||||
uint8_t* buffer;
|
||||
size_t buffer_size;
|
||||
size_t (*read_callback)(void* dst, int size, void* arg);
|
||||
} br;
|
||||
bool reduced_bandwidth;
|
||||
int multipulse_threshold;
|
||||
|
||||
float fixed_gains[64];
|
||||
float rc_data[12];
|
||||
float synth_history[12];
|
||||
float subframes[324 + 432];
|
||||
/* adapt_cb indexes may read from samples, join both + ptr to avoid
|
||||
* struct aligment issues (typically doesn't matter but for completeness) */
|
||||
float* adapt_cb; /* subframes + 0 */
|
||||
float* samples; /* subframes + 324 */
|
||||
};
|
||||
|
||||
|
||||
/* bit mask; (1 << count) - 1 is probably faster now but OG code uses a table */
|
||||
static const uint8_t mask_table[8] = {
|
||||
0x01,0x03,0x07,0x0F,0x1F,0x3F,0x7F,0xFF
|
||||
};
|
||||
|
||||
/* reflection coefficients, rounded that correspond to hex values in exes (actual float is longer)
|
||||
* note this table is mirrored: for (i = 1 .. 32) t[64 - i] = -t[i]) */
|
||||
static const float utk_rc_table[64] = {
|
||||
/* 6b index start */
|
||||
+0.000000f, -0.996776f, -0.990327f, -0.983879f,
|
||||
-0.977431f, -0.970982f, -0.964534f, -0.958085f,
|
||||
-0.951637f, -0.930754f, -0.904960f, -0.879167f,
|
||||
-0.853373f, -0.827579f, -0.801786f, -0.775992f,
|
||||
/* 5b index start */
|
||||
-0.750198f, -0.724405f, -0.698611f, -0.670635f,
|
||||
-0.619048f, -0.567460f, -0.515873f, -0.464286f,
|
||||
-0.412698f, -0.361111f, -0.309524f, -0.257937f,
|
||||
-0.206349f, -0.154762f, -0.103175f, -0.051587f,
|
||||
+0.000000f, +0.051587f, +0.103175f, +0.154762f,
|
||||
+0.206349f, +0.257937f, +0.309524f, +0.361111f,
|
||||
+0.412698f, +0.464286f, +0.515873f, +0.567460f,
|
||||
+0.619048f, +0.670635f, +0.698611f, +0.724405f,
|
||||
+0.750198f, +0.775992f, +0.801786f, +0.827579f,
|
||||
+0.853373f, +0.879167f, +0.904960f, +0.930754f,
|
||||
+0.951637f, +0.958085f, +0.964534f, +0.970982f,
|
||||
+0.977431f, +0.983879f, +0.990327f, +0.996776f,
|
||||
};
|
||||
|
||||
static const uint8_t utk_codebooks[2][256] = {
|
||||
/* normal model */
|
||||
{
|
||||
4, 6, 5, 9, 4, 6, 5, 13, 4, 6, 5, 10, 4, 6, 5, 17,
|
||||
4, 6, 5, 9, 4, 6, 5, 14, 4, 6, 5, 10, 4, 6, 5, 21,
|
||||
4, 6, 5, 9, 4, 6, 5, 13, 4, 6, 5, 10, 4, 6, 5, 18,
|
||||
4, 6, 5, 9, 4, 6, 5, 14, 4, 6, 5, 10, 4, 6, 5, 25,
|
||||
4, 6, 5, 9, 4, 6, 5, 13, 4, 6, 5, 10, 4, 6, 5, 17,
|
||||
4, 6, 5, 9, 4, 6, 5, 14, 4, 6, 5, 10, 4, 6, 5, 22,
|
||||
4, 6, 5, 9, 4, 6, 5, 13, 4, 6, 5, 10, 4, 6, 5, 18,
|
||||
4, 6, 5, 9, 4, 6, 5, 14, 4, 6, 5, 10, 4, 6, 5, 0,
|
||||
4, 6, 5, 9, 4, 6, 5, 13, 4, 6, 5, 10, 4, 6, 5, 17,
|
||||
4, 6, 5, 9, 4, 6, 5, 14, 4, 6, 5, 10, 4, 6, 5, 21,
|
||||
4, 6, 5, 9, 4, 6, 5, 13, 4, 6, 5, 10, 4, 6, 5, 18,
|
||||
4, 6, 5, 9, 4, 6, 5, 14, 4, 6, 5, 10, 4, 6, 5, 26,
|
||||
4, 6, 5, 9, 4, 6, 5, 13, 4, 6, 5, 10, 4, 6, 5, 17,
|
||||
4, 6, 5, 9, 4, 6, 5, 14, 4, 6, 5, 10, 4, 6, 5, 22,
|
||||
4, 6, 5, 9, 4, 6, 5, 13, 4, 6, 5, 10, 4, 6, 5, 18,
|
||||
4, 6, 5, 9, 4, 6, 5, 14, 4, 6, 5, 10, 4, 6, 5, 2
|
||||
},
|
||||
/* large-pulse model */
|
||||
{
|
||||
4, 11, 7, 15, 4, 12, 8, 19, 4, 11, 7, 16, 4, 12, 8, 23,
|
||||
4, 11, 7, 15, 4, 12, 8, 20, 4, 11, 7, 16, 4, 12, 8, 27,
|
||||
4, 11, 7, 15, 4, 12, 8, 19, 4, 11, 7, 16, 4, 12, 8, 24,
|
||||
4, 11, 7, 15, 4, 12, 8, 20, 4, 11, 7, 16, 4, 12, 8, 1,
|
||||
4, 11, 7, 15, 4, 12, 8, 19, 4, 11, 7, 16, 4, 12, 8, 23,
|
||||
4, 11, 7, 15, 4, 12, 8, 20, 4, 11, 7, 16, 4, 12, 8, 28,
|
||||
4, 11, 7, 15, 4, 12, 8, 19, 4, 11, 7, 16, 4, 12, 8, 24,
|
||||
4, 11, 7, 15, 4, 12, 8, 20, 4, 11, 7, 16, 4, 12, 8, 3,
|
||||
4, 11, 7, 15, 4, 12, 8, 19, 4, 11, 7, 16, 4, 12, 8, 23,
|
||||
4, 11, 7, 15, 4, 12, 8, 20, 4, 11, 7, 16, 4, 12, 8, 27,
|
||||
4, 11, 7, 15, 4, 12, 8, 19, 4, 11, 7, 16, 4, 12, 8, 24,
|
||||
4, 11, 7, 15, 4, 12, 8, 20, 4, 11, 7, 16, 4, 12, 8, 1,
|
||||
4, 11, 7, 15, 4, 12, 8, 19, 4, 11, 7, 16, 4, 12, 8, 23,
|
||||
4, 11, 7, 15, 4, 12, 8, 20, 4, 11, 7, 16, 4, 12, 8, 28,
|
||||
4, 11, 7, 15, 4, 12, 8, 19, 4, 11, 7, 16, 4, 12, 8, 24,
|
||||
4, 11, 7, 15, 4, 12, 8, 20, 4, 11, 7, 16, 4, 12, 8, 3
|
||||
},
|
||||
};
|
||||
|
||||
enum {
|
||||
MDL_NORMAL = 0,
|
||||
MDL_LARGEPULSE = 1
|
||||
};
|
||||
|
||||
static const struct {
|
||||
int next_model;
|
||||
int code_size;
|
||||
float pulse_value;
|
||||
} utk_commands[29] = {
|
||||
{MDL_LARGEPULSE, 8, 0.0f},
|
||||
{MDL_LARGEPULSE, 7, 0.0f},
|
||||
{MDL_NORMAL, 8, 0.0f},
|
||||
{MDL_NORMAL, 7, 0.0f},
|
||||
{MDL_NORMAL, 2, 0.0f},
|
||||
{MDL_NORMAL, 2, -1.0f},
|
||||
{MDL_NORMAL, 2, +1.0f},
|
||||
{MDL_NORMAL, 3, -1.0f},
|
||||
{MDL_NORMAL, 3, +1.0f},
|
||||
{MDL_LARGEPULSE, 4, -2.0f},
|
||||
{MDL_LARGEPULSE, 4, +2.0f},
|
||||
{MDL_LARGEPULSE, 3, -2.0f},
|
||||
{MDL_LARGEPULSE, 3, +2.0f},
|
||||
{MDL_LARGEPULSE, 5, -3.0f},
|
||||
{MDL_LARGEPULSE, 5, +3.0f},
|
||||
{MDL_LARGEPULSE, 4, -3.0f},
|
||||
{MDL_LARGEPULSE, 4, +3.0f},
|
||||
{MDL_LARGEPULSE, 6, -4.0f},
|
||||
{MDL_LARGEPULSE, 6, +4.0f},
|
||||
{MDL_LARGEPULSE, 5, -4.0f},
|
||||
{MDL_LARGEPULSE, 5, +4.0f},
|
||||
{MDL_LARGEPULSE, 7, -5.0f},
|
||||
{MDL_LARGEPULSE, 7, +5.0f},
|
||||
{MDL_LARGEPULSE, 6, -5.0f},
|
||||
{MDL_LARGEPULSE, 6, +5.0f},
|
||||
{MDL_LARGEPULSE, 8, -6.0f},
|
||||
{MDL_LARGEPULSE, 8, +6.0f},
|
||||
{MDL_LARGEPULSE, 7, -6.0f},
|
||||
{MDL_LARGEPULSE, 7, +6.0f}
|
||||
};
|
||||
|
||||
/* In Lego Batman 2 gain[0] = 1.068 while other games (Lego Marvel, Lego SW) is 64.0f.
|
||||
* The latter makes more sense and the former is audibly worse so it was probably a bug. */
|
||||
static const float cbx_fixed_gains[64] = {
|
||||
64.0f, 68.351997f, 72.999931f, 77.963921f,
|
||||
83.265465f, 88.927513f, 94.974579f, 101.43285f,
|
||||
108.33028f, 115.69673f, 123.5641f, 131.96646f,
|
||||
140.94017f, 150.52409f, 160.75972f, 171.69138f,
|
||||
183.36638f, 195.83528f, 209.15207f, 223.3744f,
|
||||
238.56386f, 254.78619f, 272.11163f, 290.6152f,
|
||||
310.37701f, 331.48264f, 354.02344f, 378.09702f,
|
||||
403.80759f, 431.26648f, 460.59259f, 491.91287f,
|
||||
525.36292f, 561.08759f, 599.24152f, 639.98993f,
|
||||
683.50922f, 729.98779f, 779.62695f, 832.64154f,
|
||||
889.26111f, 949.73083f, 1014.3125f, 1083.2858f,
|
||||
1156.9491f, 1235.6216f, 1319.6438f, 1409.3795f,
|
||||
1505.2173f, 1607.572f, 1716.8868f, 1833.6351f,
|
||||
1958.3223f, 2091.488f, 2233.7092f, 2385.6013f,
|
||||
2547.822f, 2721.0737f, 2906.1067f, 3103.7219f,
|
||||
3314.7749f, 3540.1794f, 3780.9116f, 4038.0134f,
|
||||
};
|
||||
|
||||
/* Bitreader in OG code can only read from set ptr; doesn't seem to check bounds though.
|
||||
* Incidentally bitreader functions seem to be used only in MT and not in other EA stuff. */
|
||||
static uint8_t read_byte(struct bitreader_t* br) {
|
||||
if (br->ptr < br->end)
|
||||
return *br->ptr++;
|
||||
|
||||
if (br->read_callback) {
|
||||
size_t bytes_copied = br->read_callback(br->buffer, br->buffer_size, br->arg);
|
||||
if (bytes_copied > 0 && bytes_copied <= br->buffer_size) {
|
||||
br->ptr = br->buffer;
|
||||
br->end = br->buffer + bytes_copied;
|
||||
return *br->ptr++;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int16_t read_s16(struct bitreader_t* br) {
|
||||
int x = read_byte(br);
|
||||
x = (x << 8) | read_byte(br);
|
||||
return x;
|
||||
}
|
||||
|
||||
static void init_bits(struct bitreader_t* br) {
|
||||
if (!br->bits_count) {
|
||||
br->bits_value = read_byte(br);
|
||||
br->bits_count = 8;
|
||||
}
|
||||
}
|
||||
|
||||
static uint8_t peek_bits(struct bitreader_t* br, int count) {
|
||||
uint8_t mask = mask_table[count - 1];
|
||||
return br->bits_value & mask;
|
||||
}
|
||||
|
||||
/* assumes count <= 8, which is always true since sizes are known and don't depend on the bitstream. */
|
||||
static uint8_t read_bits(struct bitreader_t* br, int count) {
|
||||
uint8_t mask = mask_table[count - 1];
|
||||
uint8_t ret = br->bits_value & mask;
|
||||
br->bits_value >>= count;
|
||||
br->bits_count -= count;
|
||||
|
||||
if (br->bits_count < 8) {
|
||||
/* read another byte */
|
||||
br->bits_value |= read_byte(br) << br->bits_count;
|
||||
br->bits_count += 8;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* for clarity, as found in OG code (no return) */
|
||||
static void consume_bits(struct bitreader_t* br, int count) {
|
||||
read_bits(br, count);
|
||||
}
|
||||
|
||||
static void parse_header(utk_context_t* ctx) {
|
||||
if (ctx->type == UTK_CBX) {
|
||||
/* CBX uses fixed parameters unlike EA-MT, probably encoder defaults for MT10:1
|
||||
* equivalent to EA-MT with base_thre = 8, base_gain = 7, base_mult = 28 (plus rounding diffs).
|
||||
* OG CBX code uses values/tables directly rather than config though */
|
||||
ctx->reduced_bandwidth = true;
|
||||
|
||||
ctx->multipulse_threshold = 32 - 8;
|
||||
|
||||
ctx->fixed_gains[0] = cbx_fixed_gains[0];
|
||||
for (int i = 1; i < 64; i++) {
|
||||
ctx->fixed_gains[i] = cbx_fixed_gains[i];
|
||||
}
|
||||
}
|
||||
else {
|
||||
ctx->reduced_bandwidth = read_bits(&ctx->br, 1) == 1;
|
||||
|
||||
int base_thre = read_bits(&ctx->br, 4);
|
||||
int base_gain = read_bits(&ctx->br, 4);
|
||||
int base_mult = read_bits(&ctx->br, 6);
|
||||
|
||||
ctx->multipulse_threshold = 32 - base_thre;
|
||||
ctx->fixed_gains[0] = 8.0f * (1 + base_gain);
|
||||
|
||||
float multiplier = 1.04f + base_mult * 0.001f;
|
||||
for (int i = 1; i < 64; i++) {
|
||||
ctx->fixed_gains[i] = ctx->fixed_gains[i-1] * multiplier;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void decode_excitation(utk_context_t* ctx, bool use_multipulse, float* out, int stride) {
|
||||
int i = 0;
|
||||
|
||||
if (use_multipulse) {
|
||||
/* multi-pulse model: n pulses are coded explicitly; the rest are zero */
|
||||
int model = 0;
|
||||
while (i < 108) {
|
||||
int huffman_code = peek_bits(&ctx->br, 8); /* variable-length, may consume less */
|
||||
|
||||
int cmd = utk_codebooks[model][huffman_code];
|
||||
model = utk_commands[cmd].next_model;
|
||||
|
||||
consume_bits(&ctx->br, utk_commands[cmd].code_size);
|
||||
|
||||
if (cmd > 3) {
|
||||
/* insert a pulse with magnitude <= 6.0f */
|
||||
out[i] = utk_commands[cmd].pulse_value;
|
||||
i += stride;
|
||||
}
|
||||
else if (cmd > 1) {
|
||||
/* insert between 7 and 70 zeros */
|
||||
int count = 7 + read_bits(&ctx->br, 6);
|
||||
if (i + count * stride > 108)
|
||||
count = (108 - i) / stride;
|
||||
|
||||
while (count > 0) {
|
||||
out[i] = 0.0f;
|
||||
i += stride;
|
||||
count--;
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* insert a pulse with magnitude >= 7.0f */
|
||||
int x = 7;
|
||||
|
||||
while (read_bits(&ctx->br, 1)) {
|
||||
x++;
|
||||
}
|
||||
|
||||
if (!read_bits(&ctx->br, 1))
|
||||
x *= -1;
|
||||
|
||||
out[i] = (float)x;
|
||||
i += stride;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* RELP model: entire residual (excitation) signal is coded explicitly */
|
||||
while (i < 108) {
|
||||
int bits = 0;
|
||||
float val = 0.0f;
|
||||
|
||||
/* peek + partial consume code (odd to use 2 codes for 0.0 but seen in multiple exes) */
|
||||
int huffman_code = peek_bits(&ctx->br, 2); /* variable-length, may consume less */
|
||||
switch (huffman_code) {
|
||||
case 0: //code: 0
|
||||
case 2: //code: 1 (maybe meant to be -0.0?)
|
||||
val = 0.0f;
|
||||
bits = 1;
|
||||
break;
|
||||
case 1: //code: 01
|
||||
val = -2.0f;
|
||||
bits = 2;
|
||||
break;
|
||||
case 3: //code: 11
|
||||
val = 2.0f;
|
||||
bits = 2;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
consume_bits(&ctx->br, bits);
|
||||
|
||||
out[i] = val;
|
||||
i += stride;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void rc_to_lpc(const float* rc_data, float* lpc) {
|
||||
int j;
|
||||
float tmp1[12];
|
||||
float tmp2[12];
|
||||
|
||||
for (int i = 10; i >= 0; i--) {
|
||||
tmp2[i + 1] = rc_data[i];
|
||||
}
|
||||
|
||||
tmp2[0] = 1.0f;
|
||||
|
||||
for (int i = 0; i < 12; i++) {
|
||||
float x = -(rc_data[11] * tmp2[11]);
|
||||
|
||||
for (j = 10; j >= 0; j--) {
|
||||
x -= (rc_data[j] * tmp2[j]);
|
||||
tmp2[j + 1] = x * rc_data[j] + tmp2[j];
|
||||
}
|
||||
|
||||
tmp2[0] = x;
|
||||
tmp1[i] = x;
|
||||
|
||||
for (j = 0; j < i; j++) {
|
||||
x -= tmp1[i - 1 - j] * lpc[j];
|
||||
}
|
||||
|
||||
lpc[i] = x;
|
||||
}
|
||||
}
|
||||
|
||||
static void lp_synthesis_filter(utk_context_t* ctx, int offset, int blocks) {
|
||||
int i, j, k;
|
||||
float lpc[12];
|
||||
float* ptr = &ctx->samples[offset];
|
||||
|
||||
rc_to_lpc(ctx->rc_data, lpc);
|
||||
|
||||
for (i = 0; i < blocks; i++) {
|
||||
/* OG: unrolled x12*12 */
|
||||
for (j = 0; j < 12; j++) {
|
||||
float x = *ptr;
|
||||
|
||||
for (k = 0; k < j; k++) {
|
||||
x += lpc[k] * ctx->synth_history[k - j + 12];
|
||||
}
|
||||
for (; k < 12; k++) {
|
||||
x += lpc[k] * ctx->synth_history[k - j + 0];
|
||||
}
|
||||
|
||||
ctx->synth_history[11 - j] = x;
|
||||
|
||||
*ptr++ = x;
|
||||
|
||||
/* CBX only: samples are multiplied by 12582912.0, then coerce_int(sample[i]) on output
|
||||
* to get final int16, as a pseudo-optimization; not sure if worth replicating */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* OG sometimes inlines this (sx3, not B&B/CBX) */
|
||||
static void interpolate_rest(float* excitation) {
|
||||
for (int i = 0; i < 108; i += 2) {
|
||||
float tmp1 = (excitation[i - 5] + excitation[i + 5]) * 0.01803268f;
|
||||
float tmp2 = (excitation[i - 3] + excitation[i + 3]) * 0.11459156f;
|
||||
float tmp3 = (excitation[i - 1] + excitation[i + 1]) * 0.59738597f;
|
||||
excitation[i] = tmp1 - tmp2 + tmp3;
|
||||
}
|
||||
}
|
||||
|
||||
static void decode_frame_main(utk_context_t* ctx) {
|
||||
bool use_multipulse = false;
|
||||
float excitation[5 + 108 + 5]; /* extra +5*2 for interpolation */
|
||||
float rc_delta[12];
|
||||
|
||||
/* OG code usually calls this init/parse header after creation rather than on frame decode,
|
||||
* but use a flag for now since buffer can be set/reset after init */
|
||||
init_bits(&ctx->br);
|
||||
|
||||
if (!ctx->parsed_header) {
|
||||
parse_header(ctx);
|
||||
ctx->parsed_header = 1;
|
||||
}
|
||||
|
||||
|
||||
/* read the reflection coefficients (OG unrolled) */
|
||||
for (int i = 0; i < 12; i++) {
|
||||
int idx;
|
||||
if (i == 0) {
|
||||
idx = read_bits(&ctx->br, 6);
|
||||
if (idx < ctx->multipulse_threshold)
|
||||
use_multipulse = true;
|
||||
}
|
||||
else if (i < 4) {
|
||||
idx = read_bits(&ctx->br, 6);
|
||||
}
|
||||
else {
|
||||
idx = 16 + read_bits(&ctx->br, 5);
|
||||
}
|
||||
|
||||
rc_delta[i] = (utk_rc_table[idx] - ctx->rc_data[i]) * 0.25f;
|
||||
}
|
||||
|
||||
/* decode four subframes */
|
||||
for (int i = 0; i < 4; i++) {
|
||||
int pitch_lag = read_bits(&ctx->br, 8);
|
||||
int pitch_value = read_bits(&ctx->br, 4);
|
||||
int gain_index = read_bits(&ctx->br, 6);
|
||||
|
||||
float pitch_gain = (float)pitch_value / 15.0f; /* may be compiled as: value * 0.6..67 (float or double) */
|
||||
float fixed_gain = ctx->fixed_gains[gain_index];
|
||||
|
||||
if (!ctx->reduced_bandwidth) {
|
||||
/* full bandwidth (probably MT5:1) */
|
||||
decode_excitation(ctx, use_multipulse, &excitation[5 + 0], 1);
|
||||
/* OG: CBX doesn't have this flag and removes the if (so not 100% same code as MT) */
|
||||
}
|
||||
else {
|
||||
/* residual (excitation) signal is encoded at reduced bandwidth */
|
||||
int align = read_bits(&ctx->br, 1);
|
||||
int zero_flag = read_bits(&ctx->br, 1);
|
||||
|
||||
decode_excitation(ctx, use_multipulse, &excitation[5 + align], 2);
|
||||
|
||||
if (zero_flag) {
|
||||
/* fill the remaining samples with zero (spectrum is duplicated into high frequencies) */
|
||||
for (int j = 0; j < 54; j++) {
|
||||
excitation[5 + (1 - align) + 2 * j] = 0.0f;
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* 0'd first + last samples for interpolation */
|
||||
memset(&excitation[0], 0, 5 * sizeof(float));
|
||||
memset(&excitation[5 + 108], 0, 5 * sizeof(float));
|
||||
|
||||
/* interpolate the remaining samples (spectrum is low-pass filtered) */
|
||||
interpolate_rest(&excitation[5 + (1 - align)]);
|
||||
|
||||
/* scale by 0.5f to give the sinc impulse response unit energy */
|
||||
fixed_gain *= 0.5f;
|
||||
}
|
||||
}
|
||||
|
||||
/* OG: sometimes unrolled */
|
||||
for (int j = 0; j < 108; j++) {
|
||||
/* This has potential to read garbage from fixed_gains/samples (-39 ~ +648). The former
|
||||
* seems avoided by the encoder but we'll clamp it just in case, while the later is common
|
||||
* and seemingly used on purpose, so it's allowed via joining adapt_cb + samples bufs. */
|
||||
int idx = 108 * i + 216 - pitch_lag + j;
|
||||
if (idx < 0) /* OG: not done but shouldn't matter */
|
||||
idx = 0;
|
||||
|
||||
float tmp1 = fixed_gain * excitation[5 + j];
|
||||
float tmp2 = pitch_gain * ctx->adapt_cb[idx];
|
||||
ctx->samples[108 * i + j] = tmp1 + tmp2;
|
||||
}
|
||||
}
|
||||
|
||||
/* OG: may be compiler-optimized to memcpy */
|
||||
for (int i = 0; i < 324; i++) {
|
||||
ctx->adapt_cb[i] = ctx->samples[108 + i];
|
||||
}
|
||||
|
||||
/* OG: unrolled x4 */
|
||||
for (int i = 0; i < 4; i++) {
|
||||
for (int j = 0; j < 12; j++) {
|
||||
ctx->rc_data[j] += rc_delta[j];
|
||||
}
|
||||
|
||||
int blocks = i < 3 ? 1 : 33;
|
||||
lp_synthesis_filter(ctx, 12 * i, blocks);
|
||||
}
|
||||
}
|
||||
|
||||
static int decode_frame_pcm(utk_context_t* ctx) {
|
||||
int pcm_data_present = (read_byte(&ctx->br) == 0xEE);
|
||||
int i;
|
||||
|
||||
decode_frame_main(ctx);
|
||||
|
||||
/* unread the last 8 bits and reset the bit reader
|
||||
* (a bit odd but should be safe in all cases, assuming ptr has been set) */
|
||||
ctx->br.ptr--;
|
||||
ctx->br.bits_count = 0;
|
||||
|
||||
if (pcm_data_present) {
|
||||
/* Overwrite n samples at a given offset in the decoded frame with raw PCM data. */
|
||||
int offset = read_s16(&ctx->br);
|
||||
int count = read_s16(&ctx->br);
|
||||
|
||||
/* sx.exe does not do any bounds checking or clamping of these two
|
||||
* fields (see 004274D1 in sx.exe v3.01.01), which means a specially
|
||||
* crafted MT5:1 file can crash it. We will throw an error instead. */
|
||||
if (offset < 0 || offset > 432) {
|
||||
return -1; /* invalid PCM offset */
|
||||
}
|
||||
if (count < 0 || count > 432 - offset) {
|
||||
return -2; /* invalid PCM count */
|
||||
}
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
ctx->samples[offset+i] = (float)read_s16(&ctx->br);
|
||||
}
|
||||
}
|
||||
|
||||
return 432;
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
int utk_decode_frame(utk_context_t* ctx) {
|
||||
if (ctx->type == UTK_EA_PCM) {
|
||||
return decode_frame_pcm(ctx);
|
||||
}
|
||||
else {
|
||||
decode_frame_main(ctx);
|
||||
return 432;
|
||||
}
|
||||
}
|
||||
|
||||
utk_context_t* utk_init(utk_type_t type) {
|
||||
utk_context_t* ctx = calloc(1, sizeof(utk_context_t));
|
||||
if (!ctx) return NULL;
|
||||
|
||||
//memset(ctx, 0, sizeof(*ctx));
|
||||
ctx->type = type;
|
||||
|
||||
ctx->adapt_cb = ctx->subframes + 0;
|
||||
ctx->samples = ctx->subframes + 324;
|
||||
|
||||
return ctx;
|
||||
}
|
||||
|
||||
void utk_free(utk_context_t* ctx) {
|
||||
free(ctx);
|
||||
}
|
||||
|
||||
void utk_reset(utk_context_t* ctx) {
|
||||
/* resets the internal state, leaving the external config/buffers
|
||||
* untouched (could be reset externally or using utk_set_x) */
|
||||
ctx->parsed_header = 0;
|
||||
ctx->br.bits_value = 0;
|
||||
ctx->br.bits_count = 0;
|
||||
ctx->reduced_bandwidth = 0;
|
||||
ctx->multipulse_threshold = 0;
|
||||
memset(ctx->fixed_gains, 0, sizeof(ctx->fixed_gains));
|
||||
memset(ctx->rc_data, 0, sizeof(ctx->rc_data));
|
||||
memset(ctx->synth_history, 0, sizeof(ctx->synth_history));
|
||||
memset(ctx->subframes, 0, sizeof(ctx->subframes));
|
||||
}
|
||||
|
||||
void utk_set_callback(utk_context_t* ctx, uint8_t* buffer, size_t buffer_size, void *arg, size_t (*read_callback)(void *, int , void *)) {
|
||||
ctx->br.buffer = buffer;
|
||||
ctx->br.buffer_size = buffer_size;
|
||||
ctx->br.arg = arg;
|
||||
ctx->br.read_callback = read_callback;
|
||||
|
||||
/* reset the bit reader */
|
||||
ctx->br.bits_count = 0;
|
||||
}
|
||||
|
||||
void utk_set_buffer(utk_context_t* ctx, const uint8_t* buf, size_t buf_size) {
|
||||
ctx->br.ptr = buf;
|
||||
ctx->br.end = buf + buf_size;
|
||||
|
||||
/* reset the bit reader */
|
||||
ctx->br.bits_count = 0;
|
||||
}
|
||||
|
||||
float* utk_get_samples(utk_context_t* ctx) {
|
||||
return ctx->samples;
|
||||
}
|
48
src/coding/libs/utkdec.h
Normal file
48
src/coding/libs/utkdec.h
Normal file
@ -0,0 +1,48 @@
|
||||
#ifndef _UTKDEK_H_
|
||||
#define _UTKDEK_H_
|
||||
#include <stdint.h>
|
||||
|
||||
/* Decodes Electronic Arts' MicroTalk (a multipulse CELP/RELP speech codec) using utkencode lib,
|
||||
* slightly modified for vgmstream based on decompilation of EA and CBX code.
|
||||
* Original by Andrew D'Addesio: https://github.com/daddesio/utkencode (UNLICENSE/public domain)
|
||||
* Info: http://wiki.niotso.org/UTK
|
||||
*
|
||||
* EA classifies MT as MT10:1 (smaller frames) and MT5:1 (bigger frames), but both are the same
|
||||
* with different encoding parameters. Later revisions may have PCM blocks (rare). This codec was
|
||||
* also reused by Traveller Tales in CBX (same devs?) with minor modifications.
|
||||
*
|
||||
* TODO:
|
||||
* - lazy/avoid peeking/overreading when no bits left (OG code does it though, shouldn't matter)
|
||||
* - same with read_callback (doesn't affect anything but cleaner)
|
||||
*/
|
||||
|
||||
typedef enum {
|
||||
UTK_EA, // standard EA-MT (MT10 or MT5)
|
||||
UTK_EA_PCM, // EA-MT with PCM blocks
|
||||
UTK_CBX, // Traveller's Tales Chatterbox
|
||||
} utk_type_t;
|
||||
|
||||
/* opaque struct */
|
||||
typedef struct utk_context_t utk_context_t;
|
||||
|
||||
/* inits UTK (must be externally created + init here) */
|
||||
utk_context_t* utk_init(utk_type_t type);
|
||||
|
||||
void utk_free(utk_context_t*);
|
||||
|
||||
/* reset/flush */
|
||||
void utk_reset(utk_context_t* ctx);
|
||||
|
||||
/* loads current data (can also be used to reset buffered data if set to 0) */
|
||||
void utk_set_buffer(utk_context_t* ctx, const uint8_t* buf, size_t buf_size);
|
||||
|
||||
/* prepares for external streaming (buf is where reads store data, arg is any external params for the callback) */
|
||||
void utk_set_callback(utk_context_t* ctx, uint8_t* buf, size_t buf_size, void* arg, size_t (*read_callback)(void*, int , void*));
|
||||
|
||||
/* main decode; returns decoded samples on ok (always >0), < 0 on error */
|
||||
int utk_decode_frame(utk_context_t* ctx);
|
||||
|
||||
/* get sample buf (shouldn't change between calls); sample type is PCM float (+-32768 but not clamped) */
|
||||
float* utk_get_samples(utk_context_t* ctx);
|
||||
|
||||
#endif
|
@ -23,7 +23,6 @@ static const char* extension_list[] = {
|
||||
|
||||
"208",
|
||||
"2dx9",
|
||||
"2pfs",
|
||||
"3do",
|
||||
"3ds", //txth/reserved [F1 2011 (3DS)]
|
||||
"4", //for Game.com audio
|
||||
@ -133,6 +132,7 @@ static const char* extension_list[] = {
|
||||
"caf",
|
||||
"cat",
|
||||
"cbd2",
|
||||
"cbx",
|
||||
"cd",
|
||||
"cfn", //fake extension for CAF (renamed, to be removed?)
|
||||
"chd", //txth/reserved [Donkey Konga (GC), Star Fox Assault (GC)]
|
||||
@ -347,6 +347,7 @@ static const char* extension_list[] = {
|
||||
//"m4a", //common
|
||||
//"m4v", //common
|
||||
//"mov", //common
|
||||
"move",
|
||||
//"mp+", //common [Moonshine Runners (PC)]
|
||||
//"mp2", //common
|
||||
//"mp3", //common
|
||||
@ -428,7 +429,7 @@ static const char* extension_list[] = {
|
||||
"psb",
|
||||
"psf",
|
||||
"psh", //fake extension for .vsv (to be removed)
|
||||
"psnd",
|
||||
"psn",
|
||||
"pwb",
|
||||
|
||||
"r",
|
||||
@ -1072,7 +1073,7 @@ static const meta_info meta_info_list[] = {
|
||||
{meta_RSTM_ROCKSTAR, "Rockstar Games RSTM Header"},
|
||||
{meta_ACM, "InterPlay ACM Header"},
|
||||
{meta_MUS_ACM, "InterPlay MUS ACM header"},
|
||||
{meta_PS2_KCES, "Konami KCES Header"},
|
||||
{meta_VIG_KCES, "Konami .VIG Header"},
|
||||
{meta_HXD, "Tecmo HXD Header"},
|
||||
{meta_VSV, "Square Enix .vsv Header"},
|
||||
{meta_RIFF_WAVE_labl, "RIFF WAVE header (labl looping)"},
|
||||
@ -1130,8 +1131,8 @@ static const meta_info meta_info_list[] = {
|
||||
{meta_MUL, "Crystal Dynamics .MUL header"},
|
||||
{meta_THP, "Nintendo THP header"},
|
||||
{meta_STS, "Alfa System .STS header"},
|
||||
{meta_PS2_P2BT, "Pop'n'Music 7 Header"},
|
||||
{meta_PS2_GBTS, "Pop'n'Music 9 Header"},
|
||||
{meta_P2BT_MOVE_VISA, "Konami P2BT/MOVE/VISA header"},
|
||||
{meta_GBTS, "Konami GBTS header"},
|
||||
{meta_NGC_DSP_IADP, "IADP Header"},
|
||||
{meta_RIFF_WAVE_MWV, "RIFF WAVE header (ctrl looping)"},
|
||||
{meta_FFCC_STR, "Final Fantasy: Crystal Chronicles STR header"},
|
||||
@ -1219,7 +1220,7 @@ static const meta_info meta_info_list[] = {
|
||||
{meta_RAW_SNDS, "PC .snds raw header"},
|
||||
{meta_PS2_WMUS, "assumed The Warriors Sony ADPCM by .wmus extension"},
|
||||
{meta_HYPERSCAN_KVAG, "Mattel Hyperscan KVAG"},
|
||||
{meta_IOS_PSND, "PSND Header"},
|
||||
{meta_PSND, "Polarbit PSND header"},
|
||||
{meta_ADP_WILDFIRE, "Wildfire ADP! header"},
|
||||
{meta_QD_ADP, "Quantic Dream .ADP header"},
|
||||
{meta_EB_SFX, "Excitebots .sfx header"},
|
||||
@ -1231,7 +1232,7 @@ static const meta_info meta_info_list[] = {
|
||||
{meta_MSS, "Guerilla MCSS header"},
|
||||
{meta_PS2_HSF, "Lowrider 'HSF' header"},
|
||||
{meta_IVAG, "Namco IVAG header"},
|
||||
{meta_PS2_2PFS, "Konami 2PFS header"},
|
||||
{meta_2PFS, "Konami 2PFS header"},
|
||||
{meta_UBI_CKD, "Ubisoft CKD RIFF header"},
|
||||
{meta_PS2_VBK, "PS2 VBK Header"},
|
||||
{meta_OTM, "Otomedius OTM Header"},
|
||||
@ -1327,7 +1328,6 @@ static const meta_info meta_info_list[] = {
|
||||
{meta_MSV, "Sony MultiStream MSV header"},
|
||||
{meta_SDF, "Beyond Reality SDF header"},
|
||||
{meta_SVG, "High Voltage SVG header"},
|
||||
{meta_VIS, "Konami VIS header"},
|
||||
{meta_VAI, "Asobo Studio .VAI header"},
|
||||
{meta_AIF_ASOBO, "Asobo Studio .AIF header"},
|
||||
{meta_AO, "AlphaOgg .AO header"},
|
||||
@ -1428,6 +1428,7 @@ static const meta_info meta_info_list[] = {
|
||||
{meta_SNDS, "Sony SNDS header"},
|
||||
{meta_NXOF, "Nihon Falcom FDK header"},
|
||||
{meta_GWB_GWD, "Ubisoft GWB+GWD header"},
|
||||
{meta_CBX, "Traveller's Tales CBX header"},
|
||||
};
|
||||
|
||||
void get_vgmstream_coding_description(VGMSTREAM* vgmstream, char* out, size_t out_size) {
|
||||
|
@ -99,7 +99,6 @@
|
||||
<ClInclude Include="coding\coding.h" />
|
||||
<ClInclude Include="coding\coding_utils_samples.h" />
|
||||
<ClInclude Include="coding\compresswave_decoder_lib.h" />
|
||||
<ClInclude Include="coding\ea_mt_decoder_utk.h" />
|
||||
<ClInclude Include="coding\g7221_decoder_aes.h" />
|
||||
<ClInclude Include="coding\g7221_decoder_lib.h" />
|
||||
<ClInclude Include="coding\g7221_decoder_lib_data.h" />
|
||||
@ -115,6 +114,7 @@
|
||||
<ClInclude Include="coding\vorbis_custom_data_fsb.h" />
|
||||
<ClInclude Include="coding\vorbis_custom_data_wwise.h" />
|
||||
<ClInclude Include="coding\vorbis_custom_decoder.h" />
|
||||
<ClInclude Include="coding\libs\utkdec.h" />
|
||||
<ClInclude Include="layout\layout.h" />
|
||||
<ClInclude Include="meta\9tav_streamfile.h" />
|
||||
<ClInclude Include="meta\adx_keys.h" />
|
||||
@ -290,6 +290,7 @@
|
||||
<ClCompile Include="coding\xa_decoder.c" />
|
||||
<ClCompile Include="coding\xmd_decoder.c" />
|
||||
<ClCompile Include="coding\yamaha_decoder.c" />
|
||||
<ClCompile Include="coding\libs\utkdec.c" />
|
||||
<ClCompile Include="layout\blocked.c" />
|
||||
<ClCompile Include="layout\blocked_adm.c" />
|
||||
<ClCompile Include="layout\blocked_ast.c" />
|
||||
@ -335,6 +336,7 @@
|
||||
<ClCompile Include="layout\segmented.c" />
|
||||
<ClCompile Include="meta\208.c" />
|
||||
<ClCompile Include="meta\2dx9.c" />
|
||||
<ClCompile Include="meta\2pfs.c" />
|
||||
<ClCompile Include="meta\9tav.c" />
|
||||
<ClCompile Include="meta\a2m.c" />
|
||||
<ClCompile Include="meta\aac_triace.c" />
|
||||
@ -396,6 +398,7 @@
|
||||
<ClCompile Include="meta\bwav.c" />
|
||||
<ClCompile Include="meta\bw_mp3_riff.c" />
|
||||
<ClCompile Include="meta\caf.c" />
|
||||
<ClCompile Include="meta\cbx.c" />
|
||||
<ClCompile Include="meta\ck.c" />
|
||||
<ClCompile Include="meta\compresswave.c" />
|
||||
<ClCompile Include="meta\cpk.c" />
|
||||
@ -448,6 +451,7 @@
|
||||
<ClCompile Include="meta\fsb_encrypted.c" />
|
||||
<ClCompile Include="meta\fwse.c" />
|
||||
<ClCompile Include="meta\g1l.c" />
|
||||
<ClCompile Include="meta\gbts.c" />
|
||||
<ClCompile Include="meta\gca.c" />
|
||||
<ClCompile Include="meta\gcsw.c" />
|
||||
<ClCompile Include="meta\gcub.c" />
|
||||
@ -471,7 +475,6 @@
|
||||
<ClCompile Include="meta\ima.c" />
|
||||
<ClCompile Include="meta\imc.c" />
|
||||
<ClCompile Include="meta\imuse.c" />
|
||||
<ClCompile Include="meta\ios_psnd.c" />
|
||||
<ClCompile Include="meta\isb.c" />
|
||||
<ClCompile Include="meta\ish_isd.c" />
|
||||
<ClCompile Include="meta\ivag.c" />
|
||||
@ -556,6 +559,7 @@
|
||||
<ClCompile Include="meta\omu.c" />
|
||||
<ClCompile Include="meta\opus.c" />
|
||||
<ClCompile Include="meta\otm.c" />
|
||||
<ClCompile Include="meta\p2bt_move_visa.c" />
|
||||
<ClCompile Include="meta\p3d.c" />
|
||||
<ClCompile Include="meta\pasx.c" />
|
||||
<ClCompile Include="meta\pcm_sre.c" />
|
||||
@ -566,21 +570,17 @@
|
||||
<ClCompile Include="meta\pona.c" />
|
||||
<ClCompile Include="meta\pos.c" />
|
||||
<ClCompile Include="meta\ppst.c" />
|
||||
<ClCompile Include="meta\ps2_2pfs.c" />
|
||||
<ClCompile Include="meta\ps2_adm.c" />
|
||||
<ClCompile Include="meta\ps2_ass.c" />
|
||||
<ClCompile Include="meta\ps2_b1s.c" />
|
||||
<ClCompile Include="meta\ps2_bmdx.c" />
|
||||
<ClCompile Include="meta\ps2_gbts.c" />
|
||||
<ClCompile Include="meta\ps2_gcm.c" />
|
||||
<ClCompile Include="meta\ps2_hsf.c" />
|
||||
<ClCompile Include="meta\ps2_iab.c" />
|
||||
<ClCompile Include="meta\ps2_joe.c" />
|
||||
<ClCompile Include="meta\ps2_kces.c" />
|
||||
<ClCompile Include="meta\ps2_mcg.c" />
|
||||
<ClCompile Include="meta\ps2_mic.c" />
|
||||
<ClCompile Include="meta\ps2_mihb.c" />
|
||||
<ClCompile Include="meta\ps2_p2bt.c" />
|
||||
<ClCompile Include="meta\ps2_pcm.c" />
|
||||
<ClCompile Include="meta\ps2_rnd.c" />
|
||||
<ClCompile Include="meta\ps2_snd.c" />
|
||||
@ -596,6 +596,7 @@
|
||||
<ClCompile Include="meta\ps3_past.c" />
|
||||
<ClCompile Include="meta\psb.c" />
|
||||
<ClCompile Include="meta\psf.c" />
|
||||
<ClCompile Include="meta\psnd.c" />
|
||||
<ClCompile Include="meta\ps_headerless.c" />
|
||||
<ClCompile Include="meta\pwb.c" />
|
||||
<ClCompile Include="meta\rad.c" />
|
||||
@ -693,7 +694,7 @@
|
||||
<ClCompile Include="meta\vgs.c" />
|
||||
<ClCompile Include="meta\vgs_ps.c" />
|
||||
<ClCompile Include="meta\vid1.c" />
|
||||
<ClCompile Include="meta\vis.c" />
|
||||
<ClCompile Include="meta\vig_kces.c" />
|
||||
<ClCompile Include="meta\voi.c" />
|
||||
<ClCompile Include="meta\vpk.c" />
|
||||
<ClCompile Include="meta\vs.c" />
|
||||
|
@ -122,9 +122,6 @@
|
||||
<ClInclude Include="coding\compresswave_decoder_lib.h">
|
||||
<Filter>coding\Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="coding\ea_mt_decoder_utk.h">
|
||||
<Filter>coding\Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="coding\g7221_decoder_aes.h">
|
||||
<Filter>coding\Header Files</Filter>
|
||||
</ClInclude>
|
||||
@ -170,6 +167,9 @@
|
||||
<ClInclude Include="coding\vorbis_custom_decoder.h">
|
||||
<Filter>coding\Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="coding\libs\utkdec.h">
|
||||
<Filter>coding\libs\Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="layout\layout.h">
|
||||
<Filter>layout\Header Files</Filter>
|
||||
</ClInclude>
|
||||
@ -691,6 +691,9 @@
|
||||
<ClCompile Include="coding\yamaha_decoder.c">
|
||||
<Filter>coding\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="coding\libs\utkdec.c">
|
||||
<Filter>coding\libs\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="layout\blocked.c">
|
||||
<Filter>layout\Source Files</Filter>
|
||||
</ClCompile>
|
||||
@ -826,6 +829,9 @@
|
||||
<ClCompile Include="meta\2dx9.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\2pfs.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\9tav.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
@ -1009,6 +1015,9 @@
|
||||
<ClCompile Include="meta\caf.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\cbx.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\ck.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
@ -1165,6 +1174,9 @@
|
||||
<ClCompile Include="meta\g1l.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\gbts.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\gca.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
@ -1234,9 +1246,6 @@
|
||||
<ClCompile Include="meta\imuse.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\ios_psnd.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\isb.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
@ -1489,6 +1498,9 @@
|
||||
<ClCompile Include="meta\otm.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\p2bt_move_visa.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\p3d.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
@ -1519,9 +1531,6 @@
|
||||
<ClCompile Include="meta\ppst.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\ps2_2pfs.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\ps2_adm.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
@ -1534,9 +1543,6 @@
|
||||
<ClCompile Include="meta\ps2_bmdx.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\ps2_gbts.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\ps2_gcm.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
@ -1549,9 +1555,6 @@
|
||||
<ClCompile Include="meta\ps2_joe.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\ps2_kces.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\ps2_mcg.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
@ -1561,9 +1564,6 @@
|
||||
<ClCompile Include="meta\ps2_mihb.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\ps2_p2bt.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\ps2_pcm.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
@ -1609,6 +1609,9 @@
|
||||
<ClCompile Include="meta\psf.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\psnd.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\ps_headerless.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
@ -1900,7 +1903,7 @@
|
||||
<ClCompile Include="meta\vid1.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\vis.c">
|
||||
<ClCompile Include="meta\vig_kces.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\voi.c">
|
||||
|
@ -3,22 +3,20 @@
|
||||
|
||||
|
||||
/* 2PFS - from Konami Games [Mahoromatic: Moetto-KiraKira Maid-San (PS2), GANTZ The Game (PS2)] */
|
||||
VGMSTREAM* init_vgmstream_ps2_2pfs(STREAMFILE *sf) {
|
||||
VGMSTREAM* init_vgmstream_2pfs(STREAMFILE* sf) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
off_t start_offset;
|
||||
int loop_flag, channels, version, interleave;
|
||||
int loop_start_block, loop_end_block; /* block number */
|
||||
int loop_start_block, loop_end_block;
|
||||
int loop_start_adjust, loop_end_adjust; /* loops start/end a few samples into the start/end block */
|
||||
|
||||
|
||||
/* checks */
|
||||
/* .sap: standard
|
||||
* .2pfs: header id? (Mahoromatic) */
|
||||
if (!check_extensions(sf, "sap,2pfs"))
|
||||
goto fail;
|
||||
|
||||
if (read_u32be(0x00,sf) != 0x32504653) /* "2PFS" */
|
||||
goto fail;
|
||||
if (!is_id32be(0x00,sf, "2PFS"))
|
||||
return NULL;
|
||||
/* .sap: standard */
|
||||
if (!check_extensions(sf, "sap"))
|
||||
return NULL;
|
||||
|
||||
version = read_u16le(0x04,sf);
|
||||
if (version != 0x01 && version != 0x02) /* v1: Mahoromatic, v2: Gantz */
|
||||
@ -44,7 +42,7 @@ VGMSTREAM* init_vgmstream_ps2_2pfs(STREAMFILE *sf) {
|
||||
vgmstream = allocate_vgmstream(channels, loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->meta_type = meta_PS2_2PFS;
|
||||
vgmstream->meta_type = meta_2PFS;
|
||||
vgmstream->num_samples = read_u32le(0x34,sf) * 28 / 16 / channels;
|
||||
vgmstream->coding_type = coding_PSX;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
@ -77,7 +75,6 @@ VGMSTREAM* init_vgmstream_ps2_2pfs(STREAMFILE *sf) {
|
||||
if (!vgmstream_open_stream(vgmstream, sf, start_offset))
|
||||
goto fail;
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
46
src/meta/cbx.c
Normal file
46
src/meta/cbx.c
Normal file
@ -0,0 +1,46 @@
|
||||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
|
||||
|
||||
/* !B0X - Traveller's Tales speech files [Lego Batman 2 (PC), Lego Dimensions (PS3)] */
|
||||
VGMSTREAM* init_vgmstream_cbx(STREAMFILE* sf) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
uint32_t start_offset, pcm_size;
|
||||
int loop_flag, channels, sample_rate;
|
||||
|
||||
|
||||
/* checks */
|
||||
if (!is_id32be(0x00,sf, "!B0X"))
|
||||
return NULL;
|
||||
if (!check_extensions(sf, "cbx"))
|
||||
return NULL;
|
||||
|
||||
/* debug strings identify this as "Chatterbox"/"CBOX"/"CBX", while sound lib seems called "NuSound"
|
||||
* (probably based on .utk) */
|
||||
|
||||
pcm_size = read_u32le(0x04, sf);
|
||||
sample_rate = read_s32le(0x08, sf);
|
||||
start_offset = 0x0c;
|
||||
channels = 1;
|
||||
loop_flag = 0;
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channels, loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->meta_type = meta_CBX;
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
vgmstream->num_samples = pcm_size / 2;
|
||||
vgmstream->coding_type = coding_EA_MT;
|
||||
vgmstream->layout_type = layout_none;
|
||||
vgmstream->codec_data = init_ea_mt_cbx(vgmstream->channels);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream, sf, start_offset))
|
||||
goto fail;
|
||||
return vgmstream;
|
||||
fail:
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
57
src/meta/gbts.c
Normal file
57
src/meta/gbts.c
Normal file
@ -0,0 +1,57 @@
|
||||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
|
||||
|
||||
/* GbTs - from Konami/KCE Studio games [Pop'n Music 9/10 (PS2)] */
|
||||
VGMSTREAM* init_vgmstream_gbts(STREAMFILE* sf) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
uint32_t data_offset, data_size;
|
||||
int loop_flag, channels, sample_rate;
|
||||
uint32_t loop_start, loop_end;
|
||||
|
||||
|
||||
/* checks */
|
||||
if (!is_id32be(0x00,sf, "GbTs"))
|
||||
return NULL;
|
||||
/* .gbts: header id (no apparent exts) */
|
||||
if (!check_extensions(sf, "gbts"))
|
||||
return NULL;
|
||||
|
||||
|
||||
/* 04: always 0x24 */
|
||||
data_offset = read_u32le(0x08,sf);
|
||||
data_size = read_u32le(0x0C,sf); /* without padding */
|
||||
loop_start = read_u32le(0x10,sf); /* (0x20 = start frame if not set) */
|
||||
loop_end = read_u32le(0x14,sf); /* (0x00 if not set) */
|
||||
sample_rate = read_s32le(0x18,sf);
|
||||
channels = read_s32le(0x1C,sf);
|
||||
/* 20: 1? */
|
||||
/* 24: block size (interleave * channels) */
|
||||
/* 30+: empty */
|
||||
|
||||
loop_flag = (loop_end > 0);
|
||||
loop_end += loop_start; /* loop region matches PS-ADPCM flags */
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channels, loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
vgmstream->num_samples = ps_bytes_to_samples(data_size, channels);
|
||||
vgmstream->loop_start_sample = ps_bytes_to_samples(loop_start, channels);
|
||||
vgmstream->loop_end_sample = ps_bytes_to_samples(loop_end, channels);
|
||||
|
||||
vgmstream->coding_type = coding_PSX;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = 0x10;
|
||||
|
||||
vgmstream->meta_type = meta_GBTS;
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream, sf, data_offset))
|
||||
goto fail;
|
||||
return vgmstream;
|
||||
fail:
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
@ -1,76 +0,0 @@
|
||||
#include "meta.h"
|
||||
#include "../util.h"
|
||||
|
||||
/* PSND (from Crash Bandicoot Nitro Kart 2 (iOS) */
|
||||
VGMSTREAM * init_vgmstream_ios_psnd(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
char filename[PATH_LIMIT];
|
||||
off_t start_offset;
|
||||
|
||||
int loop_flag;
|
||||
int channel_count;
|
||||
|
||||
/* check extension, case insensitive */
|
||||
streamFile->get_name(streamFile,filename,sizeof(filename));
|
||||
if (strcasecmp("psnd",filename_extension(filename))) goto fail;
|
||||
|
||||
/* check header */
|
||||
if (read_32bitBE(0x00,streamFile) != 0x50534E44) /* "PSND" */
|
||||
goto fail;
|
||||
|
||||
if (read_16bitBE(0xC,streamFile)==0x2256){
|
||||
loop_flag = 1;
|
||||
}
|
||||
else {
|
||||
loop_flag = 0;
|
||||
}
|
||||
channel_count = read_8bit(0xE,streamFile);
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channel_count,loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
/* fill in the vital statistics */
|
||||
start_offset = 0x10;
|
||||
vgmstream->channels = channel_count;
|
||||
|
||||
if (read_16bitBE(0xC,streamFile)==0x44AC){
|
||||
vgmstream->sample_rate = 44100;
|
||||
}
|
||||
else {
|
||||
vgmstream->sample_rate = read_16bitLE(0xC,streamFile);
|
||||
}
|
||||
|
||||
vgmstream->coding_type = coding_PCM16LE;
|
||||
vgmstream->num_samples = (read_32bitLE(0x4,streamFile)-8)/4;
|
||||
if (loop_flag) {
|
||||
vgmstream->loop_start_sample = 0;
|
||||
vgmstream->loop_end_sample = vgmstream->num_samples;
|
||||
}
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = 2;
|
||||
vgmstream->meta_type = meta_IOS_PSND;
|
||||
|
||||
/* open the file for reading */
|
||||
{
|
||||
int i;
|
||||
STREAMFILE * file;
|
||||
file = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE);
|
||||
if (!file) goto fail;
|
||||
for (i=0;i<channel_count;i++) {
|
||||
vgmstream->ch[i].streamfile = file;
|
||||
|
||||
vgmstream->ch[i].channel_start_offset=
|
||||
vgmstream->ch[i].offset=start_offset+
|
||||
vgmstream->interleave_block_size*i;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return vgmstream;
|
||||
|
||||
/* clean up anything we may have opened */
|
||||
fail:
|
||||
if (vgmstream) close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
@ -26,8 +26,8 @@ typedef struct {
|
||||
//off_t name_offset;
|
||||
} kwb_header;
|
||||
|
||||
static int parse_kwb(kwb_header* kwb, STREAMFILE* sf_h, STREAMFILE* sf_b);
|
||||
static int parse_xws(kwb_header* kwb, STREAMFILE* sf);
|
||||
static bool parse_kwb(kwb_header* kwb, STREAMFILE* sf_h, STREAMFILE* sf_b);
|
||||
static bool parse_xws(kwb_header* kwb, STREAMFILE* sf);
|
||||
static VGMSTREAM* init_vgmstream_koei_wavebank(kwb_header* kwb, STREAMFILE* sf_h, STREAMFILE* sf_b);
|
||||
|
||||
|
||||
@ -43,14 +43,14 @@ VGMSTREAM* init_vgmstream_kwb(STREAMFILE* sf) {
|
||||
if (!is_id32be(0x00, sf, "WBD_") &&
|
||||
!is_id32le(0x00, sf, "WBD_") &&
|
||||
!is_id32be(0x00, sf, "WHD1"))
|
||||
goto fail;
|
||||
return NULL;
|
||||
|
||||
/* .wbd+wbh: common [Bladestorm Nightmare (PC)]
|
||||
* .wbd+whd: uncommon [Nights of Azure 2 (PS4)]
|
||||
* .wb2+wh2: newer [Nights of Azure 2 (PC)]
|
||||
* .sed: mixed header+data [Dissidia NT (PC)] */
|
||||
if (!check_extensions(sf, "wbd,wb2,sed"))
|
||||
goto fail;
|
||||
return NULL;
|
||||
|
||||
|
||||
/* open companion header */
|
||||
@ -102,13 +102,13 @@ VGMSTREAM* init_vgmstream_xws(STREAMFILE* sf) {
|
||||
|
||||
/* checks */
|
||||
if (!check_extensions(sf, "xws"))
|
||||
goto fail;
|
||||
return NULL;
|
||||
|
||||
if (target_subsong == 0) target_subsong = 1;
|
||||
kwb.target_subsong = target_subsong;
|
||||
|
||||
if (!parse_xws(&kwb, sf))
|
||||
goto fail;
|
||||
return NULL;
|
||||
|
||||
vgmstream = init_vgmstream_koei_wavebank(&kwb, sf, sf);
|
||||
if (!vgmstream) goto fail;
|
||||
@ -119,6 +119,34 @@ fail:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#if 0
|
||||
/* SND - Sound? from Koei games [Ninja Gaiden Sigma -Master Collection- (PC)] */
|
||||
VGMSTREAM* init_vgmstream_snd_koei(STREAMFILE* sf) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
kwb_header kwb = {0};
|
||||
int target_subsong = sf->stream_index;
|
||||
|
||||
|
||||
/* checks */
|
||||
/* .snd: header id/assumed by extractors? (doatool) */
|
||||
if (!check_extensions(sf, "snd"))
|
||||
return NULL;
|
||||
|
||||
if (target_subsong == 0) target_subsong = 1;
|
||||
kwb.target_subsong = target_subsong;
|
||||
|
||||
if (!parse_xws(&kwb, sf))
|
||||
return NULL;
|
||||
|
||||
vgmstream = init_vgmstream_koei_wavebank(&kwb, sf, sf);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
return vgmstream;
|
||||
fail:
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
static VGMSTREAM* init_vgmstream_koei_wavebank(kwb_header* kwb, STREAMFILE* sf_h, STREAMFILE* sf_b) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
@ -563,7 +591,7 @@ fail:
|
||||
}
|
||||
|
||||
|
||||
static int parse_kwb(kwb_header* kwb, STREAMFILE* sf_h, STREAMFILE* sf_b) {
|
||||
static bool parse_kwb(kwb_header* kwb, STREAMFILE* sf_h, STREAMFILE* sf_b) {
|
||||
off_t head_offset, body_offset, start;
|
||||
uint32_t type;
|
||||
uint32_t (*read_u32)(off_t,STREAMFILE*) = NULL;
|
||||
@ -644,12 +672,12 @@ static int parse_kwb(kwb_header* kwb, STREAMFILE* sf_h, STREAMFILE* sf_b) {
|
||||
if (!kwb->found)
|
||||
goto fail;
|
||||
|
||||
return 1;
|
||||
return true;
|
||||
fail:
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
static int parse_type_msfbank(kwb_header* kwb, off_t offset, STREAMFILE* sf) {
|
||||
static bool parse_type_msfbank(kwb_header* kwb, off_t offset, STREAMFILE* sf) {
|
||||
/* this is just like XWSF, abridged: */
|
||||
int entries, current_subsongs, relative_subsong;
|
||||
off_t header_offset;
|
||||
@ -671,9 +699,9 @@ static int parse_type_msfbank(kwb_header* kwb, off_t offset, STREAMFILE* sf) {
|
||||
|
||||
kwb->stream_offset += offset;
|
||||
|
||||
return 1;
|
||||
return true;
|
||||
//fail:
|
||||
// return 0;
|
||||
// return false;
|
||||
}
|
||||
|
||||
static int parse_type_xwsfile(kwb_header* kwb, uint32_t offset, STREAMFILE* sf) {
|
||||
@ -681,22 +709,22 @@ static int parse_type_xwsfile(kwb_header* kwb, uint32_t offset, STREAMFILE* sf)
|
||||
int i, chunks, chunks2;
|
||||
uint32_t (*read_u32)(off_t,STREAMFILE*) = NULL;
|
||||
|
||||
|
||||
if (!(is_id64be(offset + 0x00, sf, "XWSFILE\0")) &&
|
||||
!(is_id64be(offset + 0x00, sf, "tdpack\0\0")))
|
||||
if (!( is_id64be(offset + 0x00, sf, "XWSFILE\0") ||
|
||||
is_id64be(offset + 0x00, sf, "tdpack\0\0")))
|
||||
//is_id64be(offset + 0x00, sf, "SND\0\0\0\0\0")
|
||||
goto fail;
|
||||
|
||||
kwb->big_endian = read_u8(offset + 0x08, sf) == 0xFF;
|
||||
/* 0x0a: version? (0100: NG2/NG3 PS3, 0101: DoA LR PC, NG2/3 PC) */
|
||||
/* 0x0a: version? (0100: NG2/NG3 PS3, NGm PC, 0101: DoA LR PC, NG2/3 PC) */
|
||||
|
||||
read_u32 = kwb->big_endian ? read_u32be : read_u32le;
|
||||
|
||||
/* 0x0c: tables start */
|
||||
/* 0x10: file size */
|
||||
chunks = read_u32(offset + 0x14, sf);
|
||||
chunks2 = read_u32(offset + 0x18, sf);
|
||||
chunks2 = read_u32(offset + 0x14, sf);
|
||||
chunks = read_u32(offset + 0x18, sf);
|
||||
/* 0x1c: null */
|
||||
if (chunks != chunks2)
|
||||
if (chunks != chunks2) //seen in NGm PC
|
||||
goto fail;
|
||||
|
||||
table1_offset = read_u32(offset + 0x20, sf); /* offsets */
|
||||
@ -737,6 +765,13 @@ static int parse_type_xwsfile(kwb_header* kwb, uint32_t offset, STREAMFILE* sf)
|
||||
if (!parse_type_xwsfile(kwb, head_offset, sf))
|
||||
goto fail;
|
||||
}
|
||||
#if 0
|
||||
else if (entry_type == get_id32be("tdpa")) { /* + "ck\0\0" */
|
||||
i += 1;
|
||||
if (!parse_type_xwsfile(kwb, head_offset, sf))
|
||||
goto fail;
|
||||
}
|
||||
#endif
|
||||
else if (entry_type == get_id32be("CUEB") || entry_type < 0x100) {
|
||||
i += 1;
|
||||
/* CUE-like info (may start with 0 or a low number instead) */
|
||||
@ -767,7 +802,7 @@ fail:
|
||||
}
|
||||
|
||||
|
||||
static int parse_xws(kwb_header* kwb, STREAMFILE* sf) {
|
||||
static bool parse_xws(kwb_header* kwb, STREAMFILE* sf) {
|
||||
|
||||
/* Format is similar to WHD1 with some annoyances of its own. Variations:
|
||||
* - XWSFILE w/ N chunks: CUE offsets + 1 MSFBANK offset
|
||||
@ -776,16 +811,16 @@ static int parse_xws(kwb_header* kwb, STREAMFILE* sf) {
|
||||
* [Dead or Alive 5 Last Round (PC)]
|
||||
* - tdpack: same but points to N XWSFILE
|
||||
* [Ninja Gaiden 3 Razor's Edge (PS3)]
|
||||
* - SND: similar to XWSFILE w/ 2*N chunks, points to tdpack (which point to _HBW0000+KWB2)
|
||||
* [Ninja Gaiden Sigma -Master Collection- (PC)]
|
||||
*
|
||||
* Needs to call sub-parts multiple times to fill total subsongs when parsing xwsfile.
|
||||
*/
|
||||
if (!parse_type_xwsfile(kwb, 0x00, sf))
|
||||
goto fail;
|
||||
return false;
|
||||
|
||||
if (!kwb->found)
|
||||
goto fail;
|
||||
return false;
|
||||
|
||||
return 1;
|
||||
fail:
|
||||
return 0;
|
||||
return true;
|
||||
}
|
||||
|
@ -234,7 +234,7 @@ VGMSTREAM* init_vgmstream_rstm_rockstar(STREAMFILE* sf);
|
||||
|
||||
VGMSTREAM * init_vgmstream_acm(STREAMFILE * streamFile);
|
||||
|
||||
VGMSTREAM * init_vgmstream_ps2_kces(STREAMFILE * streamFile);
|
||||
VGMSTREAM* init_vgmstream_vig_kces(STREAMFILE* sf);
|
||||
|
||||
VGMSTREAM* init_vgmstream_hxd(STREAMFILE* sf);
|
||||
|
||||
@ -344,9 +344,9 @@ VGMSTREAM * init_vgmstream_thp(STREAMFILE *streamFile);
|
||||
|
||||
VGMSTREAM* init_vgmstream_sts(STREAMFILE* sf);
|
||||
|
||||
VGMSTREAM * init_vgmstream_ps2_p2bt(STREAMFILE *streamFile);
|
||||
VGMSTREAM* init_vgmstream_p2bt_move_visa(STREAMFILE* sf);
|
||||
|
||||
VGMSTREAM * init_vgmstream_ps2_gbts(STREAMFILE *streamFile);
|
||||
VGMSTREAM* init_vgmstream_gbts(STREAMFILE* sf);
|
||||
|
||||
VGMSTREAM * init_vgmstream_wii_sng(STREAMFILE *streamFile);
|
||||
|
||||
@ -500,7 +500,7 @@ VGMSTREAM * init_vgmstream_ps2_wmus(STREAMFILE* streamFile);
|
||||
|
||||
VGMSTREAM * init_vgmstream_hyperscan_kvag(STREAMFILE* streamFile);
|
||||
|
||||
VGMSTREAM * init_vgmstream_ios_psnd(STREAMFILE* streamFile);
|
||||
VGMSTREAM* init_vgmstream_psnd(STREAMFILE* sf);
|
||||
|
||||
VGMSTREAM* init_vgmstream_adp_wildfire(STREAMFILE* sf);
|
||||
|
||||
@ -523,7 +523,7 @@ VGMSTREAM * init_vgmstream_ps2_hsf(STREAMFILE* streamFile);
|
||||
|
||||
VGMSTREAM * init_vgmstream_ivag(STREAMFILE* streamFile);
|
||||
|
||||
VGMSTREAM * init_vgmstream_ps2_2pfs(STREAMFILE* streamFile);
|
||||
VGMSTREAM* init_vgmstream_2pfs(STREAMFILE* sf);
|
||||
|
||||
VGMSTREAM * init_vgmstream_ubi_ckd(STREAMFILE* streamFile);
|
||||
|
||||
@ -752,8 +752,6 @@ VGMSTREAM * init_vgmstream_sdf(STREAMFILE *streamFile);
|
||||
|
||||
VGMSTREAM * init_vgmstream_svg(STREAMFILE *streamFile);
|
||||
|
||||
VGMSTREAM * init_vgmstream_vis(STREAMFILE *streamFile);
|
||||
|
||||
VGMSTREAM * init_vgmstream_vai(STREAMFILE *streamFile);
|
||||
|
||||
VGMSTREAM * init_vgmstream_aif_asobo(STREAMFILE *streamFile);
|
||||
@ -885,7 +883,6 @@ VGMSTREAM * init_vgmstream_tgc(STREAMFILE *streamFile);
|
||||
|
||||
VGMSTREAM* init_vgmstream_kwb(STREAMFILE* sf);
|
||||
VGMSTREAM* init_vgmstream_xws(STREAMFILE* sf);
|
||||
VGMSTREAM* init_vgmstream_snd_koei(STREAMFILE* sf);
|
||||
|
||||
VGMSTREAM * init_vgmstream_lrmd(STREAMFILE* sf);
|
||||
|
||||
@ -1003,4 +1000,6 @@ VGMSTREAM* init_vgmstream_nxof(STREAMFILE* sf);
|
||||
|
||||
VGMSTREAM* init_vgmstream_gwb_gwd(STREAMFILE* sf);
|
||||
|
||||
VGMSTREAM* init_vgmstream_cbx(STREAMFILE* sf);
|
||||
|
||||
#endif /*_META_H*/
|
||||
|
@ -29,12 +29,12 @@ struct dsp_header {
|
||||
};
|
||||
|
||||
/* read and do basic validations to the above struct */
|
||||
static int read_dsp_header_endian(struct dsp_header *header, off_t offset, STREAMFILE* sf, int big_endian) {
|
||||
static bool read_dsp_header_endian(struct dsp_header *header, off_t offset, STREAMFILE* sf, int big_endian) {
|
||||
uint32_t (*get_u32)(const uint8_t*) = big_endian ? get_u32be : get_u32le;
|
||||
uint16_t (*get_u16)(const uint8_t*) = big_endian ? get_u16be : get_u16le;
|
||||
int16_t (*get_s16)(const uint8_t*) = big_endian ? get_s16be : get_s16le;
|
||||
int i;
|
||||
uint8_t buf[0x60];
|
||||
int zero_coefs;
|
||||
|
||||
if (offset > get_streamfile_size(sf))
|
||||
goto fail;
|
||||
@ -46,13 +46,13 @@ static int read_dsp_header_endian(struct dsp_header *header, off_t offset, STREA
|
||||
|
||||
/* base */
|
||||
header->sample_count = get_u32(buf+0x00);
|
||||
if (header->sample_count > 0x10000000)
|
||||
if (header->sample_count > 0x10000000 || header->sample_count == 0)
|
||||
goto fail; /* unlikely to be that big, should catch fourccs */
|
||||
|
||||
/* usually nibbles = size*2 in mono, but interleaved stereo or L+R may use nibbles =~ size (or not), so can't
|
||||
* easily reject files with more nibbles than data (nibbles may be part of the -R file) without redoing L+R handling */
|
||||
header->nibble_count = get_u32(buf+0x04);
|
||||
if (header->nibble_count > 0x10000000)
|
||||
if (header->nibble_count > 0x20000000 || header->nibble_count == 0)
|
||||
goto fail;
|
||||
|
||||
header->sample_rate = get_u32(buf+0x08);
|
||||
@ -73,14 +73,21 @@ static int read_dsp_header_endian(struct dsp_header *header, off_t offset, STREA
|
||||
if (header->initial_offset != 2 && header->initial_offset != 0)
|
||||
goto fail; /* Dr. Muto uses 0 */
|
||||
|
||||
for (i = 0; i < 16; i++)
|
||||
zero_coefs = 0;
|
||||
for (int i = 0; i < 16; i++) {
|
||||
header->coef[i] = get_s16(buf+0x1c + i*0x02);
|
||||
if (header->coef[i] == 0)
|
||||
zero_coefs++;
|
||||
}
|
||||
/* some 0s are ok, more than 8 is probably wrong */
|
||||
if (zero_coefs == 16)
|
||||
goto fail;
|
||||
|
||||
header->gain = get_u16(buf+0x3c);
|
||||
if (header->gain != 0)
|
||||
goto fail;
|
||||
|
||||
/* decoder state */
|
||||
/* decoder state (could check that ps <= 0xNN but not that useful) */
|
||||
header->initial_ps = get_u16(buf+0x3e);
|
||||
header->initial_hist1 = get_s16(buf+0x40);
|
||||
header->initial_hist2 = get_s16(buf+0x42);
|
||||
@ -97,9 +104,9 @@ static int read_dsp_header_endian(struct dsp_header *header, off_t offset, STREA
|
||||
if (header->block_size >= 0xF000) /* same, 16b (usually 0) */
|
||||
header->block_size = 0;
|
||||
|
||||
return 1;
|
||||
return true;
|
||||
fail:
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
static int read_dsp_header_be(struct dsp_header *header, off_t offset, STREAMFILE* file) {
|
||||
return read_dsp_header_endian(header, offset, file, 1);
|
||||
@ -149,16 +156,16 @@ static VGMSTREAM* init_vgmstream_dsp_common(STREAMFILE* sf, dsp_meta* dspm) {
|
||||
|
||||
|
||||
if (dspm->channels > dspm->max_channels)
|
||||
goto fail;
|
||||
if (dspm->channels > COMMON_DSP_MAX_CHANNELS)
|
||||
goto fail;
|
||||
return NULL;
|
||||
if (dspm->channels > COMMON_DSP_MAX_CHANNELS || dspm->channels < 0)
|
||||
return NULL;
|
||||
|
||||
/* load standard DSP header per channel */
|
||||
{
|
||||
for (i = 0; i < dspm->channels; i++) {
|
||||
if (!read_dsp_header_endian(&ch_header[i], dspm->header_offset + i*dspm->header_spacing, sf, !dspm->little_endian)) {
|
||||
//;VGM_LOG("DSP: bad header\n");
|
||||
goto fail;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
65
src/meta/p2bt_move_visa.c
Normal file
65
src/meta/p2bt_move_visa.c
Normal file
@ -0,0 +1,65 @@
|
||||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
|
||||
|
||||
/* P2BT/MOVE/VISA - from Konami/KCE Studio games [Pop'n Music 7/8/Best (PS2), AirForce Delta Strike (PS2)] */
|
||||
VGMSTREAM* init_vgmstream_p2bt_move_visa(STREAMFILE* sf) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
uint32_t data_offset;
|
||||
int loop_flag, channels, sample_rate, interleave;
|
||||
uint32_t loop_start, data_size;
|
||||
|
||||
|
||||
/* checks */
|
||||
if (!is_id32be(0x00,sf, "P2BT") &&
|
||||
!is_id32be(0x00,sf, "MOVE") &&
|
||||
!is_id32be(0x00,sf, "VISA"))
|
||||
return NULL;
|
||||
/* .psbt/move: header id (no apparent exts)
|
||||
* .vis: actual extension found in AFDS and other KCES games */
|
||||
if (!check_extensions(sf, "p2bt,move,vis"))
|
||||
return NULL;
|
||||
|
||||
/* (header is the same with different IDs, all may be used within a single same game) */
|
||||
/* 04: 07FC? */
|
||||
sample_rate = read_s32le(0x08,sf);
|
||||
loop_start = read_s32le(0x0c,sf);
|
||||
data_size = read_u32le(0x10,sf); /* without padding */
|
||||
interleave = read_u32le(0x14,sf); /* usually 0x10, sometimes 0x400 */
|
||||
/* 18: 1? */
|
||||
/* 1c: 0x10? */
|
||||
channels = read_s32le(0x20,sf);
|
||||
/* 24: 1? */
|
||||
/* 28: stream name (AFDS), same as basename + original ext */
|
||||
|
||||
loop_flag = (loop_start != 0);
|
||||
data_offset = 0x800;
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channels, loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
|
||||
vgmstream->num_samples = ps_bytes_to_samples(data_size, channels);
|
||||
vgmstream->loop_start_sample = ps_bytes_to_samples(loop_start, channels);
|
||||
vgmstream->loop_end_sample = vgmstream->num_samples;
|
||||
|
||||
vgmstream->coding_type = coding_PSX;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = interleave;
|
||||
|
||||
if (vgmstream->interleave_block_size)
|
||||
vgmstream->interleave_last_block_size = (data_size % (vgmstream->interleave_block_size * channels)) / channels;
|
||||
read_string(vgmstream->stream_name,0x10+1, 0x28, sf);
|
||||
|
||||
vgmstream->meta_type = meta_P2BT_MOVE_VISA;
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream, sf, data_offset))
|
||||
goto fail;
|
||||
return vgmstream;
|
||||
fail:
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
@ -1,92 +0,0 @@
|
||||
#include "meta.h"
|
||||
#include "../util.h"
|
||||
|
||||
/* GBTS : Pop'n'Music 9 Bgm File */
|
||||
|
||||
VGMSTREAM * init_vgmstream_ps2_gbts(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
char filename[PATH_LIMIT];
|
||||
|
||||
int loop_flag=0;
|
||||
int channel_count;
|
||||
off_t start_offset;
|
||||
off_t loopStart = 0;
|
||||
off_t loopEnd = 0;
|
||||
size_t filelength;
|
||||
|
||||
int i;
|
||||
|
||||
/* check extension, case insensitive */
|
||||
streamFile->get_name(streamFile,filename,sizeof(filename));
|
||||
if (strcasecmp("gbts",filename_extension(filename))) goto fail;
|
||||
|
||||
/* check loop */
|
||||
start_offset=0x801;
|
||||
|
||||
filelength = get_streamfile_size(streamFile);
|
||||
do {
|
||||
// Loop Start ...
|
||||
if(read_8bit(start_offset,streamFile)==0x06) {
|
||||
if(loopStart==0) loopStart = start_offset-0x801;
|
||||
}
|
||||
|
||||
// Loop End ...
|
||||
if(read_8bit(start_offset,streamFile)==0x03) {
|
||||
if(loopEnd==0) loopEnd = start_offset-0x801-0x10;
|
||||
}
|
||||
|
||||
start_offset+=0x10;
|
||||
|
||||
} while (start_offset<(int32_t)filelength);
|
||||
|
||||
loop_flag = (loopEnd!=0);
|
||||
channel_count=read_32bitLE(0x1C,streamFile);
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channel_count,loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
/* fill in the vital statistics */
|
||||
vgmstream->channels = channel_count;
|
||||
vgmstream->sample_rate = read_32bitLE(0x18,streamFile);;
|
||||
|
||||
/* Check for Compression Scheme */
|
||||
vgmstream->coding_type = coding_PSX;
|
||||
vgmstream->num_samples = read_32bitLE(0x0C,streamFile)/16*28/vgmstream->channels;
|
||||
vgmstream->interleave_block_size = 0x10;
|
||||
|
||||
/* Get loop point values */
|
||||
if(vgmstream->loop_flag) {
|
||||
vgmstream->loop_start_sample = (loopStart/(vgmstream->interleave_block_size)*vgmstream->interleave_block_size)/16*28;
|
||||
vgmstream->loop_start_sample += (loopStart%vgmstream->interleave_block_size)/16*28;
|
||||
vgmstream->loop_start_sample /=vgmstream->channels;
|
||||
vgmstream->loop_end_sample = (loopEnd/(vgmstream->interleave_block_size)*vgmstream->interleave_block_size)/16*28;
|
||||
vgmstream->loop_end_sample += (loopEnd%vgmstream->interleave_block_size)/16*28;
|
||||
vgmstream->loop_end_sample /=vgmstream->channels;
|
||||
}
|
||||
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->meta_type = meta_PS2_GBTS;
|
||||
|
||||
start_offset = (off_t)0x800;
|
||||
|
||||
/* open the file for reading by each channel */
|
||||
{
|
||||
for (i=0;i<channel_count;i++) {
|
||||
vgmstream->ch[i].streamfile = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE);
|
||||
|
||||
if (!vgmstream->ch[i].streamfile) goto fail;
|
||||
|
||||
vgmstream->ch[i].channel_start_offset=
|
||||
vgmstream->ch[i].offset=
|
||||
(off_t)(start_offset+vgmstream->interleave_block_size*i);
|
||||
}
|
||||
}
|
||||
|
||||
return vgmstream;
|
||||
|
||||
/* clean up anything we may have opened */
|
||||
fail:
|
||||
if (vgmstream) close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
@ -1,71 +0,0 @@
|
||||
#include "meta.h"
|
||||
#include "../util.h"
|
||||
|
||||
/* KCES (from Dance Dance Revolution) */
|
||||
VGMSTREAM * init_vgmstream_ps2_kces(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
char filename[PATH_LIMIT];
|
||||
off_t start_offset;
|
||||
|
||||
int loop_flag = 0;
|
||||
int channel_count;
|
||||
|
||||
/* check extension, case insensitive */
|
||||
streamFile->get_name(streamFile,filename,sizeof(filename));
|
||||
if (strcasecmp("kces",filename_extension(filename)) &&
|
||||
strcasecmp("vig",filename_extension(filename))) goto fail;
|
||||
|
||||
/* check header */
|
||||
if (read_32bitBE(0x00,streamFile) != 0x01006408)
|
||||
goto fail;
|
||||
|
||||
loop_flag = (read_32bitLE(0x14,streamFile)!=0);
|
||||
channel_count = read_32bitLE(0x1C,streamFile);
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channel_count,loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
/* fill in the vital statistics */
|
||||
start_offset = read_32bitLE(0x08,streamFile);
|
||||
vgmstream->channels = channel_count;
|
||||
vgmstream->sample_rate = read_32bitLE(0x18,streamFile);
|
||||
vgmstream->coding_type = coding_PSX;
|
||||
vgmstream->num_samples = read_32bitLE(0x0C,streamFile)*28/16/channel_count;
|
||||
if (loop_flag) {
|
||||
vgmstream->loop_start_sample = (read_32bitLE(0x0C,streamFile)-read_32bitLE(0x14,streamFile))*28/16/channel_count;
|
||||
vgmstream->loop_end_sample = read_32bitLE(0x0C,streamFile)*28/16/channel_count;
|
||||
}
|
||||
|
||||
|
||||
if(vgmstream->channels==1) {
|
||||
vgmstream->layout_type=layout_none;
|
||||
} else {
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = read_32bitLE(0x24,streamFile);
|
||||
}
|
||||
vgmstream->meta_type = meta_PS2_KCES;
|
||||
|
||||
/* open the file for reading */
|
||||
{
|
||||
int i;
|
||||
STREAMFILE * file;
|
||||
file = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE);
|
||||
if (!file) goto fail;
|
||||
for (i=0;i<channel_count;i++) {
|
||||
vgmstream->ch[i].streamfile = file;
|
||||
|
||||
vgmstream->ch[i].channel_start_offset=
|
||||
vgmstream->ch[i].offset=start_offset+
|
||||
vgmstream->interleave_block_size*i;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return vgmstream;
|
||||
|
||||
/* clean up anything we may have opened */
|
||||
fail:
|
||||
if (vgmstream) close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
@ -1,70 +0,0 @@
|
||||
#include "meta.h"
|
||||
#include "../util.h"
|
||||
|
||||
/* P2BT : Pop'n'Music 7 & 8 Bgm File */
|
||||
|
||||
VGMSTREAM * init_vgmstream_ps2_p2bt(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
char filename[PATH_LIMIT];
|
||||
|
||||
int loop_flag=0;
|
||||
int channel_count;
|
||||
off_t start_offset;
|
||||
int i;
|
||||
|
||||
/* check extension, case insensitive */
|
||||
streamFile->get_name(streamFile,filename,sizeof(filename));
|
||||
if (strcasecmp("p2bt",filename_extension(filename))) goto fail;
|
||||
|
||||
if((read_32bitBE(0x00,streamFile)!=0x4d4F5645) && // MOVE
|
||||
(read_32bitBE(0x00,streamFile)!=0x50324254)) // P2BT
|
||||
goto fail;
|
||||
|
||||
/* check loop */
|
||||
loop_flag = (read_32bitLE(0x0C,streamFile)!=0);
|
||||
channel_count=read_32bitLE(0x20,streamFile);
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channel_count,loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
/* fill in the vital statistics */
|
||||
vgmstream->channels = channel_count;
|
||||
vgmstream->sample_rate = read_32bitLE(0x08,streamFile);;
|
||||
|
||||
/* Check for Compression Scheme */
|
||||
vgmstream->coding_type = coding_PSX;
|
||||
vgmstream->num_samples = read_32bitLE(0x10,streamFile)/16*28/vgmstream->channels;
|
||||
|
||||
/* Get loop point values */
|
||||
if(vgmstream->loop_flag) {
|
||||
vgmstream->loop_start_sample = read_32bitLE(0x0C,streamFile)/16*28/vgmstream->channels;
|
||||
vgmstream->loop_end_sample = vgmstream->num_samples;
|
||||
}
|
||||
|
||||
vgmstream->interleave_block_size = read_32bitLE(0x14,streamFile);;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->meta_type = meta_PS2_P2BT;
|
||||
|
||||
start_offset = (off_t)0x800;
|
||||
|
||||
/* open the file for reading by each channel */
|
||||
{
|
||||
for (i=0;i<channel_count;i++) {
|
||||
vgmstream->ch[i].streamfile = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE);
|
||||
|
||||
if (!vgmstream->ch[i].streamfile) goto fail;
|
||||
|
||||
vgmstream->ch[i].channel_start_offset=
|
||||
vgmstream->ch[i].offset=
|
||||
(off_t)(start_offset+vgmstream->interleave_block_size*i);
|
||||
}
|
||||
}
|
||||
|
||||
return vgmstream;
|
||||
|
||||
/* clean up anything we may have opened */
|
||||
fail:
|
||||
if (vgmstream) close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
74
src/meta/psnd.c
Normal file
74
src/meta/psnd.c
Normal file
@ -0,0 +1,74 @@
|
||||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
|
||||
|
||||
/* PSND - from Polarbit games [Crash Bandicoot Nitro Kart 3D/2 (iOS), Reckless Racing 2 (Android/iOS)] */
|
||||
VGMSTREAM* init_vgmstream_psnd(STREAMFILE* sf) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
uint32_t start_offset, data_size, type;
|
||||
int loop_flag, channels, sample_rate;
|
||||
|
||||
|
||||
/* checks */
|
||||
if (!is_id32be(0x00,sf, "PSND"))
|
||||
return NULL;
|
||||
/* .psn: actual extension in exes */
|
||||
if (!check_extensions(sf, "psn"))
|
||||
return NULL;
|
||||
|
||||
data_size = read_u32le(0x04, sf); /* after this field */
|
||||
type = read_u32le(0x08,sf);
|
||||
sample_rate = read_u16le(0x0c,sf);
|
||||
|
||||
switch (type) {
|
||||
case 0x0030006: /* CBNK */
|
||||
channels = read_u8(0xE,sf);
|
||||
if (read_u8(0x0f, sf) != 16) goto fail; /* bps */
|
||||
start_offset = 0x10;
|
||||
|
||||
break;
|
||||
case 0x0000004: /* RR */
|
||||
channels = 1;
|
||||
start_offset = 0x0e;
|
||||
break;
|
||||
default:
|
||||
goto fail;
|
||||
}
|
||||
|
||||
data_size = data_size + 0x08 - start_offset;
|
||||
loop_flag = 0; /* generally 22050hz music loops */
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channels, loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
switch (type) {
|
||||
case 0x0030006:
|
||||
vgmstream->coding_type = coding_PCM16LE;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = 0x02;
|
||||
vgmstream->num_samples = pcm16_bytes_to_samples(data_size, channels);
|
||||
break;
|
||||
case 0x0000004:
|
||||
vgmstream->coding_type = coding_DVI_IMA;
|
||||
vgmstream->layout_type = layout_none;
|
||||
vgmstream->num_samples = ima_bytes_to_samples(data_size, channels);
|
||||
break;
|
||||
default:
|
||||
goto fail;
|
||||
}
|
||||
|
||||
vgmstream->loop_start_sample = 0;
|
||||
vgmstream->loop_end_sample = vgmstream->num_samples;
|
||||
|
||||
vgmstream->meta_type = meta_PSND;
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream, sf, start_offset))
|
||||
goto fail;
|
||||
return vgmstream;
|
||||
fail:
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
57
src/meta/vig_kces.c
Normal file
57
src/meta/vig_kces.c
Normal file
@ -0,0 +1,57 @@
|
||||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
|
||||
|
||||
/* .vig - from Konami/KCE Studio games [Pop'n Music 11~14 (PS2), Dance Dance Revolution SuperNova/X (PS2)] */
|
||||
VGMSTREAM* init_vgmstream_vig_kces(STREAMFILE* sf) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
uint32_t data_offset, data_size;
|
||||
int loop_flag, channels, sample_rate, interleave;
|
||||
uint32_t loop_start, loop_end;
|
||||
|
||||
|
||||
/* checks */
|
||||
if (read_u32be(0x00,sf) != 0x01006408)
|
||||
return NULL;
|
||||
/* .vig: actual extension from DDR exes */
|
||||
if (!check_extensions(sf, "vig"))
|
||||
return NULL;
|
||||
|
||||
/* note this is almost the same as GbTs, may be fused later */
|
||||
/* 04: null */
|
||||
data_offset = read_u32le(0x08,sf);
|
||||
data_size = read_u32le(0x0C,sf); /* without padding */
|
||||
loop_start = read_u32le(0x10,sf); /* (0x00 if not set) */
|
||||
loop_end = read_u32le(0x14,sf); /* (0x00 if not set) */
|
||||
sample_rate = read_s32le(0x18,sf);
|
||||
channels = read_s32le(0x1C,sf);
|
||||
/* 20: 0? */
|
||||
interleave = read_u32le(0x24,sf); /* 0 for mono */
|
||||
/* 30+: garbage from other data */
|
||||
|
||||
loop_flag = (loop_end > 0);
|
||||
loop_end += loop_start; /* loop region matches PS-ADPCM flags */
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channels, loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
vgmstream->num_samples = ps_bytes_to_samples(data_size, channels);
|
||||
vgmstream->loop_start_sample = ps_bytes_to_samples(loop_start, channels);
|
||||
vgmstream->loop_end_sample = ps_bytes_to_samples(loop_end, channels);
|
||||
|
||||
vgmstream->coding_type = coding_PSX;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = interleave;
|
||||
|
||||
vgmstream->meta_type = meta_VIG_KCES;
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream, sf, data_offset))
|
||||
goto fail;
|
||||
return vgmstream;
|
||||
fail:
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
@ -1,52 +0,0 @@
|
||||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
|
||||
/* VIS - from Konami games [AirForce Delta Strike (PS2) (PS2)] */
|
||||
VGMSTREAM * init_vgmstream_vis(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
off_t start_offset;
|
||||
size_t data_size;
|
||||
int loop_flag, channel_count;
|
||||
|
||||
|
||||
/* checks */
|
||||
if ( !check_extensions(streamFile,"vis") )
|
||||
goto fail;
|
||||
if (read_32bitBE(0x00,streamFile) != 0x56495341) /* "VISA" */
|
||||
goto fail;
|
||||
|
||||
start_offset = 0x800;
|
||||
data_size = get_streamfile_size(streamFile) - start_offset;
|
||||
|
||||
loop_flag = read_32bitLE(0x18,streamFile);
|
||||
channel_count = read_32bitLE(0x20,streamFile); /* assumed */
|
||||
/* 0x1c: always 0x10 */
|
||||
/* 0x24: always 0x01 */
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channel_count,loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->meta_type = meta_VIS;
|
||||
vgmstream->sample_rate = read_32bitLE(0x08,streamFile);
|
||||
vgmstream->num_samples = ps_bytes_to_samples(data_size,channel_count);
|
||||
vgmstream->loop_start_sample = ps_bytes_to_samples(read_32bitLE(0x0c,streamFile),channel_count);
|
||||
vgmstream->loop_end_sample = ps_bytes_to_samples(read_32bitLE(0x10,streamFile),channel_count);
|
||||
|
||||
vgmstream->coding_type = coding_PSX;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = read_32bitLE(0x14,streamFile); /* usually 0x10 or 0x4000 */
|
||||
if (vgmstream->interleave_block_size)
|
||||
vgmstream->interleave_last_block_size =
|
||||
(data_size % (vgmstream->interleave_block_size*channel_count)) / channel_count;
|
||||
read_string(vgmstream->stream_name,0x10+1, 0x28,streamFile);
|
||||
|
||||
if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) )
|
||||
goto fail;
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
@ -9,11 +9,11 @@ VGMSTREAM* init_vgmstream_waf(STREAMFILE* sf) {
|
||||
|
||||
|
||||
/* checks */
|
||||
if (!check_extensions(sf, "waf"))
|
||||
goto fail;
|
||||
|
||||
if (!is_id32be(0x00,sf, "WAF\0"))
|
||||
goto fail;
|
||||
return NULL;
|
||||
if (!check_extensions(sf, "waf"))
|
||||
return NULL;
|
||||
|
||||
if (read_u32le(0x34,sf) + 0x38 != get_streamfile_size(sf))
|
||||
goto fail;
|
||||
|
||||
|
@ -15,8 +15,6 @@
|
||||
* (adapted from hcs's code to do multiple querys in the same table)
|
||||
*/
|
||||
|
||||
//todo move to src/util subdir
|
||||
|
||||
/* opaque struct */
|
||||
typedef struct utf_context utf_context;
|
||||
|
||||
|
@ -93,7 +93,7 @@ init_vgmstream_t init_vgmstream_functions[] = {
|
||||
init_vgmstream_rstm_rockstar,
|
||||
init_vgmstream_acm,
|
||||
init_vgmstream_mus_acm,
|
||||
init_vgmstream_ps2_kces,
|
||||
init_vgmstream_vig_kces,
|
||||
init_vgmstream_hxd,
|
||||
init_vgmstream_vsv,
|
||||
init_vgmstream_ps2_pcm,
|
||||
@ -149,8 +149,8 @@ init_vgmstream_t init_vgmstream_functions[] = {
|
||||
init_vgmstream_mul,
|
||||
init_vgmstream_thp,
|
||||
init_vgmstream_sts,
|
||||
init_vgmstream_ps2_p2bt,
|
||||
init_vgmstream_ps2_gbts,
|
||||
init_vgmstream_p2bt_move_visa,
|
||||
init_vgmstream_gbts,
|
||||
init_vgmstream_wii_sng,
|
||||
init_vgmstream_ngc_dsp_iadp,
|
||||
init_vgmstream_aax,
|
||||
@ -233,7 +233,7 @@ init_vgmstream_t init_vgmstream_functions[] = {
|
||||
init_vgmstream_xwav_new,
|
||||
init_vgmstream_xwav_old,
|
||||
init_vgmstream_hyperscan_kvag,
|
||||
init_vgmstream_ios_psnd,
|
||||
init_vgmstream_psnd,
|
||||
init_vgmstream_adp_wildfire,
|
||||
init_vgmstream_adp_qd,
|
||||
init_vgmstream_eb_sfx,
|
||||
@ -245,7 +245,7 @@ init_vgmstream_t init_vgmstream_functions[] = {
|
||||
init_vgmstream_mss,
|
||||
init_vgmstream_ps2_hsf,
|
||||
init_vgmstream_ivag,
|
||||
init_vgmstream_ps2_2pfs,
|
||||
init_vgmstream_2pfs,
|
||||
init_vgmstream_xnb,
|
||||
init_vgmstream_ubi_ckd,
|
||||
init_vgmstream_ps2_vbk,
|
||||
@ -375,7 +375,6 @@ init_vgmstream_t init_vgmstream_functions[] = {
|
||||
init_vgmstream_msv,
|
||||
init_vgmstream_sdf,
|
||||
init_vgmstream_svg,
|
||||
init_vgmstream_vis,
|
||||
init_vgmstream_vai,
|
||||
init_vgmstream_aif_asobo,
|
||||
init_vgmstream_ao,
|
||||
@ -524,6 +523,7 @@ init_vgmstream_t init_vgmstream_functions[] = {
|
||||
init_vgmstream_nxof,
|
||||
init_vgmstream_gwb_gwd,
|
||||
init_vgmstream_s_pack,
|
||||
init_vgmstream_cbx,
|
||||
|
||||
/* lower priority metas (no clean header identity, somewhat ambiguous, or need extension/companion file to identify) */
|
||||
init_vgmstream_scd_pcm,
|
||||
|
@ -337,7 +337,7 @@ typedef enum {
|
||||
meta_STER,
|
||||
meta_BG00, /* Ibara, Mushihimesama */
|
||||
meta_RSTM_ROCKSTAR,
|
||||
meta_PS2_KCES, /* Dance Dance Revolution */
|
||||
meta_VIG_KCES,
|
||||
meta_HXD,
|
||||
meta_VSV,
|
||||
meta_SCD_PCM, /* Lunar - Eternal Blue */
|
||||
@ -428,8 +428,8 @@ typedef enum {
|
||||
meta_FFXI_BGW, /* FFXI (PC) BGW */
|
||||
meta_FFXI_SPW, /* FFXI (PC) SPW */
|
||||
meta_STS,
|
||||
meta_PS2_P2BT, /* Pop'n'Music 7 Audio File */
|
||||
meta_PS2_GBTS, /* Pop'n'Music 9 Audio File */
|
||||
meta_P2BT_MOVE_VISA,
|
||||
meta_GBTS,
|
||||
meta_NGC_DSP_IADP, /* Gamecube Interleave DSP */
|
||||
meta_PS2_MCG, /* Gunvari MCG Files (was name .GCM on disk) */
|
||||
meta_ZSD, /* Dragon Booster ZSD */
|
||||
@ -496,7 +496,7 @@ typedef enum {
|
||||
meta_RAW_SNDS,
|
||||
meta_PS2_WMUS, /* The Warriors (PS2) */
|
||||
meta_HYPERSCAN_KVAG, /* Hyperscan KVAG/BVG */
|
||||
meta_IOS_PSND, /* Crash Bandicoot Nitro Kart 2 (iOS) */
|
||||
meta_PSND,
|
||||
meta_ADP_WILDFIRE,
|
||||
meta_QD_ADP,
|
||||
meta_EB_SFX, /* Excitebots .sfx */
|
||||
@ -508,7 +508,7 @@ typedef enum {
|
||||
meta_MSS, /* Guerilla: ShellShock Nam '67 (PS2/Xbox), Killzone (PS2) */
|
||||
meta_PS2_HSF, /* Lowrider (PS2) */
|
||||
meta_IVAG,
|
||||
meta_PS2_2PFS, /* Konami: Mahoromatic: Moetto - KiraKira Maid-San, GANTZ (PS2) */
|
||||
meta_2PFS,
|
||||
meta_PS2_VBK, /* Disney's Stitch - Experiment 626 */
|
||||
meta_OTM, /* Otomedius (Arcade) */
|
||||
meta_CSTM, /* Nintendo 3DS CSTM (Century Stream) */
|
||||
@ -602,7 +602,6 @@ typedef enum {
|
||||
meta_MSV,
|
||||
meta_SDF,
|
||||
meta_SVG, /* Hunter - The Reckoning - Wayward (PS2) */
|
||||
meta_VIS, /* AirForce Delta Strike (PS2) */
|
||||
meta_VAI, /* Ratatouille (GC) */
|
||||
meta_AIF_ASOBO, /* Ratatouille (PC) */
|
||||
meta_AO, /* Cloudphobia (PC) */
|
||||
@ -704,6 +703,7 @@ typedef enum {
|
||||
meta_SNDS,
|
||||
meta_NXOF,
|
||||
meta_GWB_GWD,
|
||||
meta_CBX,
|
||||
|
||||
} meta_t;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user