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:
bnnm 2021-02-16 23:28:36 +01:00 committed by GitHub
commit 2081a5b322
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 3544 additions and 548 deletions

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View 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_ */

File diff suppressed because it is too large Load Diff

View 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_ */

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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
View 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;
}

View File

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

View File

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

View File

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