mirror of
https://github.com/vgmstream/vgmstream.git
synced 2025-01-19 00:04:04 +01:00
Add more SqueakStream/SqueakSample codecs
This commit is contained in:
parent
fef676f840
commit
7cb570b2ad
@ -588,6 +588,7 @@ void free_celt_fsb(celt_codec_data* data);
|
||||
typedef struct speex_codec_data speex_codec_data;
|
||||
|
||||
speex_codec_data* init_speex_ea(int channels);
|
||||
speex_codec_data* init_speex_torus(int channels);
|
||||
void decode_speex(VGMSTREAM* vgmstream, sample_t* outbuf, int32_t samples_to_do);
|
||||
void reset_speex(speex_codec_data* data);
|
||||
void seek_speex(VGMSTREAM* vgmstream, int32_t num_sample);
|
||||
|
@ -10,15 +10,19 @@
|
||||
#define SPEEX_DECODE_OK 0 /* -1 for end of stream, -2 corrupt stream */
|
||||
|
||||
|
||||
typedef enum { EA, TORUS } type_t;
|
||||
|
||||
/* opaque struct */
|
||||
struct speex_codec_data {
|
||||
type_t type;
|
||||
|
||||
/* config */
|
||||
int channels;
|
||||
int samples_discard;
|
||||
int encoder_delay;
|
||||
|
||||
uint8_t buf[SPEEX_MAX_FRAME_SIZE];
|
||||
uint8_t frame_size;
|
||||
int frame_size;
|
||||
|
||||
int16_t* samples;
|
||||
int frame_samples;
|
||||
@ -32,7 +36,7 @@ struct speex_codec_data {
|
||||
|
||||
|
||||
/* raw SPEEX */
|
||||
speex_codec_data* init_speex_ea(int channels) {
|
||||
static speex_codec_data* init_speex(type_t type, int channels) {
|
||||
int res, sample_rate;
|
||||
speex_codec_data* data = NULL;
|
||||
|
||||
@ -40,7 +44,9 @@ speex_codec_data* init_speex_ea(int channels) {
|
||||
data = calloc(1, sizeof(speex_codec_data));
|
||||
if (!data) goto fail;
|
||||
|
||||
//TODO: EA uses N decoders, unknown layout (known samples are mono)
|
||||
data->type = type;
|
||||
|
||||
//TODO: unknown layout (known samples are mono, EA: N decoders, Torus: N too?)
|
||||
data->channels = channels;
|
||||
if (channels != 1)
|
||||
goto fail;
|
||||
@ -78,6 +84,14 @@ fail:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
speex_codec_data* init_speex_ea(int channels) {
|
||||
return init_speex(EA, channels);
|
||||
}
|
||||
|
||||
speex_codec_data* init_speex_torus(int channels) {
|
||||
return init_speex(TORUS, channels);
|
||||
}
|
||||
|
||||
|
||||
static int decode_frame(speex_codec_data* data) {
|
||||
int res;
|
||||
@ -102,8 +116,18 @@ fail:
|
||||
static int read_frame(speex_codec_data* data, VGMSTREAMCHANNEL* stream) {
|
||||
size_t bytes;
|
||||
|
||||
data->frame_size = read_u8(stream->offset, stream->streamfile);
|
||||
stream->offset += 0x01;
|
||||
switch(data->type) {
|
||||
case EA:
|
||||
data->frame_size = read_u8(stream->offset, stream->streamfile);
|
||||
stream->offset += 0x01;
|
||||
break;
|
||||
case TORUS:
|
||||
data->frame_size = read_u16le(stream->offset, stream->streamfile);
|
||||
stream->offset += 0x02;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (data->frame_size == 0) goto fail;
|
||||
|
||||
bytes = read_streamfile(data->buf, stream->offset, data->frame_size, stream->streamfile);
|
||||
|
@ -175,6 +175,7 @@
|
||||
<ClInclude Include="util\cri_keys.h" />
|
||||
<ClInclude Include="util\cri_utf.h" />
|
||||
<ClInclude Include="util\endianness.h" />
|
||||
<ClInclude Include="util\layout_utils.h" />
|
||||
<ClInclude Include="util\log.h" />
|
||||
<ClInclude Include="util\m2_psb.h" />
|
||||
<ClInclude Include="util\miniz.h" />
|
||||
@ -737,6 +738,7 @@
|
||||
<ClCompile Include="util\companion_files.c" />
|
||||
<ClCompile Include="util\cri_keys.c" />
|
||||
<ClCompile Include="util\cri_utf.c" />
|
||||
<ClCompile Include="util\layout_utils.c" />
|
||||
<ClCompile Include="util\log.c" />
|
||||
<ClCompile Include="util\m2_psb.c" />
|
||||
<ClCompile Include="util\miniz.c" />
|
||||
|
@ -350,6 +350,9 @@
|
||||
<ClInclude Include="util\endianness.h">
|
||||
<Filter>util\Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="util\layout_utils.h">
|
||||
<Filter>util\Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="util\log.h">
|
||||
<Filter>util\Header Files</Filter>
|
||||
</ClInclude>
|
||||
@ -2032,6 +2035,9 @@
|
||||
<ClCompile Include="util\cri_utf.c">
|
||||
<Filter>util\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="util\layout_utils.c">
|
||||
<Filter>util\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="util\log.c">
|
||||
<Filter>util\Source Files</Filter>
|
||||
</ClCompile>
|
||||
|
@ -2,9 +2,10 @@
|
||||
#include "../layout/layout.h"
|
||||
#include "../coding/coding.h"
|
||||
#include "../util/endianness.h"
|
||||
#include "../util/layout_utils.h"
|
||||
|
||||
#define SQUEAK_MAX_CHANNELS 8 /* seen 3 in some voices */
|
||||
typedef enum { PCM16LE, PCM16BE, PCM8, DSP, PSX, MSIMA, IMA } squeak_type_t;
|
||||
#define SQUEAK_MAX_CHANNELS 6 /* seen 3 in some voices */
|
||||
typedef enum { PCM16LE, PCM16BE, PCM8, DSP, PSX, MSIMA, IMA, XMA2, VORBIS, SPEEX } squeak_type_t;
|
||||
|
||||
typedef struct {
|
||||
squeak_type_t type;
|
||||
@ -26,6 +27,8 @@ typedef struct {
|
||||
uint32_t coef_offset;
|
||||
uint32_t coef_spacing;
|
||||
|
||||
uint32_t data_offsets[SQUEAK_MAX_CHANNELS];
|
||||
uint32_t coef_offsets[SQUEAK_MAX_CHANNELS];
|
||||
uint32_t data_size;
|
||||
|
||||
bool big_endian;
|
||||
@ -37,22 +40,21 @@ typedef struct {
|
||||
static VGMSTREAM* init_vgmstream_squeak_common(STREAMFILE* sf, squeak_header_t* h);
|
||||
|
||||
|
||||
/* SqueakStream - from Torus games (as identified in .hnk subdirs) */
|
||||
/* SqueakStream - from Torus games (name/engine as identified in .hnk subdirs) */
|
||||
VGMSTREAM* init_vgmstream_squeakstream(STREAMFILE* sf) {
|
||||
squeak_header_t h = {0};
|
||||
bool is_old = false;
|
||||
|
||||
|
||||
/* checks */
|
||||
h.big_endian = false;
|
||||
if (is_id32be(0x00,sf, "RAWI")) {
|
||||
h.big_endian = false;
|
||||
if (is_id32be(0x00,sf, "RAWI") || is_id32be(0x00,sf, "VORB") || is_id32be(0x00,sf, "SPEX")) {
|
||||
h.big_endian = false; /* VORB/SPEX only use vorbis/speex but no apparent diffs */
|
||||
}
|
||||
else if (is_id32be(0x00,sf, "IWAR")) {
|
||||
h.big_endian = true; /* Wii/PS3/X360 */
|
||||
}
|
||||
else {
|
||||
/* no header id so test codec in dumb endian */
|
||||
/* no header id in early version so test codec in dumb endian */
|
||||
if ((read_u32le(0x00,sf) & 0x00FFFFFF) > 9 || (read_u32be(0x00,sf) & 0x00FFFFFF) > 9)
|
||||
return NULL;
|
||||
is_old = true;
|
||||
@ -137,13 +139,17 @@ VGMSTREAM* init_vgmstream_squeakstream(STREAMFILE* sf) {
|
||||
if (h.extb_offset > h.name_offset) return NULL;
|
||||
|
||||
switch(h.codec) {
|
||||
case 0x00: h.type = DSP; break; /* Turbo Super Stunt Squad (Wii/3DS), Penguins of Madagascar (Wii/U/3DS) */
|
||||
case 0x01: h.type = PCM16LE; break; /* Falling Skies The Game (PC) */
|
||||
case 0x02: h.type = PCM16BE; break; /* Falling Skies The Game (X360) */
|
||||
case 0x03: h.type = PSX; break; /* How to Train Your Dragon 2 (PS3), Falling Skies The Game (PS3) */
|
||||
case 0x05: h.type = PCM8; break; /* Scooby Doo and the Spooky Swamp (DS), Scooby Doo! First Frights (DS) */
|
||||
case 0x09: h.type = MSIMA; break; /* Turbo Super Stunt Squad (DS) */
|
||||
case 0x00: h.type = DSP; break; /* Turbo Super Stunt Squad (Wii/3DS), Penguins of Madagascar (Wii/U/3DS) */
|
||||
case 0x01: h.type = PCM16LE; break; /* Falling Skies The Game (PC) */
|
||||
case 0x02: h.type = h.big_endian ? PCM16BE : PCM16LE; break; /* Falling Skies The Game (X360)-be, Scooby Doo and the Spooky Swamp (PC)-le */
|
||||
case 0x03: h.type = PSX; break; /* How to Train Your Dragon 2 (PS3), Falling Skies The Game (PS3) */
|
||||
case 0x04: h.type = MSIMA; break; /* Barbie Dreamhouse Party (DS) */
|
||||
case 0x05: h.type = PCM8; break; /* Scooby Doo and the Spooky Swamp (DS), Scooby Doo! First Frights (DS) */
|
||||
case 0x07: h.type = SPEEX; break; /* Scooby Doo and the Spooky Swamp (PC) */
|
||||
case 0x08: h.type = VORBIS; break; /* Scooby Doo and the Spooky Swamp (PC) */
|
||||
case 0x09: h.type = MSIMA; break; /* Turbo Super Stunt Squad (DS) */
|
||||
default:
|
||||
VGM_LOG("SqueakStream: unknown codec %x\n", h.codec);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -151,7 +157,7 @@ VGMSTREAM* init_vgmstream_squeakstream(STREAMFILE* sf) {
|
||||
}
|
||||
|
||||
|
||||
/* SqueakSample - from Torus games (as identified in .hnk subdirs) */
|
||||
/* SqueakSample - from Torus games (name/engine as identified in .hnk subdirs) */
|
||||
VGMSTREAM* init_vgmstream_squeaksample(STREAMFILE* sf) {
|
||||
squeak_header_t h = {0};
|
||||
|
||||
@ -187,7 +193,6 @@ VGMSTREAM* init_vgmstream_squeaksample(STREAMFILE* sf) {
|
||||
h.loop_end = read_s32(offset + 0x0c,sf);
|
||||
if (h.loop_start > h.loop_end || h.loop_end > h.num_samples) return NULL;
|
||||
h.codec = read_s32(offset + 0x10,sf);
|
||||
if (h.codec > 0x09) return NULL;
|
||||
h.sample_rate = read_s32(offset + 0x14,sf);
|
||||
if (h.sample_rate > 48000 || h.sample_rate < 0) return NULL;
|
||||
|
||||
@ -206,14 +211,25 @@ VGMSTREAM* init_vgmstream_squeaksample(STREAMFILE* sf) {
|
||||
h.external_data = (h.data_offset & 0xF0000000);
|
||||
h.data_offset = h.data_offset & 0x0FFFFFFF;
|
||||
|
||||
/* absolute offsets, should read for each channel but simplify
|
||||
* (also channels may have padding, but files end with no padding) */
|
||||
if (h.channels > 1) {
|
||||
int separation = h.codec == 0xFFFE0001 ? 0x68 : 0x2c;
|
||||
uint32_t data_offset = read_u32le(offset + 0x04 + 1 * separation, sf) & 0x0FFFFFFF;
|
||||
uint32_t coef_offset = read_u32le(offset + 0x28 + 1 * separation, sf);
|
||||
h.interleave = data_offset - h.data_offset; /* distance */
|
||||
h.coef_spacing = coef_offset - h.coef_offset;
|
||||
/* each channel has its own info but mostly repeats (data may have padding, but files end with no padding) */
|
||||
{
|
||||
int separation = 0;
|
||||
switch(h.codec) {
|
||||
case 0xFFFE0001:
|
||||
case 0x0001FFFE:
|
||||
case 0x01660001: separation = 0x68; break;
|
||||
default: separation = 0x2c; break;
|
||||
}
|
||||
|
||||
for (int i = 0; i < h.channels; i++) {
|
||||
h.data_offsets[i] = read_u32le(offset + 0x04 + i * separation, sf) & 0x0FFFFFFF;
|
||||
h.coef_offsets[i] = read_u32le(offset + 0x28 + i * separation, sf);
|
||||
}
|
||||
|
||||
if (h.channels > 1) {
|
||||
h.interleave = h.data_offsets[1] - h.data_offsets[0];
|
||||
h.coef_spacing = h.coef_offsets[1] - h.coef_offsets[0];
|
||||
}
|
||||
}
|
||||
|
||||
switch(h.codec) {
|
||||
@ -223,8 +239,11 @@ VGMSTREAM* init_vgmstream_squeaksample(STREAMFILE* sf) {
|
||||
case 0x07: h.type = PSX; break; /* How to Train Your Dragon 2 (PS3), Falling Skies The Game (PS3) */
|
||||
case 0x08: /* (same as below for unlooped audio) */
|
||||
case 0x09: h.type = IMA; break; /* Scooby-Doo! First Frights (DS), Turbo Super Stunt Squad (DS) */
|
||||
case 0xFFFE0001: h.type = h.big_endian ? PCM16BE : PCM16LE; break; /* Falling Skies The Game (X360) */
|
||||
case 0xFFFE0001: h.type = PCM16BE; break; /* Falling Skies The Game (X360) */
|
||||
case 0x0001FFFE: h.type = PCM16LE; break; /* Scooby Doo and the Spooky Swamp (PC), Monster High: New Ghoul in School (PC) */
|
||||
case 0x01660001: h.type = XMA2; break; /* Rise of the Guardians (X360) */
|
||||
default:
|
||||
VGM_LOG("SqueakSample: unknown codec %x\n", h.codec);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -295,6 +314,7 @@ fail:
|
||||
static VGMSTREAM* init_vgmstream_squeak_common(STREAMFILE* sf, squeak_header_t* h) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
STREAMFILE* sb = NULL;
|
||||
STREAMFILE* sf_body = NULL;
|
||||
|
||||
/* common */
|
||||
int loop_flag = h->loop_end > 0;
|
||||
@ -306,6 +326,7 @@ static VGMSTREAM* init_vgmstream_squeak_common(STREAMFILE* sf, squeak_header_t*
|
||||
if (!sb) goto fail;
|
||||
}
|
||||
|
||||
sf_body = sb ? sb : sf;
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(h->channels, loop_flag);
|
||||
@ -334,12 +355,13 @@ static VGMSTREAM* init_vgmstream_squeak_common(STREAMFILE* sf, squeak_header_t*
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = h->interleave; /* not 0x02 */
|
||||
|
||||
break;
|
||||
|
||||
case PCM16BE:
|
||||
vgmstream->coding_type = coding_PCM16BE;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = h->interleave; /* not 0x02 */
|
||||
|
||||
/* etbl_offset may set offsets to RIFF fmts per channel) */
|
||||
break;
|
||||
|
||||
case PSX:
|
||||
@ -358,7 +380,7 @@ static VGMSTREAM* init_vgmstream_squeak_common(STREAMFILE* sf, squeak_header_t*
|
||||
vgmstream->coding_type = coding_MS_IMA;
|
||||
vgmstream->layout_type = layout_none;
|
||||
//vgmstream->interleave_block_size = h->interleave; /* unused? (mono) */
|
||||
vgmstream->frame_size = 0x20;
|
||||
vgmstream->frame_size = h->codec == 0x04 ? 0x400 : 0x20;
|
||||
break;
|
||||
|
||||
case IMA:
|
||||
@ -370,8 +392,46 @@ static VGMSTREAM* init_vgmstream_squeak_common(STREAMFILE* sf, squeak_header_t*
|
||||
h->data_offset += 0x04;
|
||||
break;
|
||||
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
case XMA2: {
|
||||
/* uses separate mono streams */
|
||||
vgmstream->coding_type = coding_FFmpeg;
|
||||
for (int i = 0; i < h->channels; i++) {
|
||||
uint32_t offset = h->data_offsets[i];
|
||||
uint32_t next_offset = (i + 1 == h->channels) ? get_streamfile_size(sf_body) : h->data_offsets[i+1];
|
||||
uint32_t data_size = next_offset - offset;
|
||||
int layer_channels = 1;
|
||||
|
||||
vgmstream->codec_data = init_ffmpeg_xma2_raw(sf_body, offset, data_size, h->num_samples, layer_channels, h->sample_rate, 0, 0);
|
||||
if (!layered_add_codec(vgmstream, 0, layer_channels))
|
||||
goto fail;
|
||||
}
|
||||
if (!layered_add_done(vgmstream))
|
||||
goto fail;
|
||||
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
#ifdef VGM_USE_VORBIS
|
||||
case VORBIS:
|
||||
vgmstream->codec_data = init_ogg_vorbis(sf_body, h->data_offset, 0, NULL);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->coding_type = coding_OGG_VORBIS;
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
||||
break;
|
||||
#endif
|
||||
#ifdef VGM_USE_SPEEX
|
||||
case SPEEX: {
|
||||
vgmstream->codec_data = init_speex_torus(h->channels);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->coding_type = coding_SPEEX;
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
default:
|
||||
vgm_logi("RAWI: unknown codec %x (report)\n", h->codec);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
|
@ -1,10 +1,12 @@
|
||||
#ifndef _UTIL_ENDIAN_H
|
||||
#define _UTIL_ENDIAN_H
|
||||
#ifndef _ENDIANNESS_H
|
||||
#define _ENDIANNESS_H
|
||||
|
||||
#include "../streamfile.h"
|
||||
#include "reader_get.h"
|
||||
#include "reader_sf.h"
|
||||
|
||||
typedef uint64_t (*read_u64_t)(off_t, STREAMFILE*);
|
||||
typedef int64_t (*read_s64_t)(off_t, STREAMFILE*);
|
||||
typedef uint32_t (*read_u32_t)(off_t, STREAMFILE*);
|
||||
typedef int32_t (*read_s32_t)(off_t, STREAMFILE*);
|
||||
typedef uint16_t (*read_u16_t)(off_t, STREAMFILE*);
|
||||
|
71
src/util/layout_utils.c
Normal file
71
src/util/layout_utils.c
Normal file
@ -0,0 +1,71 @@
|
||||
#include "layout_utils.h"
|
||||
|
||||
#include "../vgmstream.h"
|
||||
#include "../layout/layout.h"
|
||||
|
||||
bool layered_add_codec(VGMSTREAM* vs, int layers, int layer_channels) {
|
||||
if (!vs || !vs->codec_data) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!layer_channels)
|
||||
layer_channels = 1;
|
||||
if (!layers)
|
||||
layers = vs->channels / layer_channels;
|
||||
|
||||
int i;
|
||||
layered_layout_data* data;
|
||||
|
||||
switch(vs->layout_type) {
|
||||
case layout_segmented: //to be improved
|
||||
goto fail;
|
||||
|
||||
case layout_layered:
|
||||
data = vs->layout_data;
|
||||
|
||||
i = data->curr_layer;
|
||||
break;
|
||||
|
||||
default:
|
||||
data = init_layout_layered(layers);
|
||||
if (!data) goto fail;
|
||||
vs->layout_data = data;
|
||||
vs->layout_type = layout_layered;
|
||||
|
||||
i = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
data->layers[i] = allocate_vgmstream(layer_channels, vs->loop_flag);
|
||||
if (!data->layers[i]) goto fail;
|
||||
|
||||
data->layers[i]->meta_type = vs->meta_type;
|
||||
data->layers[i]->sample_rate = vs->sample_rate;
|
||||
data->layers[i]->num_samples = vs->num_samples;
|
||||
data->layers[i]->loop_start_sample = vs->loop_start_sample;
|
||||
data->layers[i]->loop_end_sample = vs->loop_end_sample;
|
||||
|
||||
data->layers[i]->codec_data = vs->codec_data;
|
||||
if (!data->layers[i]->codec_data) goto fail;
|
||||
data->layers[i]->coding_type = vs->coding_type;
|
||||
data->layers[i]->layout_type = layout_none;
|
||||
|
||||
vs->codec_data = NULL; /* moved to layer, don't hold it */
|
||||
|
||||
data->curr_layer++;
|
||||
|
||||
return true;
|
||||
fail:
|
||||
return false;
|
||||
}
|
||||
|
||||
bool layered_add_done(VGMSTREAM* vs) {
|
||||
//TODO: some extra checks/setup?
|
||||
|
||||
if (!setup_layout_layered(vs->layout_data))
|
||||
goto fail;
|
||||
|
||||
return true;
|
||||
fail:
|
||||
return false;
|
||||
}
|
12
src/util/layout_utils.h
Normal file
12
src/util/layout_utils.h
Normal file
@ -0,0 +1,12 @@
|
||||
#ifndef _LAYOUTS_UTIL_H
|
||||
#define _LAYOUTS_UTIL_H
|
||||
|
||||
#include "../vgmstream.h"
|
||||
|
||||
/* add a new layer from codec data (setups layout if needed)
|
||||
* codec is passed in the vs for easier free/etc control */
|
||||
bool layered_add_codec(VGMSTREAM* vs, int layers, int layer_channels);
|
||||
|
||||
/* call when done adding layers */
|
||||
bool layered_add_done(VGMSTREAM* vs);
|
||||
#endif
|
@ -273,6 +273,7 @@ typedef struct {
|
||||
int input_channels; /* internal buffer channels */
|
||||
int output_channels; /* resulting channels (after mixing, if applied) */
|
||||
int external_looping; /* don't loop using per-layer loops, but layout's own looping */
|
||||
int curr_layer; /* helper */
|
||||
} layered_layout_data;
|
||||
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user