mirror of
https://github.com/vgmstream/vgmstream.git
synced 2025-01-19 00:04:04 +01:00
Merge pull request #811 from bnnm/tac-etc
- Add tri-Ace Codec [Star Ocean 3 (PS2), Valkyrie Profile 2 (PS2)] - Add HCA key [Sakura Kakumei (iOS/Android)]
This commit is contained in:
commit
2081a5b322
54
README.md
54
README.md
@ -1,7 +1,21 @@
|
||||
# vgmstream
|
||||
|
||||
This is vgmstream, a library for playing streamed (pre-recorded) audio
|
||||
from video games.
|
||||
This is vgmstream, a library for playing streamed (pre-recorded) audio from
|
||||
video games.
|
||||
|
||||
Some of vgmstream's features:
|
||||
- hundreds of video game music formats and codecs, from typical game engine files to
|
||||
obscure single-game codecs, aiming for high accuracy and compatibility.
|
||||
- support for looped BGM, using file's internal metadata for smooth transitions,
|
||||
with accurate sample counts
|
||||
- subsongs, playing internal songs separatedly
|
||||
- encryption keys, audio split in multiple files, internal stream names, and many
|
||||
other unusual cases found in game audio
|
||||
- TXTH function, to support extra formats (including raw audio in many forms)
|
||||
- TXTP function, for real-time and per-file config (like forced looping, removing
|
||||
channels, playing certain subsong, or fusing together multiple files as a single one)
|
||||
- simple external tagging via .m3u files
|
||||
- plugins available for various common players and O.S.
|
||||
|
||||
There are multiple end-user bits:
|
||||
- a command line decoder called "test.exe/vgmstream-cli"
|
||||
@ -11,22 +25,23 @@ There are multiple end-user bits:
|
||||
- an Audacious plugin called "libvgmstream"
|
||||
- a command line player called "vgmstream123"
|
||||
|
||||
Help and newest builds can be found here: https://www.hcs64.com/
|
||||
Help can be found here: https://www.hcs64.com/
|
||||
|
||||
Latest development is usually here: https://github.com/losnoco/vgmstream/
|
||||
|
||||
Latest releases are here: https://github.com/losnoco/vgmstream/releases
|
||||
Latest development is usually here: https://github.com/vgmstream/vgmstream/
|
||||
|
||||
Automated builds with the latest changes: https://vgmstream.org/downloads
|
||||
|
||||
You can find further info about other details in https://github.com/losnoco/vgmstream/tree/master/doc
|
||||
Releases are here: https://github.com/vgmstream/vgmstream/releases
|
||||
|
||||
You can find further info about other details in https://github.com/vgmstream/vgmstream/tree/master/doc
|
||||
|
||||
|
||||
## Needed extra files (for Windows)
|
||||
Support for some codecs (Ogg Vorbis, MPEG audio, etc) is done with external
|
||||
libraries, so you will need to have certain DLL files.
|
||||
|
||||
In the case of the foobar2000 component they are all bundled for convenience,
|
||||
or you can get them here: https://github.com/losnoco/vgmstream/tree/master/ext_libs
|
||||
or you can get them here: https://github.com/vgmstream/vgmstream/tree/master/ext_libs
|
||||
(bundled here: https://f.losno.co/vgmstream-win32-deps.zip, may not be latest).
|
||||
|
||||
Put the following files somewhere Windows can find them:
|
||||
@ -155,7 +170,7 @@ those files automatically into the playlist. For others without support, you can
|
||||
multiple .txtp (explained below) to select one of the subsongs (like `bgm.sxd#10.txtp`).
|
||||
|
||||
You can use this python script to autogenerate one `.txtp` per subsong:
|
||||
https://github.com/losnoco/vgmstream/tree/master/cli/txtp_maker.py
|
||||
https://github.com/vgmstream/vgmstream/tree/master/cli/txtp_maker.py
|
||||
Put in the same dir as test.exe/vgmstream_cli, then to drag-and-drop files with
|
||||
subsongs to `txtp_maker.py` (it has CLI options to control output too).
|
||||
|
||||
@ -321,20 +336,24 @@ The key file can be `.(ext)key` (for the whole folder), or `(name).(ext)key"
|
||||
### Artificial files
|
||||
In some cases a file only has raw data, while important header info (codec type,
|
||||
sample rate, channels, etc) is stored in the .exe or other hard to locate places.
|
||||
Or maybe the file plays normally, but has many layers at once that are silenced
|
||||
dynamically during gameplay, or looping metadata is stored externally.
|
||||
|
||||
Those can be played using an artificial header with info vgmstream needs.
|
||||
Cases like those can be supported using an artificial files with info vgmstream
|
||||
needs.
|
||||
|
||||
**GENH**: a byte header placed right before the original data, modyfing it.
|
||||
The resulting file must be `(name).genh`. Contains static header data.
|
||||
Programs like VGMToolbox can help to create *GENH*.
|
||||
Programs like VGMToolbox can help to create *GENH*, but consider using *TXTH*
|
||||
instead.
|
||||
|
||||
**TXTH**: a text header placed in an external file. The TXTH must be named
|
||||
`.txth` or `.(ext).txth` (for the whole folder), or `(name.ext).txth` (for a
|
||||
single file). Contains dynamic text commands to read data from the original
|
||||
file, or static values.
|
||||
file, or static values. This allows vgmstream to play unsupported formats.
|
||||
|
||||
*TXTH* is recommended over *GENH* as it's far easier to create and has many
|
||||
more functions.
|
||||
more functions, plus doesn't modify original data.
|
||||
|
||||
For files that already play, sometimes they are used by the game in various
|
||||
complex and non-standard ways, like playing multiple small songs as a single
|
||||
@ -630,7 +649,7 @@ are used in few games.
|
||||
- Ubisoft 4/6-bit ADPCM
|
||||
- Tiger Game.com ADPCM
|
||||
- LucasArts iMUSE VBR ADPCM
|
||||
- CompressWave Huffman ADPCM
|
||||
- CompressWave (CWav) Huffman ADPCM
|
||||
- SDX2 2:1 Squareroot-Delta-Exact compression DPCM
|
||||
- CBD2 2:1 Cuberoot-Delta-Exact compression DPCM
|
||||
- Activision EXAKT SASSC DPCM
|
||||
@ -640,10 +659,11 @@ are used in few games.
|
||||
- Electronic Arts MicroTalk a.k.a. UTK or UMT
|
||||
- Relic Codec
|
||||
- CRI HCA
|
||||
- tri-Ace PS2 Codec
|
||||
- Xiph Vorbis (Ogg, FSB5, Wwise, OGL, Silicon Knights)
|
||||
- MPEG MP1/2/3 (standard, AHX, XVAG, FSB, AWC, P3D, etc)
|
||||
- ITU-T G.722.1 annex C (Polycom Siren 14)
|
||||
- ITU-T G.719 annex B (Polycom Siren 22)
|
||||
- MPEG MP1/2/3 (standard, AHX, XVAG, FSB, AWC, P3D, EA, etc)
|
||||
- ITU-T G.722.1 annex C a.k.a. Polycom Siren 14 (Namco)
|
||||
- ITU-T G.719 annex B a.k.a. Polycom Siren 22
|
||||
- Electronic Arts EASpeex
|
||||
- Electronic Arts EALayer3
|
||||
- Electronic Arts EA-XMA
|
||||
|
@ -317,6 +317,16 @@ clHCA_stInfo* hca_get_info(hca_codec_data* data);
|
||||
STREAMFILE* hca_get_streamfile(hca_codec_data* data);
|
||||
|
||||
|
||||
/* tac_decoder */
|
||||
typedef struct tac_codec_data tac_codec_data;
|
||||
|
||||
tac_codec_data* init_tac(STREAMFILE* sf);
|
||||
void decode_tac(VGMSTREAM* vgmstream, sample_t* outbuf, int32_t samples_to_do);
|
||||
void reset_tac(tac_codec_data* data);
|
||||
void seek_tac(tac_codec_data* data, int32_t num_sample);
|
||||
void free_tac(tac_codec_data* data);
|
||||
|
||||
|
||||
#ifdef VGM_USE_VORBIS
|
||||
/* ogg_vorbis_decoder */
|
||||
typedef struct ogg_vorbis_codec_data ogg_vorbis_codec_data;
|
||||
|
185
src/coding/tac_decoder.c
Normal file
185
src/coding/tac_decoder.c
Normal file
@ -0,0 +1,185 @@
|
||||
#include "coding.h"
|
||||
#include "coding_utils_samples.h"
|
||||
|
||||
#include "tac_decoder_lib.h"
|
||||
|
||||
|
||||
/* opaque struct */
|
||||
struct tac_codec_data {
|
||||
/* config */
|
||||
int channels;
|
||||
int samples_discard;
|
||||
int encoder_delay;
|
||||
|
||||
uint8_t buf[TAC_BLOCK_SIZE];
|
||||
int read_block;
|
||||
off_t offset;
|
||||
|
||||
int16_t* samples;
|
||||
int frame_samples;
|
||||
|
||||
/* frame state */
|
||||
s16buf_t sbuf;
|
||||
|
||||
void* handle;
|
||||
};
|
||||
|
||||
|
||||
/* raw SPEEX */
|
||||
tac_codec_data* init_tac(STREAMFILE* sf) {
|
||||
tac_codec_data* data = NULL;
|
||||
int bytes;
|
||||
|
||||
|
||||
data = calloc(1, sizeof(tac_codec_data));
|
||||
if (!data) goto fail;
|
||||
|
||||
bytes = read_streamfile(data->buf, 0x00, sizeof(data->buf), sf);
|
||||
data->handle = tac_init(data->buf, bytes);
|
||||
if (!data->handle) goto fail;
|
||||
|
||||
data->read_block = 0; /* ok to use current block */
|
||||
data->offset = bytes;
|
||||
data->channels = TAC_CHANNELS;
|
||||
data->frame_samples = TAC_FRAME_SAMPLES;
|
||||
|
||||
data->encoder_delay = 0;
|
||||
data->samples_discard = data->encoder_delay;
|
||||
|
||||
data->samples = malloc(data->channels * data->frame_samples * sizeof(int16_t));
|
||||
if (!data->samples) goto fail;
|
||||
|
||||
return data;
|
||||
fail:
|
||||
free_tac(data);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static int decode_frame(tac_codec_data* data) {
|
||||
int err;
|
||||
|
||||
data->sbuf.samples = data->samples;
|
||||
data->sbuf.channels = 2;
|
||||
data->sbuf.filled = 0;
|
||||
|
||||
err = tac_decode_frame(data->handle, data->buf);
|
||||
|
||||
if (err == TAC_PROCESS_NEXT_BLOCK) {
|
||||
data->read_block = 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (err == TAC_PROCESS_DONE) {
|
||||
goto fail; /* shouldn't reach this */
|
||||
}
|
||||
|
||||
if (err != TAC_PROCESS_OK) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
|
||||
tac_get_samples_pcm16(data->handle, data->sbuf.samples);
|
||||
data->sbuf.filled = data->frame_samples;
|
||||
|
||||
return 1;
|
||||
fail:
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int read_frame(tac_codec_data* data, STREAMFILE* sf) {
|
||||
|
||||
/* new block must be read only when signaled by lib */
|
||||
if (data->read_block) {
|
||||
int bytes = read_streamfile(data->buf, data->offset, sizeof(data->buf), sf);
|
||||
data->offset += bytes;
|
||||
data->read_block = 0;
|
||||
if (bytes <= 0) goto fail; /* can read less that buf near EOF */
|
||||
}
|
||||
|
||||
return 1;
|
||||
fail:
|
||||
return 0;
|
||||
}
|
||||
|
||||
void decode_tac(VGMSTREAM* vgmstream, sample_t* outbuf, int32_t samples_to_do) {
|
||||
VGMSTREAMCHANNEL* stream = &vgmstream->ch[0];
|
||||
tac_codec_data* data = vgmstream->codec_data;
|
||||
int ok;
|
||||
|
||||
|
||||
while (samples_to_do > 0) {
|
||||
s16buf_t* sbuf = &data->sbuf;
|
||||
|
||||
if (sbuf->filled <= 0) {
|
||||
ok = read_frame(data, stream->streamfile);
|
||||
if (!ok) goto fail;
|
||||
|
||||
ok = decode_frame(data);
|
||||
if (!ok) goto fail;
|
||||
}
|
||||
|
||||
if (data->samples_discard)
|
||||
s16buf_discard(&outbuf, sbuf, &data->samples_discard);
|
||||
else
|
||||
s16buf_consume(&outbuf, sbuf, &samples_to_do);
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
fail:
|
||||
/* on error just put some 0 samples */
|
||||
VGM_LOG("TAC: decode fail at %x, missing %i samples\n", (uint32_t)data->offset, samples_to_do);
|
||||
s16buf_silence(&outbuf, &samples_to_do, data->channels);
|
||||
}
|
||||
|
||||
|
||||
void reset_tac(tac_codec_data* data) {
|
||||
if (!data) return;
|
||||
|
||||
tac_reset(data->handle);
|
||||
|
||||
data->read_block = 1;
|
||||
data->sbuf.filled = 0;
|
||||
data->samples_discard = data->encoder_delay;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void seek_tac(tac_codec_data* data, int32_t num_sample) {
|
||||
int32_t loop_sample;
|
||||
const tac_header_t* hdr;
|
||||
|
||||
if (!data)
|
||||
return;
|
||||
|
||||
hdr = tac_get_header(data->handle);
|
||||
|
||||
loop_sample = hdr->loop_frame * TAC_FRAME_SAMPLES + hdr->loop_discard;
|
||||
if (loop_sample == num_sample) {
|
||||
/* simulates original looping (that wouldn't clean codec internals) */
|
||||
tac_set_loop(data->handle);
|
||||
|
||||
data->samples_discard = hdr->loop_discard;
|
||||
data->offset = hdr->loop_offset;
|
||||
data->read_block = 1;
|
||||
data->sbuf.filled = 0;
|
||||
}
|
||||
else {
|
||||
tac_reset(data->handle);
|
||||
|
||||
data->samples_discard = num_sample;
|
||||
data->offset = 0;
|
||||
data->read_block = 1;
|
||||
data->sbuf.filled = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void free_tac(tac_codec_data* data) {
|
||||
if (!data)
|
||||
return;
|
||||
|
||||
tac_free(data->handle);
|
||||
free(data->samples);
|
||||
free(data);
|
||||
}
|
1225
src/coding/tac_decoder_lib.c
Normal file
1225
src/coding/tac_decoder_lib.c
Normal file
File diff suppressed because it is too large
Load Diff
55
src/coding/tac_decoder_lib.h
Normal file
55
src/coding/tac_decoder_lib.h
Normal file
@ -0,0 +1,55 @@
|
||||
#ifndef _TAC_DECODER_LIB_H_
|
||||
#define _TAC_DECODER_LIB_H_
|
||||
|
||||
/* tri-Ace Codec (TAC) lib, found in PS2 games */
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define TAC_SAMPLE_RATE 48000
|
||||
#define TAC_CHANNELS 2
|
||||
#define TAC_FRAME_SAMPLES 1024
|
||||
#define TAC_BLOCK_SIZE 0x4E000 /* size of a single block with N VBR frames */
|
||||
|
||||
#define TAC_PROCESS_OK 0 /* frame decoded correctly */
|
||||
#define TAC_PROCESS_NEXT_BLOCK 1 /* must pass next block (didn't decode) */
|
||||
#define TAC_PROCESS_DONE 2 /* no more frames to do (didn't decode) */
|
||||
#define TAC_PROCESS_HEADER_ERROR -1 /* file doesn't match expected header */
|
||||
#define TAC_PROCESS_ERROR_SIZE -2 /* buffer is smaller than needed */
|
||||
#define TAC_PROCESS_ERROR_ID -3 /* expected frame id mismatch */
|
||||
#define TAC_PROCESS_ERROR_CRC -4 /* expected frame crc mismatch */
|
||||
#define TAC_PROCESS_ERROR_HUFFMAN -5 /* expected huffman count mismatch */
|
||||
|
||||
typedef struct tac_handle_t tac_handle_t;
|
||||
|
||||
typedef struct {
|
||||
/* 0x20 header config */
|
||||
uint32_t huffman_offset; /* setup */
|
||||
uint32_t unknown1; /* ignored? may be CDVD stuff (divided/multiplied during PS2 process), not file size related */
|
||||
uint16_t loop_frame; /* aligned to block stard */
|
||||
uint16_t loop_discard; /* assumed */
|
||||
uint16_t frame_count; /* number of valid frames ("block end" frame not included) */
|
||||
uint16_t frame_discard; /* assumed */
|
||||
uint32_t loop_offset; /* file size if not looped */
|
||||
uint32_t file_size; /* actual file size can be a bit smaller if last block is truncated */
|
||||
uint32_t unknown2; /* usually 0 and rarely 1 (R channel has less data, joint stereo mode?) */
|
||||
uint32_t empty; /* null? */
|
||||
} tac_header_t;
|
||||
|
||||
|
||||
/* inits codec with data from at least one block */
|
||||
tac_handle_t* tac_init(const uint8_t* buf, int buf_size);
|
||||
|
||||
const tac_header_t* tac_get_header(tac_handle_t* handle);
|
||||
|
||||
void tac_reset(tac_handle_t* handle);
|
||||
|
||||
void tac_free(tac_handle_t* handle);
|
||||
|
||||
/* decodes a frame from current block (of TAC_BLOCK_SIZE), returning TAC_PROCESS_* codes */
|
||||
int tac_decode_frame(tac_handle_t* handle, const uint8_t* block);
|
||||
|
||||
void tac_get_samples_pcm16(tac_handle_t* handle, int16_t* dst);
|
||||
|
||||
void tac_set_loop(tac_handle_t* handle);
|
||||
|
||||
#endif /* _TAC_DECODER_LIB_H_ */
|
1002
src/coding/tac_decoder_lib_data.h
Normal file
1002
src/coding/tac_decoder_lib_data.h
Normal file
File diff suppressed because it is too large
Load Diff
377
src/coding/tac_decoder_lib_ops.h
Normal file
377
src/coding/tac_decoder_lib_ops.h
Normal file
@ -0,0 +1,377 @@
|
||||
#ifndef _TAC_DECODER_LIB_OPS_H_
|
||||
#define _TAC_DECODER_LIB_OPS_H_
|
||||
|
||||
#include <math.h>
|
||||
#include "tac_decoder_lib_ops.h"
|
||||
|
||||
/* The following ops are similar to VU1's ops, but not quite the same. For example VU1 has special op
|
||||
* registers like the ACC, and updates zero/neg/etc flags per op (plus added here a few helper ops).
|
||||
* Main reason to use them vs doing standard +*-/ in code is allowing to simulate PS2 floats.
|
||||
* See Nisto's decoder for actual emulation. */
|
||||
|
||||
|
||||
/* PS2 floats are slightly different vs IEEE 754 floats:
|
||||
* - NaN and Inf (exp 255) don't exist on the PS2, meaning it has a bigger range of floats
|
||||
* - denormals (exp 0) don't exist either, and ops truncate to 0
|
||||
* - rounding on PS2 always rounds towards zero
|
||||
* The code below (partially) simulates this, but for audio it only means +-1 differences,
|
||||
* plus we can't fully emulate exact behaviour, so it's disabled for performance
|
||||
* (function call is optimized out by compiler). */
|
||||
#define TAC_ENABLE_PS2_FLOATS 0
|
||||
|
||||
static inline void UPDATE_FLOATS(uint8_t dest, REG_VF *vf) {
|
||||
#if TAC_ENABLE_PS2_FLOATS
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
int shift = 3 - i;
|
||||
if (dest & (1 << shift)) {
|
||||
|
||||
if (vf->F[i] == 0.0) {
|
||||
uint32_t v = vf->UL[i];
|
||||
int exp = (v >> 23) & 0xff;
|
||||
uint32_t s = v & 0x80000000;
|
||||
|
||||
switch (exp) {
|
||||
case 0:
|
||||
vf->UL[i] = s;
|
||||
break;
|
||||
case 255:
|
||||
vf->UL[i] = s|0x7f7fffff; /* max allowed */
|
||||
break;
|
||||
default: /* standard */
|
||||
vf->UL[i] = v;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void _DIV_INTERNAL(REG_VF *fd, const REG_VF *fs, const REG_VF *ft, int from) {
|
||||
float dividend = fs->F[from];
|
||||
float divisor = ft->F[from];
|
||||
|
||||
#if TAC_ENABLE_PS2_FLOATS
|
||||
if (divisor == 0.0) {
|
||||
if ((ft->UL[from] & 0x80000000) != (0x80000000 & fs->UL[from])) {
|
||||
fd->UL[from] = 0xFF7FFFFF;
|
||||
}
|
||||
else {
|
||||
fd->UL[from] = 0x7F7FFFFF;
|
||||
}
|
||||
|
||||
}
|
||||
else {
|
||||
fd->F[from] = dividend / divisor;
|
||||
}
|
||||
#else
|
||||
fd->F[from] = dividend / divisor;
|
||||
#endif
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static inline void DIV(uint8_t dest, REG_VF *fd, const REG_VF *fs, const REG_VF *ft) {
|
||||
if (dest & _x___) _DIV_INTERNAL(fd, fs, ft, 0);
|
||||
if (dest & __y__) _DIV_INTERNAL(fd, fs, ft, 1);
|
||||
if (dest & ___z_) _DIV_INTERNAL(fd, fs, ft, 2);
|
||||
if (dest & ____w) _DIV_INTERNAL(fd, fs, ft, 3);
|
||||
UPDATE_FLOATS(dest, fd);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static inline void ADD(uint8_t dest, REG_VF *fd, const REG_VF *fs, const REG_VF *ft) {
|
||||
if (dest & _x___) fd->f.x = fs->f.x + ft->f.x;
|
||||
if (dest & __y__) fd->f.y = fs->f.y + ft->f.y;
|
||||
if (dest & ___z_) fd->f.z = fs->f.z + ft->f.z;
|
||||
if (dest & ____w) fd->f.w = fs->f.w + ft->f.w;
|
||||
UPDATE_FLOATS(dest, fd);
|
||||
}
|
||||
|
||||
static inline void ADDx(uint8_t dest, REG_VF *fd, const REG_VF *fs, const REG_VF *ft) {
|
||||
if (dest & _x___) fd->f.x = fs->f.x + ft->f.x;
|
||||
if (dest & __y__) fd->f.y = fs->f.y + ft->f.x;
|
||||
if (dest & ___z_) fd->f.z = fs->f.z + ft->f.x;
|
||||
if (dest & ____w) fd->f.w = fs->f.w + ft->f.x;
|
||||
UPDATE_FLOATS(dest, fd);
|
||||
}
|
||||
|
||||
static inline void ADDy(uint8_t dest, REG_VF *fd, const REG_VF *fs, const REG_VF *ft) {
|
||||
if (dest & _x___) fd->f.x = fs->f.x + ft->f.y;
|
||||
if (dest & __y__) fd->f.y = fs->f.y + ft->f.y;
|
||||
if (dest & ___z_) fd->f.z = fs->f.z + ft->f.y;
|
||||
if (dest & ____w) fd->f.w = fs->f.w + ft->f.y;
|
||||
UPDATE_FLOATS(dest, fd);
|
||||
}
|
||||
|
||||
static inline void ADDz(uint8_t dest, REG_VF *fd, const REG_VF *fs, const REG_VF *ft) {
|
||||
if (dest & _x___) fd->f.x = fs->f.x + ft->f.z;
|
||||
if (dest & __y__) fd->f.y = fs->f.y + ft->f.z;
|
||||
if (dest & ___z_) fd->f.z = fs->f.z + ft->f.z;
|
||||
if (dest & ____w) fd->f.w = fs->f.w + ft->f.z;
|
||||
UPDATE_FLOATS(dest, fd);
|
||||
}
|
||||
|
||||
static inline void ADDw(uint8_t dest, REG_VF *fd, const REG_VF *fs, const REG_VF *ft) {
|
||||
if (dest & _x___) fd->f.x = fs->f.x + ft->f.w;
|
||||
if (dest & __y__) fd->f.y = fs->f.y + ft->f.w;
|
||||
if (dest & ___z_) fd->f.z = fs->f.z + ft->f.w;
|
||||
if (dest & ____w) fd->f.w = fs->f.w + ft->f.w;
|
||||
UPDATE_FLOATS(dest, fd);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static inline void SUB(uint8_t dest, REG_VF *fd, const REG_VF *fs, const REG_VF *ft) {
|
||||
if (dest & _x___) fd->f.x = fs->f.x - ft->f.x;
|
||||
if (dest & __y__) fd->f.y = fs->f.y - ft->f.y;
|
||||
if (dest & ___z_) fd->f.z = fs->f.z - ft->f.z;
|
||||
if (dest & ____w) fd->f.w = fs->f.w - ft->f.w;
|
||||
UPDATE_FLOATS(dest, fd);
|
||||
}
|
||||
|
||||
static inline void SUBx(uint8_t dest, REG_VF *fd, const REG_VF *fs, const REG_VF *ft) {
|
||||
if (dest & _x___) fd->f.x = fs->f.x - ft->f.x;
|
||||
if (dest & __y__) fd->f.y = fs->f.y - ft->f.x;
|
||||
if (dest & ___z_) fd->f.z = fs->f.z - ft->f.x;
|
||||
if (dest & ____w) fd->f.w = fs->f.w - ft->f.x;
|
||||
UPDATE_FLOATS(dest, fd);
|
||||
}
|
||||
|
||||
static inline void SUBy(uint8_t dest, REG_VF *fd, const REG_VF *fs, const REG_VF *ft) {
|
||||
if (dest & _x___) fd->f.x = fs->f.x - ft->f.y;
|
||||
if (dest & __y__) fd->f.y = fs->f.y - ft->f.y;
|
||||
if (dest & ___z_) fd->f.z = fs->f.z - ft->f.y;
|
||||
if (dest & ____w) fd->f.w = fs->f.w - ft->f.y;
|
||||
UPDATE_FLOATS(dest, fd);
|
||||
}
|
||||
|
||||
static inline void SUBz(uint8_t dest, REG_VF *fd, const REG_VF *fs, const REG_VF *ft) {
|
||||
if (dest & _x___) fd->f.x = fs->f.x - ft->f.z;
|
||||
if (dest & __y__) fd->f.y = fs->f.y - ft->f.z;
|
||||
if (dest & ___z_) fd->f.z = fs->f.z - ft->f.z;
|
||||
if (dest & ____w) fd->f.w = fs->f.w - ft->f.z;
|
||||
UPDATE_FLOATS(dest, fd);
|
||||
}
|
||||
|
||||
static inline void SUBw(uint8_t dest, REG_VF *fd, const REG_VF *fs, const REG_VF *ft) {
|
||||
if (dest & _x___) fd->f.x = fs->f.x - ft->f.w;
|
||||
if (dest & __y__) fd->f.y = fs->f.y - ft->f.w;
|
||||
if (dest & ___z_) fd->f.z = fs->f.z - ft->f.w;
|
||||
if (dest & ____w) fd->f.w = fs->f.w - ft->f.w;
|
||||
UPDATE_FLOATS(dest, fd);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static inline void MUL(uint8_t dest, REG_VF *fd, const REG_VF *fs, const REG_VF *ft) {
|
||||
if (dest & _x___) fd->f.x = fs->f.x * ft->f.x;
|
||||
if (dest & __y__) fd->f.y = fs->f.y * ft->f.y;
|
||||
if (dest & ___z_) fd->f.z = fs->f.z * ft->f.z;
|
||||
if (dest & ____w) fd->f.w = fs->f.w * ft->f.w;
|
||||
UPDATE_FLOATS(dest, fd);
|
||||
}
|
||||
|
||||
static inline void MULx(uint8_t dest, REG_VF *fd, const REG_VF *fs, const REG_VF *ft) {
|
||||
if (dest & _x___) fd->f.x = fs->f.x * ft->f.x;
|
||||
if (dest & __y__) fd->f.y = fs->f.y * ft->f.x;
|
||||
if (dest & ___z_) fd->f.z = fs->f.z * ft->f.x;
|
||||
if (dest & ____w) fd->f.w = fs->f.w * ft->f.x;
|
||||
UPDATE_FLOATS(dest, fd);
|
||||
}
|
||||
|
||||
static inline void MULy(uint8_t dest, REG_VF *fd, const REG_VF *fs, const REG_VF *ft) {
|
||||
if (dest & _x___) fd->f.x = fs->f.x * ft->f.y;
|
||||
if (dest & __y__) fd->f.y = fs->f.y * ft->f.y;
|
||||
if (dest & ___z_) fd->f.z = fs->f.z * ft->f.y;
|
||||
if (dest & ____w) fd->f.w = fs->f.w * ft->f.y;
|
||||
UPDATE_FLOATS(dest, fd);
|
||||
}
|
||||
|
||||
static inline void MULz(uint8_t dest, REG_VF *fd, const REG_VF *fs, const REG_VF *ft) {
|
||||
if (dest & _x___) fd->f.x = fs->f.x * ft->f.z;
|
||||
if (dest & __y__) fd->f.y = fs->f.y * ft->f.z;
|
||||
if (dest & ___z_) fd->f.z = fs->f.z * ft->f.z;
|
||||
if (dest & ____w) fd->f.w = fs->f.w * ft->f.z;
|
||||
UPDATE_FLOATS(dest, fd);
|
||||
}
|
||||
|
||||
static inline void MULw(uint8_t dest, REG_VF *fd, const REG_VF *fs, const REG_VF *ft) {
|
||||
if (dest & _x___) fd->f.x = fs->f.x * ft->f.w;
|
||||
if (dest & __y__) fd->f.y = fs->f.y * ft->f.w;
|
||||
if (dest & ___z_) fd->f.z = fs->f.z * ft->f.w;
|
||||
if (dest & ____w) fd->f.w = fs->f.w * ft->f.w;
|
||||
UPDATE_FLOATS(dest, fd);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static inline void MADD(uint8_t dest, REG_VF *fd, const REG_VF *fs, const REG_VF *ft) {
|
||||
if (dest & _x___) fd->f.x = fd->f.x + (fs->f.x * ft->f.x);
|
||||
if (dest & __y__) fd->f.y = fd->f.y + (fs->f.y * ft->f.y);
|
||||
if (dest & ___z_) fd->f.z = fd->f.z + (fs->f.z * ft->f.z);
|
||||
if (dest & ____w) fd->f.w = fd->f.w + (fs->f.w * ft->f.w);
|
||||
UPDATE_FLOATS(dest, fd);
|
||||
}
|
||||
|
||||
static inline void MADDx(uint8_t dest, REG_VF *fd, const REG_VF *fs, const REG_VF *ft) {
|
||||
if (dest & _x___) fd->f.x = fd->f.x + (fs->f.x * ft->f.x);
|
||||
if (dest & __y__) fd->f.y = fd->f.y + (fs->f.y * ft->f.x);
|
||||
if (dest & ___z_) fd->f.z = fd->f.z + (fs->f.z * ft->f.x);
|
||||
if (dest & ____w) fd->f.w = fd->f.w + (fs->f.w * ft->f.x);
|
||||
UPDATE_FLOATS(dest, fd);
|
||||
}
|
||||
|
||||
static inline void MADDy(uint8_t dest, REG_VF *fd, const REG_VF *fs, const REG_VF *ft) {
|
||||
if (dest & _x___) fd->f.x = fd->f.x + (fs->f.x * ft->f.y);
|
||||
if (dest & __y__) fd->f.y = fd->f.y + (fs->f.y * ft->f.y);
|
||||
if (dest & ___z_) fd->f.z = fd->f.z + (fs->f.z * ft->f.y);
|
||||
if (dest & ____w) fd->f.w = fd->f.w + (fs->f.w * ft->f.y);
|
||||
UPDATE_FLOATS(dest, fd);
|
||||
}
|
||||
|
||||
static inline void MADDz(uint8_t dest, REG_VF *fd, const REG_VF *fs, const REG_VF *ft) {
|
||||
if (dest & _x___) fd->f.x = fd->f.x + (fs->f.x * ft->f.z);
|
||||
if (dest & __y__) fd->f.y = fd->f.y + (fs->f.y * ft->f.z);
|
||||
if (dest & ___z_) fd->f.z = fd->f.z + (fs->f.z * ft->f.z);
|
||||
if (dest & ____w) fd->f.w = fd->f.w + (fs->f.w * ft->f.z);
|
||||
UPDATE_FLOATS(dest, fd);
|
||||
}
|
||||
|
||||
static inline void MADDw(uint8_t dest, REG_VF *fd, const REG_VF *fs, const REG_VF *ft) {
|
||||
if (dest & _x___) fd->f.x = fd->f.x + (fs->f.x * ft->f.w);
|
||||
if (dest & __y__) fd->f.y = fd->f.y + (fs->f.y * ft->f.w);
|
||||
if (dest & ___z_) fd->f.z = fd->f.z + (fs->f.z * ft->f.w);
|
||||
if (dest & ____w) fd->f.w = fd->f.w + (fs->f.w * ft->f.w);
|
||||
UPDATE_FLOATS(dest, fd);
|
||||
}
|
||||
|
||||
static inline void MSUBx(uint8_t dest, REG_VF *fd, const REG_VF *fs, const REG_VF *ft) {
|
||||
if (dest & _x___) fd->f.x = fd->f.x - (fs->f.x * ft->f.x);
|
||||
if (dest & __y__) fd->f.y = fd->f.y - (fs->f.y * ft->f.x);
|
||||
if (dest & ___z_) fd->f.z = fd->f.z - (fs->f.z * ft->f.x);
|
||||
if (dest & ____w) fd->f.w = fd->f.w - (fs->f.w * ft->f.x);
|
||||
UPDATE_FLOATS(dest, fd);
|
||||
}
|
||||
|
||||
static inline void MSUBy(uint8_t dest, REG_VF *fd, const REG_VF *fs, const REG_VF *ft) {
|
||||
if (dest & _x___) fd->f.x = fd->f.x - (fs->f.x * ft->f.y);
|
||||
if (dest & __y__) fd->f.y = fd->f.y - (fs->f.y * ft->f.y);
|
||||
if (dest & ___z_) fd->f.z = fd->f.z - (fs->f.z * ft->f.y);
|
||||
if (dest & ____w) fd->f.w = fd->f.w - (fs->f.w * ft->f.y);
|
||||
UPDATE_FLOATS(dest, fd);
|
||||
}
|
||||
|
||||
static inline void MSUBz(uint8_t dest, REG_VF *fd, const REG_VF *fs, const REG_VF *ft) {
|
||||
if (dest & _x___) fd->f.x = fd->f.x - (fs->f.x * ft->f.z);
|
||||
if (dest & __y__) fd->f.y = fd->f.y - (fs->f.y * ft->f.z);
|
||||
if (dest & ___z_) fd->f.z = fd->f.z - (fs->f.z * ft->f.z);
|
||||
if (dest & ____w) fd->f.w = fd->f.w - (fs->f.w * ft->f.z);
|
||||
UPDATE_FLOATS(dest, fd);
|
||||
}
|
||||
|
||||
static inline void MSUBw(uint8_t dest, REG_VF *fd, const REG_VF *fs, const REG_VF *ft) {
|
||||
if (dest & _x___) fd->f.x = fd->f.x - (fs->f.x * ft->f.w);
|
||||
if (dest & __y__) fd->f.y = fd->f.y - (fs->f.y * ft->f.w);
|
||||
if (dest & ___z_) fd->f.z = fd->f.z - (fs->f.z * ft->f.w);
|
||||
if (dest & ____w) fd->f.w = fd->f.w - (fs->f.w * ft->f.w);
|
||||
UPDATE_FLOATS(dest, fd);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static inline void FMUL(uint8_t dest, REG_VF *fd, const REG_VF *fs, const float I_F) {
|
||||
if (dest & _x___) fd->f.x = fs->f.x * I_F;
|
||||
if (dest & __y__) fd->f.y = fs->f.y * I_F;
|
||||
if (dest & ___z_) fd->f.z = fs->f.z * I_F;
|
||||
if (dest & ____w) fd->f.w = fs->f.w * I_F;
|
||||
UPDATE_FLOATS(dest, fd);
|
||||
}
|
||||
|
||||
static inline void FMULf(uint8_t dest, REG_VF *fd, const float fs) {
|
||||
if (dest & _x___) fd->f.x = fd->f.x * fs;
|
||||
if (dest & __y__) fd->f.y = fd->f.y * fs;
|
||||
if (dest & ___z_) fd->f.z = fd->f.z * fs;
|
||||
if (dest & ____w) fd->f.w = fd->f.w * fs;
|
||||
UPDATE_FLOATS(dest, fd);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static inline void ABS(uint8_t dest, REG_VF *ft, const REG_VF *fs) {
|
||||
if (dest & _x___) ft->f.x = fabsf(fs->f.x);
|
||||
if (dest & __y__) ft->f.y = fabsf(fs->f.y);
|
||||
if (dest & ___z_) ft->f.z = fabsf(fs->f.z);
|
||||
if (dest & ____w) ft->f.w = fabsf(fs->f.w);
|
||||
}
|
||||
|
||||
static inline void FTOI0(uint8_t dest, REG_VF *ft, const REG_VF *fs) {
|
||||
if (dest & _x___) ft->SL[0] = (int32_t)fs->f.x;
|
||||
if (dest & __y__) ft->SL[1] = (int32_t)fs->f.y;
|
||||
if (dest & ___z_) ft->SL[2] = (int32_t)fs->f.z;
|
||||
if (dest & ____w) ft->SL[3] = (int32_t)fs->f.w;
|
||||
}
|
||||
|
||||
static inline void ITOF0(uint8_t dest, REG_VF *ft, const REG_VF *fs) {
|
||||
if (dest & _x___) ft->f.x = (float)fs->SL[0];
|
||||
if (dest & __y__) ft->f.y = (float)fs->SL[1];
|
||||
if (dest & ___z_) ft->f.z = (float)fs->SL[2];
|
||||
if (dest & ____w) ft->f.w = (float)fs->SL[3];
|
||||
}
|
||||
|
||||
static inline void MR32(uint8_t dest, REG_VF *ft, const REG_VF *fs) {
|
||||
float x = fs->f.x;
|
||||
if (dest & _x___) ft->f.x = fs->f.y;
|
||||
if (dest & __y__) ft->f.y = fs->f.z;
|
||||
if (dest & ___z_) ft->f.z = fs->f.w;
|
||||
if (dest & ____w) ft->f.w = x;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static inline void LOAD(uint8_t dest, REG_VF *ft, REG_VF* src, int pos) {
|
||||
if (dest & _x___) ft->f.x = src[pos].f.x;
|
||||
if (dest & __y__) ft->f.y = src[pos].f.y;
|
||||
if (dest & ___z_) ft->f.z = src[pos].f.z;
|
||||
if (dest & ____w) ft->f.w = src[pos].f.w;
|
||||
}
|
||||
|
||||
static inline void STORE(uint8_t dest, REG_VF* dst, const REG_VF *fs, int pos) {
|
||||
if (dest & _x___) dst[pos].f.x = fs->f.x;
|
||||
if (dest & __y__) dst[pos].f.y = fs->f.y;
|
||||
if (dest & ___z_) dst[pos].f.z = fs->f.z;
|
||||
if (dest & ____w) dst[pos].f.w = fs->f.w;
|
||||
}
|
||||
|
||||
static inline void MOVE(uint8_t dest, REG_VF *fd, const REG_VF *fs) {
|
||||
if (dest & _x___) fd->f.x = fs->f.x;
|
||||
if (dest & __y__) fd->f.y = fs->f.y;
|
||||
if (dest & ___z_) fd->f.z = fs->f.z;
|
||||
if (dest & ____w) fd->f.w = fs->f.w;
|
||||
}
|
||||
|
||||
static inline void MOVEx(uint8_t dest, REG_VF *fd, const REG_VF *fs) {
|
||||
if (dest & _x___) fd->f.x = fs->f.x;
|
||||
if (dest & __y__) fd->f.y = fs->f.x;
|
||||
if (dest & ___z_) fd->f.z = fs->f.x;
|
||||
if (dest & ____w) fd->f.w = fs->f.x;
|
||||
}
|
||||
|
||||
static inline void SIGN(uint8_t dest, REG_VF *fd, const REG_VF *fs) {
|
||||
if (dest & _x___) if (fs->f.x < 0) fd->f.x = -fd->f.x;
|
||||
if (dest & __y__) if (fs->f.y < 0) fd->f.y = -fd->f.y;
|
||||
if (dest & ___z_) if (fs->f.z < 0) fd->f.z = -fd->f.z;
|
||||
if (dest & ____w) if (fs->f.w < 0) fd->f.w = -fd->f.w;
|
||||
}
|
||||
|
||||
static inline void COPY(uint8_t dest, REG_VF *fd, const int16_t* buf) {
|
||||
if (dest & _x___) fd->f.x = buf[0];
|
||||
if (dest & __y__) fd->f.y = buf[1];
|
||||
if (dest & ___z_) fd->f.z = buf[2];
|
||||
if (dest & ____w) fd->f.w = buf[3];
|
||||
}
|
||||
|
||||
#endif /* _TAC_DECODER_LIB_OPS_H_ */
|
48
src/decode.c
48
src/decode.c
@ -32,6 +32,10 @@ void free_codec(VGMSTREAM* vgmstream) {
|
||||
free_hca(vgmstream->codec_data);
|
||||
}
|
||||
|
||||
if (vgmstream->coding_type == coding_TAC) {
|
||||
free_tac(vgmstream->codec_data);
|
||||
}
|
||||
|
||||
if (vgmstream->coding_type == coding_UBI_ADPCM) {
|
||||
free_ubi_adpcm(vgmstream->codec_data);
|
||||
}
|
||||
@ -125,8 +129,8 @@ void seek_codec(VGMSTREAM* vgmstream) {
|
||||
seek_relic(vgmstream->codec_data, vgmstream->loop_current_sample);
|
||||
}
|
||||
|
||||
if (vgmstream->coding_type == coding_CRI_HCA) {
|
||||
loop_hca(vgmstream->codec_data, vgmstream->loop_current_sample);
|
||||
if (vgmstream->coding_type == coding_TAC) {
|
||||
seek_tac(vgmstream->codec_data, vgmstream->loop_current_sample);
|
||||
}
|
||||
|
||||
if (vgmstream->coding_type == coding_UBI_ADPCM) {
|
||||
@ -227,8 +231,8 @@ void reset_codec(VGMSTREAM* vgmstream) {
|
||||
reset_relic(vgmstream->codec_data);
|
||||
}
|
||||
|
||||
if (vgmstream->coding_type == coding_CRI_HCA) {
|
||||
reset_hca(vgmstream->codec_data);
|
||||
if (vgmstream->coding_type == coding_TAC) {
|
||||
reset_tac(vgmstream->codec_data);
|
||||
}
|
||||
|
||||
if (vgmstream->coding_type == coding_UBI_ADPCM) {
|
||||
@ -509,6 +513,8 @@ int get_vgmstream_samples_per_frame(VGMSTREAM* vgmstream) {
|
||||
return 0; /* 512 */
|
||||
case coding_CRI_HCA:
|
||||
return 0; /* 1024 - delay/padding (which can be bigger than 1024) */
|
||||
case coding_TAC:
|
||||
return 0; /* 1024 - delay/padding */
|
||||
#if defined(VGM_USE_MP4V2) && defined(VGM_USE_FDKAAC)
|
||||
case coding_MP4_AAC:
|
||||
return ((mp4_aac_codec_data*)vgmstream->codec_data)->samples_per_frame;
|
||||
@ -705,27 +711,16 @@ int get_vgmstream_frame_size(VGMSTREAM* vgmstream) {
|
||||
return vgmstream->interleave_block_size;
|
||||
case coding_PTADPCM:
|
||||
return vgmstream->interleave_block_size;
|
||||
case coding_UBI_ADPCM:
|
||||
return 0; /* varies per mode? */
|
||||
case coding_IMUSE:
|
||||
return 0; /* varies per frame */
|
||||
case coding_COMPRESSWAVE:
|
||||
return 0; /* huffman bits */
|
||||
case coding_EA_MT:
|
||||
return 0; /* variable (frames of bit counts or PCM frames) */
|
||||
#ifdef VGM_USE_ATRAC9
|
||||
case coding_ATRAC9:
|
||||
return 0; /* varies with config data, usually 0x100-200 */
|
||||
#endif
|
||||
#ifdef VGM_USE_CELT
|
||||
case coding_CELT_FSB:
|
||||
return 0; /* varies, usually 0x80-100 */
|
||||
#endif
|
||||
#ifdef VGM_USE_SPEEX
|
||||
case coding_SPEEX:
|
||||
return 0; /* varies, usually 0x40-60 */
|
||||
#endif
|
||||
default: /* Vorbis, MPEG, ACM, etc */
|
||||
/* UBI_ADPCM: varies per mode? */
|
||||
/* IMUSE: VBR */
|
||||
/* EA_MT: VBR, frames of bit counts or PCM frames */
|
||||
/* COMPRESSWAVE: VBR/huffman bits */
|
||||
/* ATRAC9: CBR around 0x100-200 */
|
||||
/* CELT FSB: varies, usually 0x80-100 */
|
||||
/* SPEEX: varies, usually 0x40-60 */
|
||||
/* TAC: VBR around ~0x200-300 */
|
||||
/* Vorbis, MPEG, ACM, etc: varies */
|
||||
default: /* (VBR or managed by decoder) */
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@ -1038,6 +1033,9 @@ void decode_vgmstream(VGMSTREAM* vgmstream, int samples_written, int samples_to_
|
||||
case coding_CRI_HCA:
|
||||
decode_hca(vgmstream->codec_data, buffer, samples_to_do);
|
||||
break;
|
||||
case coding_TAC:
|
||||
decode_tac(vgmstream, buffer, samples_to_do);
|
||||
break;
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
case coding_FFmpeg:
|
||||
decode_ffmpeg(vgmstream, buffer, samples_to_do, vgmstream->channels);
|
||||
|
@ -803,7 +803,8 @@ static const coding_info coding_info_list[] = {
|
||||
{coding_CIRCUS_VQ, "Circus VQ"},
|
||||
{coding_RELIC, "Relic Codec"},
|
||||
{coding_CRI_HCA, "CRI HCA"},
|
||||
|
||||
{coding_TAC, "tri-Ace Codec"},
|
||||
|
||||
#ifdef VGM_USE_VORBIS
|
||||
{coding_OGG_VORBIS, "Ogg Vorbis"},
|
||||
{coding_VORBIS_custom, "Custom Vorbis"},
|
||||
@ -1338,6 +1339,7 @@ static const meta_info meta_info_list[] = {
|
||||
{meta_KTAC, "Koei Tecmo KTAC header"},
|
||||
{meta_MJB_MJH, "Sony MultiStream MJH+MJB header"},
|
||||
{meta_BSNF, "id Software BSNF header"},
|
||||
{meta_TAC, "tri-Ace Codec header"},
|
||||
};
|
||||
|
||||
void get_vgmstream_coding_description(VGMSTREAM* vgmstream, char* out, size_t out_size) {
|
||||
|
@ -1741,6 +1741,10 @@
|
||||
<File
|
||||
RelativePath=".\meta\ta_aac.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\meta\tac.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\meta\thp.c"
|
||||
@ -2074,10 +2078,22 @@
|
||||
RelativePath=".\coding\mpeg_decoder.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\coding\nwa_decoder.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\coding\nwa_decoder.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\coding\tac_decoder_lib.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\coding\tac_decoder_lib_data.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\coding\tac_decoder_lib_ops.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\coding\vorbis_bitreader.h"
|
||||
>
|
||||
@ -2326,6 +2342,14 @@
|
||||
RelativePath=".\coding\speex_decoder.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\coding\tac_decoder.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\coding\tac_decoder_lib.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\coding\ubi_adpcm_decoder.c"
|
||||
>
|
||||
|
@ -163,6 +163,9 @@
|
||||
<ClInclude Include="coding\fsb_vorbis_data.h" />
|
||||
<ClInclude Include="coding\g72x_state.h" />
|
||||
<ClInclude Include="coding\nwa_decoder.h" />
|
||||
<ClInclude Include="coding\tac_decoder_lib.h" />
|
||||
<ClInclude Include="coding\tac_decoder_lib_data.h" />
|
||||
<ClInclude Include="coding\tac_decoder_lib_ops.h" />
|
||||
<ClInclude Include="layout\layout.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
@ -535,6 +538,7 @@
|
||||
<ClCompile Include="meta\svs.c" />
|
||||
<ClCompile Include="meta\sxd.c" />
|
||||
<ClCompile Include="meta\ta_aac.c" />
|
||||
<ClCompile Include="meta\tac.c" />
|
||||
<ClCompile Include="meta\thp.c" />
|
||||
<ClCompile Include="meta\vgs.c" />
|
||||
<ClCompile Include="meta\ubi_bao.c" />
|
||||
@ -639,6 +643,8 @@
|
||||
<ClCompile Include="coding\sassc_decoder.c" />
|
||||
<ClCompile Include="coding\sdx2_decoder.c" />
|
||||
<ClCompile Include="coding\speex_decoder.c" />
|
||||
<ClCompile Include="coding\tac_decoder.c" />
|
||||
<ClCompile Include="coding\tac_decoder_lib.c" />
|
||||
<ClCompile Include="coding\ubi_adpcm_decoder.c" />
|
||||
<ClCompile Include="coding\vadpcm_decoder.c" />
|
||||
<ClCompile Include="coding\vorbis_custom_decoder.c" />
|
||||
|
@ -251,6 +251,15 @@
|
||||
<ClInclude Include="coding\nwa_decoder.h">
|
||||
<Filter>coding\Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="coding\tac_decoder_lib.h">
|
||||
<Filter>coding\Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="coding\tac_decoder_lib_data.h">
|
||||
<Filter>coding\Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="coding\tac_decoder_lib_ops.h">
|
||||
<Filter>coding\Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="layout\layout.h">
|
||||
<Filter>layout\Header Files</Filter>
|
||||
</ClInclude>
|
||||
@ -1102,6 +1111,9 @@
|
||||
<ClCompile Include="meta\ta_aac.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\tac.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\thp.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
@ -1390,6 +1402,12 @@
|
||||
<ClCompile Include="coding\speex_decoder.c">
|
||||
<Filter>coding\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="coding\tac_decoder.c">
|
||||
<Filter>coding\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="coding\tac_decoder_lib.c">
|
||||
<Filter>coding\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="coding\ubi_adpcm_decoder.c">
|
||||
<Filter>coding\Source Files</Filter>
|
||||
</ClCompile>
|
||||
|
@ -388,6 +388,9 @@ static const hcakey_info hcakey_list[] = {
|
||||
/* Assault Lily Last Bullet (Android) */
|
||||
{6349046567469313}, // 00168E6C99510101
|
||||
|
||||
/* Sakura Kakumei (iOS/Android) */
|
||||
{382759}, // 000000000005D727
|
||||
|
||||
/* Dragalia Lost (iOS/Android) */
|
||||
{2967411924141, subkeys_dgl, sizeof(subkeys_dgl) / sizeof(subkeys_dgl[0]) }, // 000002B2E7889CAD
|
||||
|
||||
|
@ -944,4 +944,6 @@ VGMSTREAM* init_vgmstream_ktac(STREAMFILE* sf);
|
||||
|
||||
VGMSTREAM* init_vgmstream_mjb_mjh(STREAMFILE* sf);
|
||||
|
||||
VGMSTREAM* init_vgmstream_tac(STREAMFILE* sf);
|
||||
|
||||
#endif /*_META_H*/
|
||||
|
459
src/meta/rsd.c
459
src/meta/rsd.c
@ -1,230 +1,229 @@
|
||||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
|
||||
|
||||
/* RSD - from Radical Entertainment games */
|
||||
VGMSTREAM * init_vgmstream_rsd(STREAMFILE *sf) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
off_t start_offset, name_offset;
|
||||
size_t data_size;
|
||||
int loop_flag, channel_count, sample_rate, interleave;
|
||||
uint32_t codec;
|
||||
uint8_t version;
|
||||
|
||||
|
||||
/* checks */
|
||||
if (!check_extensions(sf,"rsd,rsp"))
|
||||
goto fail;
|
||||
if ((read_32bitBE(0x00,sf) & 0xFFFFFF00) != 0x52534400) /* "RSD\00" */
|
||||
goto fail;
|
||||
|
||||
loop_flag = 0;
|
||||
|
||||
codec = (uint32_t)read_32bitBE(0x04,sf);
|
||||
channel_count = read_32bitLE(0x08, sf);
|
||||
/* 0x0c: always 16? */
|
||||
sample_rate = read_32bitLE(0x10, sf);
|
||||
|
||||
version = read_8bit(0x03, sf);
|
||||
switch(version) {
|
||||
case '2': /* known codecs: VAG/XADP/PCMB [The Simpsons: Road Rage] */
|
||||
case '3': /* known codecs: VAG/PCM/PCMB/GADP? [Dark Summit] */
|
||||
interleave = read_32bitLE(0x14,sf); /* VAG only, 0x04 otherwise */
|
||||
start_offset = read_32bitLE(0x18,sf);
|
||||
name_offset = 0;
|
||||
break;
|
||||
|
||||
case '4': /* known codecs: VAG/PCM/RADP/PCMB [The Simpsons: Hit & Run, Tetris Worlds, Hulk] */
|
||||
/* 0x14: padding */
|
||||
/* 0x18: padding */
|
||||
interleave = 0;
|
||||
start_offset = 0x800;
|
||||
name_offset = 0;
|
||||
|
||||
/* PCMB/PCM/GADP normally start early but sometimes have padding [The Simpsons: Hit & Run (GC/Xbox)] */
|
||||
if ((codec == 0x50434D20 || codec == 0x550434D42 || codec == 0x47414450)
|
||||
&& read_32bitLE(0x80,sf) != 0x2D2D2D2D)
|
||||
start_offset = 0x80;
|
||||
break;
|
||||
|
||||
case '6': /* known codecs: VAG/XADP/WADP/RADP/OOGV/AT3+/XMA [Hulk 2, Crash Tag Team Racing, Crash: Mind over Mutant, Scarface] */
|
||||
/* 0x14: padding */
|
||||
name_offset = 0x18; /* dev file path */
|
||||
interleave = 0;
|
||||
start_offset = 0x800;
|
||||
break;
|
||||
|
||||
default:
|
||||
goto fail;
|
||||
}
|
||||
|
||||
data_size = get_streamfile_size(sf) - start_offset;
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channel_count,loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->meta_type = meta_RSD;
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
|
||||
switch(codec) {
|
||||
case 0x50434D20: /* "PCM " [Dark Summit (Xbox), Hulk (PC)] */
|
||||
vgmstream->coding_type = coding_PCM16LE;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = 0x2;
|
||||
|
||||
vgmstream->num_samples = pcm_bytes_to_samples(data_size, channel_count, 16);
|
||||
break;
|
||||
|
||||
case 0x50434D42: /* "PCMB" [The Simpsons: Road Rage (GC), Dark Summit (GC)] */
|
||||
vgmstream->coding_type = coding_PCM16BE;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = 0x2;
|
||||
|
||||
vgmstream->num_samples = pcm_bytes_to_samples(data_size, channel_count, 16);
|
||||
break;
|
||||
|
||||
case 0x56414720: /* "VAG " [The Simpsons: Road Rage (PS2), Crash Tag Team Racing (PSP)] */
|
||||
vgmstream->coding_type = coding_PSX;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = (interleave == 0) ? 0x10 : interleave;
|
||||
|
||||
vgmstream->num_samples = ps_bytes_to_samples(data_size, channel_count);
|
||||
break;
|
||||
|
||||
case 0x58414450: /* "XADP" [The Simpsons: Road Rage (Xbox)], Crash Tag Team Racing (Xbox)] */
|
||||
vgmstream->coding_type = (channel_count > 2) ? coding_XBOX_IMA_mch : coding_XBOX_IMA;
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
||||
vgmstream->num_samples = xbox_ima_bytes_to_samples(data_size, vgmstream->channels);
|
||||
break;
|
||||
|
||||
case 0x47414450: /* "GADP" [Hulk (GC)] */
|
||||
vgmstream->coding_type = coding_NGC_DSP;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = 0x08; /* assumed, known files are mono */
|
||||
dsp_read_coefs_le(vgmstream,sf,0x14,0x2e); /* LE! */
|
||||
dsp_read_hist_le (vgmstream,sf,0x38,0x2e);
|
||||
|
||||
vgmstream->num_samples = dsp_bytes_to_samples(data_size, channel_count);
|
||||
break;
|
||||
|
||||
case 0x57414450: /* "WADP" [Crash: Mind Over Mutant (Wii)] */
|
||||
vgmstream->coding_type = coding_NGC_DSP_subint;
|
||||
vgmstream->layout_type = layout_none;
|
||||
vgmstream->interleave_block_size = 0x02;
|
||||
dsp_read_coefs_be(vgmstream,sf,0x1a4,0x28);
|
||||
dsp_read_hist_be (vgmstream,sf,0x1c8,0x28);
|
||||
|
||||
vgmstream->num_samples = dsp_bytes_to_samples(data_size, channel_count);
|
||||
break;
|
||||
|
||||
case 0x52414450: /* "RADP" [The Simpsons: Hit & Run (GC), Scarface (Wii)] */
|
||||
vgmstream->coding_type = coding_RAD_IMA;
|
||||
vgmstream->layout_type = layout_none;
|
||||
vgmstream->interleave_block_size = 0x14*channel_count;
|
||||
|
||||
vgmstream->num_samples = data_size / 0x14 / channel_count * 32; /* bytes-to-samples */
|
||||
break;
|
||||
|
||||
#ifdef VGM_USE_VORBIS
|
||||
case 0x4F4F4756: { /* "OOGV" [Scarface (PC)] */
|
||||
ogg_vorbis_meta_info_t ovmi = {0};
|
||||
|
||||
ovmi.meta_type = meta_RSD;
|
||||
close_vgmstream(vgmstream);
|
||||
vgmstream = init_vgmstream_ogg_vorbis_callbacks(sf, NULL, start_offset, &ovmi);
|
||||
if (!vgmstream) goto fail;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
case 0x574D4120: { /* "WMA " [Scarface (Xbox)] */
|
||||
ffmpeg_codec_data *ffmpeg_data = NULL;
|
||||
|
||||
/* mini header + WMA header at start_offset */
|
||||
ffmpeg_data = init_ffmpeg_offset(sf, start_offset+0x08,data_size);
|
||||
if (!ffmpeg_data) goto fail;
|
||||
vgmstream->codec_data = ffmpeg_data;
|
||||
vgmstream->coding_type = coding_FFmpeg;
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
||||
vgmstream->num_samples = (int32_t)ffmpeg_data->totalSamples; /* an estimation, sometimes cuts files a bit early */
|
||||
//vgmstream->num_samples = read_32bitLE(start_offset + 0x00, sf) / channel_count / 2; /* may be PCM data size, but not exact */
|
||||
vgmstream->sample_rate = read_32bitLE(start_offset + 0x04, sf);
|
||||
break;
|
||||
}
|
||||
|
||||
case 0x4154332B: { /* "AT3+" [Crash of the Titans (PSP)] */
|
||||
int fact_samples = 0;
|
||||
|
||||
vgmstream->codec_data = init_ffmpeg_atrac3_riff(sf, start_offset, &fact_samples);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->coding_type = coding_FFmpeg;
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
||||
vgmstream->num_samples = fact_samples;
|
||||
break;
|
||||
}
|
||||
|
||||
case 0x584D4120: { /* "XMA " [Crash of the Titans (X360)-v1, Crash: Mind over Mutant (X360)-v2] */
|
||||
ffmpeg_codec_data *ffmpeg_data = NULL;
|
||||
uint8_t buf[0x100];
|
||||
size_t bytes, xma_size, block_size, block_count;
|
||||
int xma_version;
|
||||
|
||||
|
||||
/* skip mini header */
|
||||
start_offset = 0x800 + read_32bitBE(0x800, sf) + read_32bitBE(0x804, sf) + 0xc; /* assumed, seek table always at 0x800 */
|
||||
xma_size = read_32bitBE(0x808, sf);
|
||||
xma_version = read_32bitBE(0x80C, sf);
|
||||
|
||||
switch (xma_version) {
|
||||
case 0x03010000:
|
||||
case 0x03030000:
|
||||
vgmstream->sample_rate = read_32bitBE(0x818, sf);
|
||||
vgmstream->num_samples = read_32bitBE(0x824, sf);
|
||||
block_count = read_32bitBE(0x828, sf);
|
||||
block_size = 0x10000;
|
||||
break;
|
||||
case 0x04010000:
|
||||
vgmstream->num_samples = read_32bitBE(0x814, sf);
|
||||
vgmstream->sample_rate = read_32bitBE(0x818, sf);
|
||||
block_count = read_32bitBE(0x830, sf);
|
||||
block_size = 0x10000;
|
||||
break;
|
||||
default:
|
||||
goto fail;
|
||||
}
|
||||
|
||||
bytes = ffmpeg_make_riff_xma2(buf,sizeof(buf), vgmstream->num_samples, xma_size, vgmstream->channels, vgmstream->sample_rate, block_count, block_size);
|
||||
ffmpeg_data = init_ffmpeg_header_offset(sf, buf, bytes, start_offset, xma_size);
|
||||
if (!ffmpeg_data) goto fail;
|
||||
vgmstream->codec_data = ffmpeg_data;
|
||||
vgmstream->coding_type = coding_FFmpeg;
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
||||
/* for some reason (dev trickery?) .rsd don't set skip in the bitstream, though they should */
|
||||
//xma_fix_raw_samples(vgmstream, sf, start_offset,xma_size, 0, 0,0);
|
||||
ffmpeg_set_skip_samples(ffmpeg_data, 512+64);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
default:
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (name_offset)
|
||||
read_string(vgmstream->stream_name,STREAM_NAME_SIZE, name_offset,sf);
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream, sf, start_offset))
|
||||
goto fail;
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
|
||||
|
||||
/* RSD - from Radical Entertainment games */
|
||||
VGMSTREAM * init_vgmstream_rsd(STREAMFILE *sf) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
off_t start_offset, name_offset;
|
||||
size_t data_size;
|
||||
int loop_flag, channel_count, sample_rate, interleave;
|
||||
uint32_t codec;
|
||||
uint8_t version;
|
||||
|
||||
|
||||
/* checks */
|
||||
if (!check_extensions(sf,"rsd,rsp"))
|
||||
goto fail;
|
||||
if ((read_32bitBE(0x00,sf) & 0xFFFFFF00) != 0x52534400) /* "RSD\00" */
|
||||
goto fail;
|
||||
|
||||
loop_flag = 0;
|
||||
|
||||
codec = (uint32_t)read_32bitBE(0x04,sf);
|
||||
channel_count = read_32bitLE(0x08, sf);
|
||||
/* 0x0c: always 16? */
|
||||
sample_rate = read_32bitLE(0x10, sf);
|
||||
|
||||
version = read_8bit(0x03, sf);
|
||||
switch(version) {
|
||||
case '2': /* known codecs: VAG/XADP/PCMB [The Simpsons: Road Rage] */
|
||||
case '3': /* known codecs: VAG/PCM/PCMB/GADP? [Dark Summit] */
|
||||
interleave = read_32bitLE(0x14,sf); /* VAG only, 0x04 otherwise */
|
||||
start_offset = read_32bitLE(0x18,sf);
|
||||
name_offset = 0;
|
||||
break;
|
||||
|
||||
case '4': /* known codecs: VAG/PCM/RADP/PCMB [The Simpsons: Hit & Run, Tetris Worlds, Hulk] */
|
||||
/* 0x14: padding */
|
||||
/* 0x18: padding */
|
||||
interleave = 0;
|
||||
start_offset = 0x800;
|
||||
name_offset = 0;
|
||||
|
||||
/* PCMB/PCM/GADP normally start early but sometimes have padding [The Simpsons: Hit & Run (GC/Xbox)] */
|
||||
if ((codec == 0x50434D20 || codec == 0x550434D42 || codec == 0x47414450)
|
||||
&& read_32bitLE(0x80,sf) != 0x2D2D2D2D)
|
||||
start_offset = 0x80;
|
||||
break;
|
||||
|
||||
case '6': /* known codecs: VAG/XADP/WADP/RADP/OOGV/AT3+/XMA [Hulk 2, Crash Tag Team Racing, Crash: Mind over Mutant, Scarface] */
|
||||
/* 0x14: padding */
|
||||
name_offset = 0x18; /* dev file path */
|
||||
interleave = 0;
|
||||
start_offset = 0x800;
|
||||
break;
|
||||
|
||||
default:
|
||||
goto fail;
|
||||
}
|
||||
|
||||
data_size = get_streamfile_size(sf) - start_offset;
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channel_count,loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->meta_type = meta_RSD;
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
|
||||
switch(codec) {
|
||||
case 0x50434D20: /* "PCM " [Dark Summit (Xbox), Hulk (PC)] */
|
||||
vgmstream->coding_type = coding_PCM16LE;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = 0x2;
|
||||
|
||||
vgmstream->num_samples = pcm_bytes_to_samples(data_size, channel_count, 16);
|
||||
break;
|
||||
|
||||
case 0x50434D42: /* "PCMB" [The Simpsons: Road Rage (GC), Dark Summit (GC)] */
|
||||
vgmstream->coding_type = coding_PCM16BE;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = 0x2;
|
||||
|
||||
vgmstream->num_samples = pcm_bytes_to_samples(data_size, channel_count, 16);
|
||||
break;
|
||||
|
||||
case 0x56414720: /* "VAG " [The Simpsons: Road Rage (PS2), Crash Tag Team Racing (PSP)] */
|
||||
vgmstream->coding_type = coding_PSX;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = (interleave == 0) ? 0x10 : interleave;
|
||||
|
||||
vgmstream->num_samples = ps_bytes_to_samples(data_size, channel_count);
|
||||
break;
|
||||
|
||||
case 0x58414450: /* "XADP" [The Simpsons: Road Rage (Xbox)], Crash Tag Team Racing (Xbox)] */
|
||||
vgmstream->coding_type = (channel_count > 2) ? coding_XBOX_IMA_mch : coding_XBOX_IMA;
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
||||
vgmstream->num_samples = xbox_ima_bytes_to_samples(data_size, vgmstream->channels);
|
||||
break;
|
||||
|
||||
case 0x47414450: /* "GADP" [Hulk (GC)] */
|
||||
vgmstream->coding_type = coding_NGC_DSP;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = 0x08; /* assumed, known files are mono */
|
||||
dsp_read_coefs_le(vgmstream,sf,0x14,0x2e); /* LE! */
|
||||
dsp_read_hist_le (vgmstream,sf,0x38,0x2e);
|
||||
|
||||
vgmstream->num_samples = dsp_bytes_to_samples(data_size, channel_count);
|
||||
break;
|
||||
|
||||
case 0x57414450: /* "WADP" [Crash: Mind Over Mutant (Wii)] */
|
||||
vgmstream->coding_type = coding_NGC_DSP_subint;
|
||||
vgmstream->layout_type = layout_none;
|
||||
vgmstream->interleave_block_size = 0x02;
|
||||
dsp_read_coefs_be(vgmstream,sf,0x1a4,0x28);
|
||||
dsp_read_hist_be (vgmstream,sf,0x1c8,0x28);
|
||||
|
||||
vgmstream->num_samples = dsp_bytes_to_samples(data_size, channel_count);
|
||||
break;
|
||||
|
||||
case 0x52414450: /* "RADP" [The Simpsons: Hit & Run (GC), Scarface (Wii)] */
|
||||
vgmstream->coding_type = coding_RAD_IMA;
|
||||
vgmstream->layout_type = layout_none;
|
||||
vgmstream->interleave_block_size = 0x14*channel_count;
|
||||
|
||||
vgmstream->num_samples = data_size / 0x14 / channel_count * 32; /* bytes-to-samples */
|
||||
break;
|
||||
|
||||
#ifdef VGM_USE_VORBIS
|
||||
case 0x4F4F4756: { /* "OOGV" [Scarface (PC)] */
|
||||
ogg_vorbis_meta_info_t ovmi = {0};
|
||||
|
||||
ovmi.meta_type = meta_RSD;
|
||||
close_vgmstream(vgmstream);
|
||||
vgmstream = init_vgmstream_ogg_vorbis_callbacks(sf, NULL, start_offset, &ovmi);
|
||||
if (!vgmstream) goto fail;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
case 0x574D4120: { /* "WMA " [Scarface (Xbox)] */
|
||||
ffmpeg_codec_data *ffmpeg_data = NULL;
|
||||
|
||||
/* mini header + WMA header at start_offset */
|
||||
ffmpeg_data = init_ffmpeg_offset(sf, start_offset+0x08,data_size);
|
||||
if (!ffmpeg_data) goto fail;
|
||||
vgmstream->codec_data = ffmpeg_data;
|
||||
vgmstream->coding_type = coding_FFmpeg;
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
||||
vgmstream->num_samples = (int32_t)ffmpeg_data->totalSamples; /* an estimation, sometimes cuts files a bit early */
|
||||
//vgmstream->num_samples = read_32bitLE(start_offset + 0x00, sf) / channel_count / 2; /* may be PCM data size, but not exact */
|
||||
vgmstream->sample_rate = read_32bitLE(start_offset + 0x04, sf);
|
||||
break;
|
||||
}
|
||||
|
||||
case 0x4154332B: { /* "AT3+" [Crash of the Titans (PSP)] */
|
||||
int fact_samples = 0;
|
||||
|
||||
vgmstream->codec_data = init_ffmpeg_atrac3_riff(sf, start_offset, &fact_samples);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->coding_type = coding_FFmpeg;
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
||||
vgmstream->num_samples = fact_samples;
|
||||
break;
|
||||
}
|
||||
|
||||
case 0x584D4120: { /* "XMA " [Crash of the Titans (X360)-v1, Crash: Mind over Mutant (X360)-v2] */
|
||||
ffmpeg_codec_data *ffmpeg_data = NULL;
|
||||
uint8_t buf[0x100];
|
||||
size_t bytes, xma_size, block_size, block_count;
|
||||
int xma_version;
|
||||
|
||||
|
||||
/* skip mini header */
|
||||
start_offset = 0x800 + read_32bitBE(0x800, sf) + read_32bitBE(0x804, sf) + 0xc; /* assumed, seek table always at 0x800 */
|
||||
xma_size = read_32bitBE(0x808, sf);
|
||||
xma_version = read_u8(0x80C, sf);
|
||||
|
||||
switch (xma_version) {
|
||||
case 0x03:
|
||||
vgmstream->sample_rate = read_32bitBE(0x818, sf);
|
||||
vgmstream->num_samples = read_32bitBE(0x824, sf);
|
||||
block_count = read_32bitBE(0x828, sf);
|
||||
block_size = 0x10000;
|
||||
break;
|
||||
case 0x04:
|
||||
vgmstream->num_samples = read_32bitBE(0x814, sf);
|
||||
vgmstream->sample_rate = read_32bitBE(0x818, sf);
|
||||
block_count = read_32bitBE(0x830, sf);
|
||||
block_size = 0x10000;
|
||||
break;
|
||||
default:
|
||||
goto fail;
|
||||
}
|
||||
|
||||
bytes = ffmpeg_make_riff_xma2(buf,sizeof(buf), vgmstream->num_samples, xma_size, vgmstream->channels, vgmstream->sample_rate, block_count, block_size);
|
||||
ffmpeg_data = init_ffmpeg_header_offset(sf, buf, bytes, start_offset, xma_size);
|
||||
if (!ffmpeg_data) goto fail;
|
||||
vgmstream->codec_data = ffmpeg_data;
|
||||
vgmstream->coding_type = coding_FFmpeg;
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
||||
/* for some reason (dev trickery?) .rsd don't set skip in the bitstream, though they should */
|
||||
//xma_fix_raw_samples(vgmstream, sf, start_offset,xma_size, 0, 0,0);
|
||||
ffmpeg_set_skip_samples(ffmpeg_data, 512+64);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
default:
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (name_offset)
|
||||
read_string(vgmstream->stream_name,STREAM_NAME_SIZE, name_offset,sf);
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream, sf, start_offset))
|
||||
goto fail;
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
|
66
src/meta/tac.c
Normal file
66
src/meta/tac.c
Normal file
@ -0,0 +1,66 @@
|
||||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
|
||||
/* tri-Ace codec file [Star Ocean 3 (PS2), Valkyrie Profile 2 (PS2), Radiata Stories (PS2)] */
|
||||
VGMSTREAM* init_vgmstream_tac(STREAMFILE* sf) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
int loop_flag, channel_count;
|
||||
uint16_t loop_frame, frame_count, loop_discard, frame_discard;
|
||||
uint32_t info_offset, loop_offset, stream_size, file_size;
|
||||
off_t start_offset;
|
||||
|
||||
|
||||
/* checks */
|
||||
/* (extensionless): bigfiles have no known names (libs calls mention "St*" and "Sac*" though)
|
||||
* .aac: fake for convenience given it's a tri-Ace AAC's grandpa (but don't use unless you must)
|
||||
* .pk3/.20: extremely ugly fake extensions randomly given by an old extractor, *DON'T* */
|
||||
if (!check_extensions(sf, ",aac,laac"))
|
||||
goto fail;
|
||||
/* file is validated on decoder init, early catch of simple errors (see decoder for full header) */
|
||||
info_offset = read_u32le(0x00,sf);
|
||||
if (info_offset > 0x4E000 || info_offset < 0x20) /* offset points to value inside first "block" */
|
||||
goto fail;
|
||||
loop_frame = read_u16le(0x08,sf);
|
||||
loop_discard = read_u16le(0x0a,sf);
|
||||
frame_count = read_u16le(0x0c,sf);
|
||||
frame_discard = read_u16le(0x0e,sf);
|
||||
loop_offset = read_u32le(0x10,sf);
|
||||
stream_size = read_u32le(0x14,sf);
|
||||
if (stream_size % 0x4E000 != 0) /* multiple of blocks */
|
||||
goto fail;
|
||||
|
||||
/* actual file can truncate last block */
|
||||
file_size = get_streamfile_size(sf);
|
||||
if (file_size > stream_size || file_size < stream_size - 0x4E000)
|
||||
goto fail;
|
||||
|
||||
channel_count = 2; /* always stereo */
|
||||
loop_flag = (loop_offset != stream_size);
|
||||
start_offset = 0;
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channel_count, loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->meta_type = meta_TAC;
|
||||
vgmstream->sample_rate = 48000;
|
||||
vgmstream->num_samples = frame_count * 1024 - frame_discard;
|
||||
vgmstream->loop_start_sample = loop_frame * 1024 + loop_discard;
|
||||
vgmstream->loop_end_sample = vgmstream->num_samples;
|
||||
|
||||
{
|
||||
vgmstream->codec_data = init_tac(sf);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->coding_type = coding_TAC;
|
||||
vgmstream->layout_type = layout_none;
|
||||
}
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream, sf, start_offset))
|
||||
goto fail;
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
@ -1,268 +1,269 @@
|
||||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
|
||||
|
||||
/* RAKI - Ubisoft audio format [Rayman Legends, Just Dance 2017 (multi)] */
|
||||
VGMSTREAM * init_vgmstream_ubi_raki(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
off_t start_offset, offset, fmt_offset;
|
||||
size_t header_size, data_size;
|
||||
int big_endian;
|
||||
int loop_flag, channel_count, block_align, bits_per_sample;
|
||||
uint32_t platform, type;
|
||||
|
||||
int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL;
|
||||
int16_t (*read_16bit)(off_t,STREAMFILE*) = NULL;
|
||||
|
||||
|
||||
/* checks */
|
||||
/* .rak: Just Dance 2017
|
||||
* .ckd: Rayman Legends (technically .wav.ckd/rak) */
|
||||
if (!check_extensions(streamFile,"rak,ckd"))
|
||||
goto fail;
|
||||
|
||||
/* some games (ex. Rayman Legends PS3) have a 32b file type before the RAKI data. However
|
||||
* offsets are absolute and expect the type exists, so it's part of the file and not an extraction defect. */
|
||||
if ((read_32bitBE(0x00,streamFile) == 0x52414B49)) /* "RAKI" */
|
||||
offset = 0x00;
|
||||
else if ((read_32bitBE(0x04,streamFile) == 0x52414B49)) /* type varies between platforms (0x09, 0x0b) so ignore */
|
||||
offset = 0x04;
|
||||
else
|
||||
goto fail;
|
||||
|
||||
/* 0x04: version? (0x00, 0x07, 0x0a, etc); */
|
||||
platform = read_32bitBE(offset+0x08,streamFile); /* string */
|
||||
type = read_32bitBE(offset+0x0c,streamFile); /* string */
|
||||
|
||||
switch(platform) {
|
||||
case 0x57696920: /* "Wii " */
|
||||
case 0x43616665: /* "Cafe" */
|
||||
case 0x50533320: /* "PS3 " */
|
||||
case 0x58333630: /* "X360" */
|
||||
big_endian = 1;
|
||||
read_32bit = read_32bitBE;
|
||||
read_16bit = read_16bitBE;
|
||||
break;
|
||||
default:
|
||||
big_endian = 0;
|
||||
read_32bit = read_32bitLE;
|
||||
read_16bit = read_16bitLE;
|
||||
break;
|
||||
}
|
||||
|
||||
header_size = read_32bit(offset+0x10,streamFile);
|
||||
start_offset = read_32bit(offset+0x14,streamFile);
|
||||
/* 0x18: number of chunks */
|
||||
/* 0x1c: unk */
|
||||
|
||||
/* the format has a chunk offset table, and the first one always "fmt" and points
|
||||
* to a RIFF "fmt"-style chunk (even for WiiU or PS3) */
|
||||
if (read_32bitBE(offset+0x20,streamFile) != 0x666D7420) goto fail; /* "fmt " */
|
||||
fmt_offset = read_32bit(offset+0x24,streamFile);
|
||||
//fmt_size = read_32bit(off+0x28,streamFile);
|
||||
|
||||
loop_flag = 0; /* not seen */
|
||||
channel_count = read_16bit(fmt_offset+0x2,streamFile);
|
||||
block_align = read_16bit(fmt_offset+0xc,streamFile);
|
||||
bits_per_sample = read_16bit(fmt_offset+0xe,streamFile);
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channel_count,loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->sample_rate = read_32bit(fmt_offset+0x4,streamFile);
|
||||
vgmstream->meta_type = meta_UBI_RAKI;
|
||||
|
||||
/* codecs have a "data" or equivalent chunk with the size/start_offset, but always agree with this */
|
||||
data_size = get_streamfile_size(streamFile) - start_offset;
|
||||
|
||||
/* parse compound codec to simplify */
|
||||
switch(((uint64_t)platform << 32) | type) {
|
||||
|
||||
case 0x57696E2070636D20: /* "Win pcm " */
|
||||
case 0x4F72626970636D20: /* "Orbipcm " (Orbis = PS4) */
|
||||
case 0x4E78202070636D20: /* "Nx pcm " (Nx = Switch) */
|
||||
/* chunks: "data" */
|
||||
vgmstream->coding_type = coding_PCM16LE;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = 0x2;
|
||||
|
||||
vgmstream->num_samples = pcm_bytes_to_samples(data_size, channel_count, bits_per_sample);
|
||||
break;
|
||||
|
||||
case 0x57696E2061647063: /* "Win adpc" */
|
||||
/* chunks: "data" */
|
||||
vgmstream->coding_type = coding_MSADPCM;
|
||||
vgmstream->layout_type = layout_none;
|
||||
vgmstream->frame_size = block_align;
|
||||
|
||||
vgmstream->num_samples = msadpcm_bytes_to_samples(data_size, vgmstream->frame_size, channel_count);
|
||||
|
||||
if (!msadpcm_check_coefs(streamFile, fmt_offset + 0x14))
|
||||
goto fail;
|
||||
break;
|
||||
|
||||
case 0x5769692061647063: /* "Wii adpc" */
|
||||
case 0x4361666561647063: /* "Cafeadpc" (Cafe = WiiU) */
|
||||
/* chunks: "datS" (stereo), "datL" (mono or full interleave), "datR" (full interleave), "data" equivalents */
|
||||
vgmstream->coding_type = coding_NGC_DSP;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = 0x8;
|
||||
|
||||
/* we need to know if the file uses "datL" and is full-interleave */
|
||||
if (channel_count > 1) {
|
||||
off_t chunk_offset = offset+ 0x20 + 0xc; /* after "fmt" */
|
||||
while (chunk_offset < header_size) {
|
||||
if (read_32bitBE(chunk_offset,streamFile) == 0x6461744C) { /* "datL" found */
|
||||
size_t chunk_size = read_32bit(chunk_offset+0x8,streamFile);
|
||||
data_size = chunk_size * channel_count; /* to avoid counting the "datR" chunk */
|
||||
vgmstream->interleave_block_size = (4+4) + chunk_size; /* don't forget to skip the "datR"+size chunk */
|
||||
break;
|
||||
}
|
||||
chunk_offset += 0xc;
|
||||
}
|
||||
|
||||
/* not found? probably "datS" (regular stereo interleave) */
|
||||
}
|
||||
|
||||
{
|
||||
/* get coef offsets; could check "dspL" and "dspR" chunks after "fmt " better but whatevs (only "dspL" if mono) */
|
||||
off_t dsp_coefs = read_32bitBE(offset+0x30,streamFile); /* after "dspL"; spacing is consistent but could vary */
|
||||
dsp_read_coefs(vgmstream,streamFile, dsp_coefs+0x1c, 0x60, big_endian);
|
||||
/* dsp_coefs + 0x00-0x1c: ? (special coefs or adpcm history?) */
|
||||
}
|
||||
|
||||
vgmstream->num_samples = dsp_bytes_to_samples(data_size, channel_count);
|
||||
break;
|
||||
|
||||
case 0x4354520061647063: /* "CTR\0adpc" (Citrus = 3DS) */
|
||||
/* chunks: "dspL" (CWAV-L header), "dspR" (CWAV-R header), "cwav" ("data" equivalent) */
|
||||
vgmstream->coding_type = coding_NGC_DSP;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = 0x8;
|
||||
|
||||
/* reading could be improved but should work with some luck since most values are semi-fixed */
|
||||
if (channel_count > 1) {
|
||||
/* find "dspL" pointing to "CWAV" header and read coefs (separate from data at start_offset) */
|
||||
off_t chunk_offset = offset+ 0x20 + 0xc; /* after "fmt" */
|
||||
while (chunk_offset < header_size) {
|
||||
if (read_32bitBE(chunk_offset,streamFile) == 0x6473704C) { /* "dspL" found */
|
||||
off_t cwav_offset = read_32bit(chunk_offset+0x4,streamFile);
|
||||
size_t cwav_size = read_32bit(chunk_offset+0x8,streamFile);
|
||||
|
||||
dsp_read_coefs(vgmstream,streamFile, cwav_offset + 0x7c, cwav_size, big_endian);
|
||||
break;
|
||||
}
|
||||
chunk_offset += 0xc;
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* CWAV at start (a full CWAV, unlike the above) */
|
||||
dsp_read_coefs(vgmstream,streamFile, start_offset + 0x7c, 0x00, big_endian);
|
||||
start_offset += 0xE0;
|
||||
data_size = get_streamfile_size(streamFile) - start_offset;
|
||||
}
|
||||
|
||||
vgmstream->num_samples = dsp_bytes_to_samples(data_size, channel_count);
|
||||
break;
|
||||
|
||||
|
||||
#ifdef VGM_USE_MPEG
|
||||
case 0x505333206D703320: { /* "PS3 mp3 " */
|
||||
/* chunks: "MARK" (optional seek table), "STRG" (optional description), "Msf " ("data" equivalent) */
|
||||
vgmstream->codec_data = init_mpeg(streamFile, start_offset, &vgmstream->coding_type, vgmstream->channels);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
||||
vgmstream->num_samples = mpeg_bytes_to_samples(data_size, vgmstream->codec_data);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
case 0x58333630786D6132: { /* "X360xma2" */
|
||||
/* chunks: "seek" (XMA2 seek table), "data" */
|
||||
uint8_t buf[100];
|
||||
int bytes, block_count;
|
||||
|
||||
block_count = data_size / block_align + (data_size % block_align ? 1 : 0);
|
||||
|
||||
bytes = ffmpeg_make_riff_xma2(buf, 100, vgmstream->num_samples, data_size, vgmstream->channels, vgmstream->sample_rate, block_count, block_align);
|
||||
vgmstream->codec_data = init_ffmpeg_header_offset(streamFile, buf,bytes, start_offset,data_size);
|
||||
if ( !vgmstream->codec_data ) goto fail;
|
||||
vgmstream->coding_type = coding_FFmpeg;
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
||||
vgmstream->num_samples = read_32bit(fmt_offset+0x18,streamFile);
|
||||
|
||||
xma_fix_raw_samples(vgmstream, streamFile, start_offset,data_size, 0, 0,0); /* should apply to num_samples? */
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef VGM_USE_ATRAC9
|
||||
case 0x5649544161743920: { /* "VITAat9 " */
|
||||
/* chunks: "fact" (equivalent to a RIFF "fact", num_samples + skip_samples), "data" */
|
||||
atrac9_config cfg = {0};
|
||||
|
||||
cfg.channels = vgmstream->channels;
|
||||
cfg.config_data = read_32bitBE(fmt_offset+0x2c,streamFile);
|
||||
cfg.encoder_delay = read_32bit(fmt_offset+0x3c,streamFile);
|
||||
|
||||
vgmstream->codec_data = init_atrac9(&cfg);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->coding_type = coding_ATRAC9;
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
||||
/* could get the "fact" offset but seems it always follows "fmt " */
|
||||
vgmstream->num_samples = read_32bit(fmt_offset+0x34,streamFile);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
case 0x4E7820204E782020: { /* "Nx Nx " */
|
||||
/* chunks: "MARK" (optional seek table), "STRG" (optional description) */
|
||||
size_t skip, opus_size;
|
||||
|
||||
/* a standard Switch Opus header */
|
||||
skip = read_32bitLE(start_offset + 0x1c, streamFile);
|
||||
opus_size = read_32bitLE(start_offset + 0x10, streamFile) + 0x08;
|
||||
start_offset += opus_size;
|
||||
data_size -= opus_size;
|
||||
|
||||
vgmstream->codec_data = init_ffmpeg_switch_opus(streamFile, start_offset,data_size, vgmstream->channels, skip, vgmstream->sample_rate);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->coding_type = coding_FFmpeg;
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
||||
{
|
||||
off_t chunk_offset = offset + 0x20 + 0xc; /* after "fmt" */
|
||||
while (chunk_offset < header_size) {
|
||||
if (read_32bitBE(chunk_offset,streamFile) == 0x4164496E) { /*"AdIn" additional info */
|
||||
off_t adin_offset = read_32bitLE(chunk_offset+0x04,streamFile);
|
||||
vgmstream->num_samples = read_32bitLE(adin_offset,streamFile);
|
||||
break;
|
||||
}
|
||||
chunk_offset += 0xc;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
default:
|
||||
VGM_LOG("RAKI: unknown platform %x and type %x\n", platform, type);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
|
||||
goto fail;
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
|
||||
|
||||
/* RAKI - Ubisoft audio format [Rayman Legends, Just Dance 2017 (multi)] */
|
||||
VGMSTREAM * init_vgmstream_ubi_raki(STREAMFILE *sf) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
off_t start_offset, offset, fmt_offset;
|
||||
size_t header_size, data_size;
|
||||
int big_endian;
|
||||
int loop_flag, channel_count, block_align, bits_per_sample;
|
||||
uint32_t platform, type;
|
||||
|
||||
int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL;
|
||||
int16_t (*read_16bit)(off_t,STREAMFILE*) = NULL;
|
||||
|
||||
|
||||
/* checks */
|
||||
/* .rak: Just Dance 2017
|
||||
* .ckd: Rayman Legends (technically .wav.ckd/rak) */
|
||||
if (!check_extensions(sf,"rak,ckd"))
|
||||
goto fail;
|
||||
|
||||
/* some games (ex. Rayman Legends PS3) have a 32b file type before the RAKI data. However
|
||||
* offsets are absolute and expect the type exists, so it's part of the file and not an extraction defect. */
|
||||
if ((read_32bitBE(0x00,sf) == 0x52414B49)) /* "RAKI" */
|
||||
offset = 0x00;
|
||||
else if ((read_32bitBE(0x04,sf) == 0x52414B49)) /* type varies between platforms (0x09, 0x0b) so ignore */
|
||||
offset = 0x04;
|
||||
else
|
||||
goto fail;
|
||||
|
||||
/* 0x04: version? (0x00, 0x07, 0x0a, etc); */
|
||||
platform = read_32bitBE(offset+0x08,sf); /* string */
|
||||
type = read_32bitBE(offset+0x0c,sf); /* string */
|
||||
|
||||
switch(platform) {
|
||||
case 0x57696920: /* "Wii " */
|
||||
case 0x43616665: /* "Cafe" */
|
||||
case 0x50533320: /* "PS3 " */
|
||||
case 0x58333630: /* "X360" */
|
||||
big_endian = 1;
|
||||
read_32bit = read_32bitBE;
|
||||
read_16bit = read_16bitBE;
|
||||
break;
|
||||
default:
|
||||
big_endian = 0;
|
||||
read_32bit = read_32bitLE;
|
||||
read_16bit = read_16bitLE;
|
||||
break;
|
||||
}
|
||||
|
||||
header_size = read_32bit(offset+0x10,sf);
|
||||
start_offset = read_32bit(offset+0x14,sf);
|
||||
/* 0x18: number of chunks */
|
||||
/* 0x1c: unk */
|
||||
|
||||
/* the format has a chunk offset table, and the first one always "fmt" and points
|
||||
* to a RIFF "fmt"-style chunk (even for WiiU or PS3) */
|
||||
if (read_32bitBE(offset+0x20,sf) != 0x666D7420) goto fail; /* "fmt " */
|
||||
fmt_offset = read_32bit(offset+0x24,sf);
|
||||
//fmt_size = read_32bit(off+0x28,sf);
|
||||
|
||||
loop_flag = 0; /* not seen */
|
||||
channel_count = read_16bit(fmt_offset+0x2,sf);
|
||||
block_align = read_16bit(fmt_offset+0xc,sf);
|
||||
bits_per_sample = read_16bit(fmt_offset+0xe,sf);
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channel_count,loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->sample_rate = read_32bit(fmt_offset+0x4,sf);
|
||||
vgmstream->meta_type = meta_UBI_RAKI;
|
||||
|
||||
/* codecs have a "data" or equivalent chunk with the size/start_offset, but always agree with this */
|
||||
data_size = get_streamfile_size(sf) - start_offset;
|
||||
|
||||
/* parse compound codec to simplify */
|
||||
switch(((uint64_t)platform << 32) | type) {
|
||||
|
||||
case 0x57696E2070636D20: /* "Win pcm " */
|
||||
case 0x4F72626970636D20: /* "Orbipcm " (Orbis = PS4) */
|
||||
case 0x4E78202070636D20: /* "Nx pcm " (Nx = Switch) */
|
||||
/* chunks: "data" */
|
||||
vgmstream->coding_type = coding_PCM16LE;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = 0x2;
|
||||
|
||||
vgmstream->num_samples = pcm_bytes_to_samples(data_size, channel_count, bits_per_sample);
|
||||
break;
|
||||
|
||||
case 0x57696E2061647063: /* "Win adpc" */
|
||||
/* chunks: "data" */
|
||||
vgmstream->coding_type = coding_MSADPCM;
|
||||
vgmstream->layout_type = layout_none;
|
||||
vgmstream->frame_size = block_align;
|
||||
|
||||
vgmstream->num_samples = msadpcm_bytes_to_samples(data_size, vgmstream->frame_size, channel_count);
|
||||
|
||||
if (!msadpcm_check_coefs(sf, fmt_offset + 0x14))
|
||||
goto fail;
|
||||
break;
|
||||
|
||||
case 0x5769692061647063: /* "Wii adpc" */
|
||||
case 0x4361666561647063: /* "Cafeadpc" (Cafe = WiiU) */
|
||||
/* chunks: "datS" (stereo), "datL" (mono or full interleave), "datR" (full interleave), "data" equivalents */
|
||||
vgmstream->coding_type = coding_NGC_DSP;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = 0x8;
|
||||
|
||||
/* we need to know if the file uses "datL" and is full-interleave */
|
||||
if (channel_count > 1) {
|
||||
off_t chunk_offset = offset+ 0x20 + 0xc; /* after "fmt" */
|
||||
while (chunk_offset < header_size) {
|
||||
if (read_32bitBE(chunk_offset,sf) == 0x6461744C) { /* "datL" found */
|
||||
size_t chunk_size = read_32bit(chunk_offset+0x8,sf);
|
||||
data_size = chunk_size * channel_count; /* to avoid counting the "datR" chunk */
|
||||
vgmstream->interleave_block_size = (4+4) + chunk_size; /* don't forget to skip the "datR"+size chunk */
|
||||
break;
|
||||
}
|
||||
chunk_offset += 0xc;
|
||||
}
|
||||
|
||||
/* not found? probably "datS" (regular stereo interleave) */
|
||||
}
|
||||
|
||||
{
|
||||
/* get coef offsets; could check "dspL" and "dspR" chunks after "fmt " better but whatevs (only "dspL" if mono) */
|
||||
off_t dsp_coefs = read_32bitBE(offset+0x30,sf); /* after "dspL"; spacing is consistent but could vary */
|
||||
dsp_read_coefs(vgmstream,sf, dsp_coefs+0x1c, 0x60, big_endian);
|
||||
/* dsp_coefs + 0x00-0x1c: ? (special coefs or adpcm history?) */
|
||||
}
|
||||
|
||||
vgmstream->num_samples = dsp_bytes_to_samples(data_size, channel_count);
|
||||
break;
|
||||
|
||||
case 0x4354520061647063: /* "CTR\0adpc" (Citrus = 3DS) */
|
||||
/* chunks: "dspL" (CWAV-L header), "dspR" (CWAV-R header), "cwav" ("data" equivalent) */
|
||||
vgmstream->coding_type = coding_NGC_DSP;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = 0x8;
|
||||
|
||||
/* reading could be improved but should work with some luck since most values are semi-fixed */
|
||||
if (channel_count > 1) {
|
||||
/* find "dspL" pointing to "CWAV" header and read coefs (separate from data at start_offset) */
|
||||
off_t chunk_offset = offset+ 0x20 + 0xc; /* after "fmt" */
|
||||
while (chunk_offset < header_size) {
|
||||
if (read_32bitBE(chunk_offset,sf) == 0x6473704C) { /* "dspL" found */
|
||||
off_t cwav_offset = read_32bit(chunk_offset+0x4,sf);
|
||||
size_t cwav_size = read_32bit(chunk_offset+0x8,sf);
|
||||
|
||||
dsp_read_coefs(vgmstream,sf, cwav_offset + 0x7c, cwav_size, big_endian);
|
||||
break;
|
||||
}
|
||||
chunk_offset += 0xc;
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* CWAV at start (a full CWAV, unlike the above) */
|
||||
dsp_read_coefs(vgmstream,sf, start_offset + 0x7c, 0x00, big_endian);
|
||||
start_offset += 0xE0;
|
||||
data_size = get_streamfile_size(sf) - start_offset;
|
||||
}
|
||||
|
||||
vgmstream->num_samples = dsp_bytes_to_samples(data_size, channel_count);
|
||||
break;
|
||||
|
||||
|
||||
#ifdef VGM_USE_MPEG
|
||||
case 0x505333206D703320: { /* "PS3 mp3 " */
|
||||
/* chunks: "MARK" (optional seek table), "STRG" (optional description), "Msf " ("data" equivalent) */
|
||||
vgmstream->codec_data = init_mpeg(sf, start_offset, &vgmstream->coding_type, vgmstream->channels);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
||||
vgmstream->num_samples = mpeg_bytes_to_samples(data_size, vgmstream->codec_data);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
case 0x58333630786D6132: { /* "X360xma2" */
|
||||
/* chunks: "seek" (XMA2 seek table), "data" */
|
||||
uint8_t buf[100];
|
||||
int bytes, block_count;
|
||||
if (!block_align) goto fail;
|
||||
|
||||
block_count = data_size / block_align + (data_size % block_align ? 1 : 0);
|
||||
|
||||
bytes = ffmpeg_make_riff_xma2(buf, 100, vgmstream->num_samples, data_size, vgmstream->channels, vgmstream->sample_rate, block_count, block_align);
|
||||
vgmstream->codec_data = init_ffmpeg_header_offset(sf, buf,bytes, start_offset,data_size);
|
||||
if ( !vgmstream->codec_data ) goto fail;
|
||||
vgmstream->coding_type = coding_FFmpeg;
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
||||
vgmstream->num_samples = read_32bit(fmt_offset+0x18,sf);
|
||||
|
||||
xma_fix_raw_samples(vgmstream, sf, start_offset,data_size, 0, 0,0); /* should apply to num_samples? */
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef VGM_USE_ATRAC9
|
||||
case 0x5649544161743920: { /* "VITAat9 " */
|
||||
/* chunks: "fact" (equivalent to a RIFF "fact", num_samples + skip_samples), "data" */
|
||||
atrac9_config cfg = {0};
|
||||
|
||||
cfg.channels = vgmstream->channels;
|
||||
cfg.config_data = read_32bitBE(fmt_offset+0x2c,sf);
|
||||
cfg.encoder_delay = read_32bit(fmt_offset+0x3c,sf);
|
||||
|
||||
vgmstream->codec_data = init_atrac9(&cfg);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->coding_type = coding_ATRAC9;
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
||||
/* could get the "fact" offset but seems it always follows "fmt " */
|
||||
vgmstream->num_samples = read_32bit(fmt_offset+0x34,sf);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
case 0x4E7820204E782020: { /* "Nx Nx " */
|
||||
/* chunks: "MARK" (optional seek table), "STRG" (optional description) */
|
||||
size_t skip, opus_size;
|
||||
|
||||
/* a standard Switch Opus header */
|
||||
skip = read_32bitLE(start_offset + 0x1c, sf);
|
||||
opus_size = read_32bitLE(start_offset + 0x10, sf) + 0x08;
|
||||
start_offset += opus_size;
|
||||
data_size -= opus_size;
|
||||
|
||||
vgmstream->codec_data = init_ffmpeg_switch_opus(sf, start_offset,data_size, vgmstream->channels, skip, vgmstream->sample_rate);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->coding_type = coding_FFmpeg;
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
||||
{
|
||||
off_t chunk_offset = offset + 0x20 + 0xc; /* after "fmt" */
|
||||
while (chunk_offset < header_size) {
|
||||
if (read_32bitBE(chunk_offset,sf) == 0x4164496E) { /*"AdIn" additional info */
|
||||
off_t adin_offset = read_32bitLE(chunk_offset+0x04,sf);
|
||||
vgmstream->num_samples = read_32bitLE(adin_offset,sf);
|
||||
break;
|
||||
}
|
||||
chunk_offset += 0xc;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
default:
|
||||
VGM_LOG("RAKI: unknown platform %x and type %x\n", platform, type);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream,sf,start_offset))
|
||||
goto fail;
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
|
@ -522,6 +522,7 @@ VGMSTREAM* (*init_vgmstream_functions[])(STREAMFILE* sf) = {
|
||||
init_vgmstream_mjb_mjh,
|
||||
init_vgmstream_mzrt_v1,
|
||||
init_vgmstream_bsnf,
|
||||
init_vgmstream_tac,
|
||||
|
||||
/* lowest priority metas (should go after all metas, and TXTH should go before raw formats) */
|
||||
init_vgmstream_txth, /* proper parsers should supersede TXTH, once added */
|
||||
|
@ -193,6 +193,7 @@ typedef enum {
|
||||
coding_CIRCUS_VQ, /* Circus VQ */
|
||||
coding_RELIC, /* Relic Codec (DCT-based) */
|
||||
coding_CRI_HCA, /* CRI High Compression Audio (MDCT-based) */
|
||||
coding_TAC, /* tri-Ace Codec (MDCT-based) */
|
||||
|
||||
#ifdef VGM_USE_VORBIS
|
||||
coding_OGG_VORBIS, /* Xiph Vorbis with Ogg layer (MDCT-based) */
|
||||
@ -754,6 +755,7 @@ typedef enum {
|
||||
meta_KTAC,
|
||||
meta_MJB_MJH,
|
||||
meta_BSNF,
|
||||
meta_TAC,
|
||||
} meta_t;
|
||||
|
||||
/* standard WAVEFORMATEXTENSIBLE speaker positions */
|
||||
@ -1048,7 +1050,7 @@ typedef struct {
|
||||
uint64_t size; // max size within the streamfile
|
||||
uint64_t logical_offset; // computed offset FFmpeg sees (including fake header)
|
||||
uint64_t logical_size; // computed size FFmpeg sees (including fake header)
|
||||
|
||||
|
||||
uint64_t header_size; // fake header (parseable by FFmpeg) prepended on reads
|
||||
uint8_t* header_block; // fake header data (ie. RIFF)
|
||||
|
||||
@ -1061,7 +1063,7 @@ typedef struct {
|
||||
int64_t totalSamples; // estimated count (may not be accurate for some demuxers)
|
||||
int64_t skipSamples; // number of start samples that will be skipped (encoder delay), for looping adjustments
|
||||
int streamCount; // number of FFmpeg audio streams
|
||||
|
||||
|
||||
/*** internal state ***/
|
||||
// config
|
||||
int channel_remap_set;
|
||||
@ -1073,7 +1075,7 @@ typedef struct {
|
||||
|
||||
// FFmpeg context used for metadata
|
||||
AVCodec *codec;
|
||||
|
||||
|
||||
// FFmpeg decoder state
|
||||
unsigned char *buffer;
|
||||
AVIOContext *ioCtx;
|
||||
|
Loading…
x
Reference in New Issue
Block a user