Updated VGMStream to r1050-3618-gfbf7bcaa
parent
3a387c3a3f
commit
ab6429b62b
|
@ -619,6 +619,12 @@
|
|||
83D7318A1A749D2200CA1366 /* g719.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 83D7313E1A74968A00CA1366 /* g719.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
83D7318C1A749EEE00CA1366 /* g719_decoder.c in Sources */ = {isa = PBXBuildFile; fileRef = 83D7318B1A749EEE00CA1366 /* g719_decoder.c */; };
|
||||
83E56BA51F2EE3520026BC60 /* vorbis_custom_utils_ogl.c in Sources */ = {isa = PBXBuildFile; fileRef = 83E56BA01F2EE3500026BC60 /* vorbis_custom_utils_ogl.c */; };
|
||||
83E7FD5F25EF2B0C00683FD2 /* tac_decoder.c in Sources */ = {isa = PBXBuildFile; fileRef = 83E7FD5725EF2B0C00683FD2 /* tac_decoder.c */; };
|
||||
83E7FD6025EF2B0C00683FD2 /* tac_decoder_lib.c in Sources */ = {isa = PBXBuildFile; fileRef = 83E7FD5B25EF2B0C00683FD2 /* tac_decoder_lib.c */; };
|
||||
83E7FD6125EF2B0C00683FD2 /* tac_decoder_lib.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E7FD5C25EF2B0C00683FD2 /* tac_decoder_lib.h */; };
|
||||
83E7FD6225EF2B0C00683FD2 /* tac_decoder_lib_ops.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E7FD5D25EF2B0C00683FD2 /* tac_decoder_lib_ops.h */; };
|
||||
83E7FD6325EF2B0C00683FD2 /* tac_decoder_lib_data.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E7FD5E25EF2B0C00683FD2 /* tac_decoder_lib_data.h */; };
|
||||
83E7FD6525EF2B2400683FD2 /* tac.c in Sources */ = {isa = PBXBuildFile; fileRef = 83E7FD6425EF2B2400683FD2 /* tac.c */; };
|
||||
83EDE5D81A70951A005F5D84 /* mca.c in Sources */ = {isa = PBXBuildFile; fileRef = 83EDE5D61A70951A005F5D84 /* mca.c */; };
|
||||
83EDE5D91A70951A005F5D84 /* btsnd.c in Sources */ = {isa = PBXBuildFile; fileRef = 83EDE5D71A70951A005F5D84 /* btsnd.c */; };
|
||||
83EED5D3203A8BC7008BEB45 /* ea_swvr.c in Sources */ = {isa = PBXBuildFile; fileRef = 83EED5D1203A8BC7008BEB45 /* ea_swvr.c */; };
|
||||
|
@ -1369,6 +1375,12 @@
|
|||
83D731381A74968900CA1366 /* g719.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = g719.xcodeproj; path = ../g719/g719.xcodeproj; sourceTree = "<group>"; };
|
||||
83D7318B1A749EEE00CA1366 /* g719_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = g719_decoder.c; sourceTree = "<group>"; };
|
||||
83E56BA01F2EE3500026BC60 /* vorbis_custom_utils_ogl.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = vorbis_custom_utils_ogl.c; sourceTree = "<group>"; };
|
||||
83E7FD5725EF2B0C00683FD2 /* tac_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = tac_decoder.c; sourceTree = "<group>"; };
|
||||
83E7FD5B25EF2B0C00683FD2 /* tac_decoder_lib.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = tac_decoder_lib.c; sourceTree = "<group>"; };
|
||||
83E7FD5C25EF2B0C00683FD2 /* tac_decoder_lib.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = tac_decoder_lib.h; sourceTree = "<group>"; };
|
||||
83E7FD5D25EF2B0C00683FD2 /* tac_decoder_lib_ops.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = tac_decoder_lib_ops.h; sourceTree = "<group>"; };
|
||||
83E7FD5E25EF2B0C00683FD2 /* tac_decoder_lib_data.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = tac_decoder_lib_data.h; sourceTree = "<group>"; };
|
||||
83E7FD6425EF2B2400683FD2 /* tac.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = tac.c; sourceTree = "<group>"; };
|
||||
83EDE5D61A70951A005F5D84 /* mca.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mca.c; sourceTree = "<group>"; };
|
||||
83EDE5D71A70951A005F5D84 /* btsnd.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = btsnd.c; sourceTree = "<group>"; };
|
||||
83EED5D1203A8BC7008BEB45 /* ea_swvr.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ea_swvr.c; sourceTree = "<group>"; };
|
||||
|
@ -1621,6 +1633,11 @@
|
|||
836F6DFB18BDC2180095E648 /* SASSC_decoder.c */,
|
||||
836F6DFC18BDC2180095E648 /* sdx2_decoder.c */,
|
||||
8346D97E25BF83B200D1A8B0 /* speex_decoder.c */,
|
||||
83E7FD5E25EF2B0C00683FD2 /* tac_decoder_lib_data.h */,
|
||||
83E7FD5D25EF2B0C00683FD2 /* tac_decoder_lib_ops.h */,
|
||||
83E7FD5B25EF2B0C00683FD2 /* tac_decoder_lib.c */,
|
||||
83E7FD5C25EF2B0C00683FD2 /* tac_decoder_lib.h */,
|
||||
83E7FD5725EF2B0C00683FD2 /* tac_decoder.c */,
|
||||
8373341023F60C7A00DE14DC /* tgcadpcm_decoder.c */,
|
||||
837CEA7623487E2400E62A4A /* ubi_adpcm_decoder.c */,
|
||||
83F1EE2C245D4FB20076E182 /* vadpcm_decoder.c */,
|
||||
|
@ -2055,6 +2072,7 @@
|
|||
83D0381724A4129A004CF90F /* swav.c */,
|
||||
831BA6121EAC61A500CF89B0 /* sxd.c */,
|
||||
83709E031ECBC1A4005C03D3 /* ta_aac.c */,
|
||||
83E7FD6425EF2B2400683FD2 /* tac.c */,
|
||||
8373342E23F60D4100DE14DC /* tgc.c */,
|
||||
836F6EFA18BDC2190095E648 /* thp.c */,
|
||||
836F6EFB18BDC2190095E648 /* tun.c */,
|
||||
|
@ -2185,7 +2203,10 @@
|
|||
83C7282222BC893D00678B4A /* mta2_streamfile.h in Headers */,
|
||||
83AA7F802519C042004C5298 /* sab_streamfile.h in Headers */,
|
||||
83A21F87201D8981000F04B9 /* fsb_keys.h in Headers */,
|
||||
83E7FD6325EF2B0C00683FD2 /* tac_decoder_lib_data.h in Headers */,
|
||||
83E7FD6125EF2B0C00683FD2 /* tac_decoder_lib.h in Headers */,
|
||||
834FE0EC215C79ED000A5D3D /* kma9_streamfile.h in Headers */,
|
||||
83E7FD6225EF2B0C00683FD2 /* tac_decoder_lib_ops.h in Headers */,
|
||||
8349A90F1FE6258200E26435 /* aix_streamfile.h in Headers */,
|
||||
836F705718BDC2190095E648 /* util.h in Headers */,
|
||||
83C7282722BC8C1500678B4A /* plugins.h in Headers */,
|
||||
|
@ -2468,6 +2489,7 @@
|
|||
832BF82821E0514B006F50F1 /* xwma.c in Sources */,
|
||||
83FC176E23AC58D100E1025F /* csb.c in Sources */,
|
||||
8306B0EB20984590000302D4 /* wave_segmented.c in Sources */,
|
||||
83E7FD5F25EF2B0C00683FD2 /* tac_decoder.c in Sources */,
|
||||
836F6F9F18BDC2190095E648 /* musc.c in Sources */,
|
||||
8349A9121FE6258200E26435 /* vsf_tta.c in Sources */,
|
||||
836F6FCA18BDC2190095E648 /* ps2_adm.c in Sources */,
|
||||
|
@ -2490,6 +2512,7 @@
|
|||
8346D97C25BF838C00D1A8B0 /* mjb_mjh.c in Sources */,
|
||||
8373341823F60C7B00DE14DC /* tgcadpcm_decoder.c in Sources */,
|
||||
836F6F3318BDC2190095E648 /* ngc_dtk_decoder.c in Sources */,
|
||||
83E7FD6525EF2B2400683FD2 /* tac.c in Sources */,
|
||||
83C7281322BC893D00678B4A /* mta2.c in Sources */,
|
||||
8306B0EF20984590000302D4 /* ubi_bao.c in Sources */,
|
||||
836F6FBB18BDC2190095E648 /* ngca.c in Sources */,
|
||||
|
@ -2701,6 +2724,7 @@
|
|||
836F700E18BDC2190095E648 /* ps2_xa2.c in Sources */,
|
||||
837CEB0023487F2C00E62A4A /* smk.c in Sources */,
|
||||
83C7281022BC893D00678B4A /* nps.c in Sources */,
|
||||
83E7FD6025EF2B0C00683FD2 /* tac_decoder_lib.c in Sources */,
|
||||
83C7281E22BC893D00678B4A /* msf.c in Sources */,
|
||||
836F6FF718BDC2190095E648 /* ps2_sl3.c in Sources */,
|
||||
8351F32D2212B57000A606E4 /* 208.c in Sources */,
|
||||
|
|
|
@ -110,8 +110,8 @@ void decode_hevag(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing
|
|||
|
||||
|
||||
/* xa_decoder */
|
||||
void decode_xa(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel);
|
||||
size_t xa_bytes_to_samples(size_t bytes, int channels, int is_blocked, int is_form2);
|
||||
void decode_xa(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, int is_xa8);
|
||||
size_t xa_bytes_to_samples(size_t bytes, int channels, int is_blocked, int is_form2, int bps);
|
||||
|
||||
|
||||
/* ea_xa_decoder */
|
||||
|
@ -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;
|
||||
|
|
|
@ -442,8 +442,10 @@ int32_t mpeg_get_samples_clean(STREAMFILE *sf, off_t start, size_t size, size_t*
|
|||
goto fail;
|
||||
|
||||
num_samples = size / info.frame_size * info.frame_samples;
|
||||
loop_start = *p_loop_start / info.frame_size * info.frame_samples;
|
||||
loop_end = *p_loop_end / info.frame_size * info.frame_samples;
|
||||
if (p_loop_start)
|
||||
loop_start = *p_loop_start / info.frame_size * info.frame_samples;
|
||||
if (p_loop_end)
|
||||
loop_end = *p_loop_end / info.frame_size * info.frame_samples;
|
||||
}
|
||||
else {
|
||||
/* VBR (or unknown) = count frames */
|
||||
|
@ -451,20 +453,22 @@ int32_t mpeg_get_samples_clean(STREAMFILE *sf, off_t start, size_t size, size_t*
|
|||
if (!mpeg_get_frame_info(sf, offset, &info))
|
||||
goto fail;
|
||||
|
||||
if (*p_loop_start + start == offset)
|
||||
if (p_loop_start && *p_loop_start + start == offset)
|
||||
loop_start = num_samples;
|
||||
|
||||
num_samples += info.frame_samples;
|
||||
offset += info.frame_size;
|
||||
|
||||
if (*p_loop_end + start == offset)
|
||||
if (p_loop_end && *p_loop_end + start == offset)
|
||||
loop_end = num_samples;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
*p_loop_start = loop_start;
|
||||
*p_loop_end = loop_end;
|
||||
if (p_loop_start)
|
||||
*p_loop_start = loop_start;
|
||||
if (p_loop_end)
|
||||
*p_loop_end = loop_end;
|
||||
|
||||
return num_samples;
|
||||
fail:
|
||||
|
|
|
@ -0,0 +1,187 @@
|
|||
#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 feed_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->feed_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->feed_block = 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (err == TAC_PROCESS_DONE) {
|
||||
VGM_LOG("TAC: process done (EOF) %i\n", err);
|
||||
goto fail; /* shouldn't reach this */
|
||||
}
|
||||
|
||||
if (err != TAC_PROCESS_OK) {
|
||||
VGM_LOG("TAC: process error %i\n", err);
|
||||
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->feed_block) {
|
||||
int bytes = read_streamfile(data->buf, data->offset, sizeof(data->buf), sf);
|
||||
data->offset += bytes;
|
||||
data->feed_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->offset = 0;
|
||||
data->feed_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 - 1) * TAC_FRAME_SAMPLES + hdr->loop_discard;
|
||||
if (loop_sample == num_sample) {
|
||||
tac_set_loop(data->handle); /* direct looping */
|
||||
|
||||
data->samples_discard = hdr->loop_discard;
|
||||
data->offset = hdr->loop_offset;
|
||||
data->feed_block = 1;
|
||||
data->sbuf.filled = 0;
|
||||
}
|
||||
else {
|
||||
tac_reset(data->handle);
|
||||
|
||||
data->samples_discard = num_sample;
|
||||
data->offset = 0;
|
||||
data->feed_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);
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -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 unknown; /* ignored? (may be CDVD stuff, divided/multiplied during PS2 process, not size related) */
|
||||
uint16_t loop_frame; /* aligned to block start */
|
||||
uint16_t loop_discard; /* discarded start samples in loop frame (lower = outputs more) */
|
||||
uint16_t frame_count; /* number of valid frames ("block end" frames not included) */
|
||||
uint16_t frame_last; /* valid samples in final frame - 1 (lower = outputs less, 0 = outputs 1), even for non-looped files */
|
||||
uint32_t loop_offset; /* points to a block; file size if not looped */
|
||||
uint32_t file_size; /* block aligned; actual file size can be a bit smaller if last block is truncated */
|
||||
uint32_t joint_stereo; /* usually 0 and rarely 1 */
|
||||
uint32_t empty; /* always 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
|
@ -0,0 +1,376 @@
|
|||
#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_ */
|
|
@ -38,45 +38,57 @@ static const int IK1[4] = { 0, 0, 832, 880 };
|
|||
* PS1 XA is apparently upsampled and interpolated to 44100, vgmstream doesn't simulate this.
|
||||
*
|
||||
* XA has an 8-bit decoding and "emphasis" modes, that no PS1 game actually uses, but apparently
|
||||
* are supported by the CD hardware and will play if found.
|
||||
* are supported by the CD hardware and will play if found. Some odd CD-i game does use 8-bit mode.
|
||||
* Official "sound quality level" modes:
|
||||
* - Level A: 37.8hz, 8-bit
|
||||
* - Level B: 37.8hz, 4-bit
|
||||
* - Level C: 18.9hz, 4-bit
|
||||
*
|
||||
* Info (Green Book): https://www.lscdweb.com/data/downloadables/2/8/cdi_may94_r2.pdf
|
||||
* BRR info (no$sns): http://problemkaputt.de/fullsnes.htm#snesapudspbrrsamples
|
||||
* (bsnes): https://github.com/byuu/bsnes/blob/master/bsnes/sfc/dsp/SPC_DSP.cpp#L316
|
||||
*/
|
||||
|
||||
void decode_xa(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) {
|
||||
/* data layout (mono):
|
||||
* - CD-XA audio is divided into sectors ("audio blocks"), each with 18*0x80 frames
|
||||
* (sectors handled externally, this decoder only sees N frames)
|
||||
* - each frame ("sound group") is divided into 8 interleaved subframes ("sound unit"), with
|
||||
* 8*0x01 subframe headers x2 ("sound parameters") first then 28*0x04 subframe nibbles ("sound data")
|
||||
* - subframe headers: 0..3 + repeat 0..3 + 4..7 + repeat 4..7 (where N = subframe N header)
|
||||
* (repeats may be for error correction, though probably unused)
|
||||
* header has a "range" N (gain of 2^N, or simplified as a shift) and a "filter" (control gains K0 and K1)
|
||||
* - subframe nibbles: 32b with nibble0 for subframes 0..8, 32b with nibble1 for subframes 0..8, etc
|
||||
* (low first: 32b = sf1-n0 sf0-n0 sf3-n0 sf2-n0 sf5-n0 sf4-n0 sf7-n0 sf6-n0, etc)
|
||||
*
|
||||
* stereo layout is the same but alternates channels: subframe 0/2/4/6=L, subframe 1/3/5/7=R
|
||||
*
|
||||
* example:
|
||||
* subframe 0: header @ 0x00 or 0x04, 28 nibbles (low) @ 0x10,14,18,1c,20 ... 7c
|
||||
* subframe 1: header @ 0x01 or 0x05, 28 nibbles (high) @ 0x10,14,18,1c,20 ... 7c
|
||||
* subframe 2: header @ 0x02 or 0x06, 28 nibbles (low) @ 0x11,15,19,1d,21 ... 7d
|
||||
* ...
|
||||
* subframe 7: header @ 0x0b or 0x0f, 28 nibbles (high) @ 0x13,17,1b,1f,23 ... 7f
|
||||
*
|
||||
* 8-bit layout is similar but only has 4 subframes + subframe bytes, so half the samples:
|
||||
* subframe 0: header @ 0x00 or 0x04/08/0c, 28 bytes @ 0x10,14,18,1c,20 ... 7c
|
||||
* subframe 1: header @ 0x01 or 0x05/09/0d, 28 bytes @ 0x11,16,19,1d,21 ... 7d
|
||||
* ...
|
||||
* subframe 3: header @ 0x03 or 0x07/0b/0f, 28 bytes @ 0x13,17,1b,1f,23 ... 7f
|
||||
*/
|
||||
|
||||
void decode_xa(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, int is_xa8) {
|
||||
uint8_t frame[0x80] = {0};
|
||||
off_t frame_offset;
|
||||
int i,j, sp_pos, frames_in, samples_done = 0, sample_count = 0;
|
||||
size_t bytes_per_frame, samples_per_frame;
|
||||
int32_t hist1 = stream->adpcm_history1_32;
|
||||
int32_t hist2 = stream->adpcm_history2_32;
|
||||
int subframes = (is_xa8) ? 4 : 8;
|
||||
|
||||
|
||||
/* data layout (mono):
|
||||
* - CD-XA audio is divided into sectors ("audio blocks"), each with 18 size 0x80 frames
|
||||
* (handled externally, this decoder only gets frames)
|
||||
* - a frame ("sound group") is divided into 8 subframes ("sound unit"), with
|
||||
* subframe headers ("sound parameters") first then subframe nibbles ("sound data")
|
||||
* - headers: 0..3 + repeat 0..3 + 4..7 + repeat 4..7 (where N = subframe N header)
|
||||
* (repeats may be for error correction, though probably unused)
|
||||
* - nibbles: 32b with nibble0 for subframes 0..8, 32b with nibble1 for subframes 0..8, etc
|
||||
* (low first: 32b = sf1-n0 sf0-n0 sf3-n0 sf2-n0 sf5-n0 sf4-n0 sf7-n0 sf6-n0, etc)
|
||||
*
|
||||
* stereo layout is the same but alternates channels: subframe 0/2/4/6=L, subframe 1/3/5/7=R
|
||||
*
|
||||
* example:
|
||||
* subframe 0: header @ 0x00 or 0x04, 28 nibbles (low) @ 0x10,14,18,1c,20 ... 7c
|
||||
* subframe 1: header @ 0x01 or 0x05, 28 nibbles (high) @ 0x10,14,18,1c,20 ... 7c
|
||||
* subframe 2: header @ 0x02 or 0x06, 28 nibbles (low) @ 0x11,15,19,1d,21 ... 7d
|
||||
* ...
|
||||
* subframe 7: header @ 0x0b or 0x0f, 28 nibbles (high) @ 0x13,17,1b,1f,23 ... 7f
|
||||
*/
|
||||
|
||||
/* external interleave (fixed size), mono/stereo */
|
||||
bytes_per_frame = 0x80;
|
||||
samples_per_frame = 28*8 / channelspacing;
|
||||
samples_per_frame = 28*subframes / channelspacing;
|
||||
frames_in = first_sample / samples_per_frame;
|
||||
first_sample = first_sample % samples_per_frame;
|
||||
|
||||
|
@ -84,25 +96,27 @@ void decode_xa(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, i
|
|||
frame_offset = stream->offset + bytes_per_frame * frames_in;
|
||||
read_streamfile(frame, frame_offset, bytes_per_frame, stream->streamfile); /* ignore EOF errors */
|
||||
|
||||
VGM_ASSERT(get_32bitBE(frame+0x0) != get_32bitBE(frame+0x4) || get_32bitBE(frame+0x8) != get_32bitBE(frame+0xC),
|
||||
VGM_ASSERT(get_u32be(frame+0x0) != get_u32be(frame+0x4) || get_u32be(frame+0x8) != get_u32be(frame+0xC),
|
||||
"bad frames at %x\n", (uint32_t)frame_offset);
|
||||
|
||||
|
||||
/* decode subframes */
|
||||
for (i = 0; i < 8 / channelspacing; i++) {
|
||||
for (i = 0; i < subframes / channelspacing; i++) {
|
||||
int32_t coef1, coef2;
|
||||
uint8_t coef_index, shift_factor;
|
||||
|
||||
/* parse current subframe (sound unit)'s header (sound parameters) */
|
||||
sp_pos = 0x04 + i*channelspacing + channel;
|
||||
sp_pos = is_xa8 ?
|
||||
i*channelspacing + channel:
|
||||
0x04 + i*channelspacing + channel;
|
||||
coef_index = (frame[sp_pos] >> 4) & 0xf;
|
||||
shift_factor = (frame[sp_pos] >> 0) & 0xf;
|
||||
|
||||
VGM_ASSERT(coef_index > 4 || shift_factor > 12, "XA: incorrect coefs/shift at %x\n", (uint32_t)frame_offset + sp_pos);
|
||||
/* mastered values like 0xFF exist [Micro Machines (CDi), demo and release] */
|
||||
VGM_ASSERT(coef_index > 4 || shift_factor > (is_xa8 ? 8 : 12), "XA: incorrect coefs/shift at %x\n", (uint32_t)frame_offset + sp_pos);
|
||||
if (coef_index > 4)
|
||||
coef_index = 0; /* only 4 filters are used, rest is apparently 0 */
|
||||
if (shift_factor > 12)
|
||||
shift_factor = 9; /* supposedly, from Nocash PSX docs */
|
||||
if (shift_factor > (is_xa8 ? 8 : 12))
|
||||
shift_factor = (is_xa8 ? 8 : 9); /* supposedly, from Nocash PSX docs (in 8-bit mode max range should be 8 though) */
|
||||
|
||||
coef1 = IK0[coef_index];
|
||||
coef2 = IK1[coef_index];
|
||||
|
@ -110,15 +124,7 @@ void decode_xa(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, i
|
|||
|
||||
/* decode subframe nibbles */
|
||||
for(j = 0; j < 28; j++) {
|
||||
uint8_t nibbles;
|
||||
int32_t new_sample;
|
||||
|
||||
int su_pos = (channelspacing==1) ?
|
||||
0x10 + j*0x04 + (i/2) : /* mono */
|
||||
0x10 + j*0x04 + i; /* stereo */
|
||||
int get_high_nibble = (channelspacing==1) ?
|
||||
(i&1) : /* mono (even subframes = low, off subframes = high) */
|
||||
(channel == 1); /* stereo (L channel / even subframes = low, R channel / odd subframes = high) */
|
||||
int32_t sample;
|
||||
|
||||
/* skip half decodes to make sure hist isn't touched (kinda hack-ish) */
|
||||
if (!(sample_count >= first_sample && samples_done < samples_to_do)) {
|
||||
|
@ -126,22 +132,39 @@ void decode_xa(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, i
|
|||
continue;
|
||||
}
|
||||
|
||||
nibbles = frame[su_pos];
|
||||
if (is_xa8) {
|
||||
int su_pos = (channelspacing==1) ?
|
||||
0x10 + j*0x04 + i : /* mono */
|
||||
0x10 + j*0x04 + i*2 + channel; /* stereo */
|
||||
|
||||
new_sample = get_high_nibble ?
|
||||
(nibbles >> 4) & 0x0f :
|
||||
(nibbles >> 0) & 0x0f;
|
||||
sample = frame[su_pos];
|
||||
sample = (int16_t)((sample << 8) & 0xff00) >> shift_factor; /* 16b sign extend + scale */
|
||||
}
|
||||
else {
|
||||
uint8_t nibbles;
|
||||
int su_pos = (channelspacing==1) ?
|
||||
0x10 + j*0x04 + (i/2) : /* mono */
|
||||
0x10 + j*0x04 + i; /* stereo */
|
||||
int get_high_nibble = (channelspacing==1) ?
|
||||
(i&1) : /* mono (even subframes = low, off subframes = high) */
|
||||
(channel == 1); /* stereo (L channel / even subframes = low, R channel / odd subframes = high) */
|
||||
|
||||
new_sample = (int16_t)((new_sample << 12) & 0xf000) >> shift_factor; /* 16b sign extend + scale */
|
||||
new_sample = new_sample << 4;
|
||||
new_sample = new_sample - ((coef1*hist1 + coef2*hist2) >> 10);
|
||||
nibbles = frame[su_pos];
|
||||
sample = get_high_nibble ?
|
||||
(nibbles >> 4) & 0x0f :
|
||||
(nibbles >> 0) & 0x0f;
|
||||
sample = (int16_t)((sample << 12) & 0xf000) >> shift_factor; /* 16b sign extend + scale */
|
||||
}
|
||||
|
||||
sample = sample << 4; /* scale for current IK */
|
||||
sample = sample - ((coef1*hist1 + coef2*hist2) >> 10);
|
||||
|
||||
hist2 = hist1;
|
||||
hist1 = new_sample; /* must go before clamp, somehow */
|
||||
new_sample = new_sample >> 4;
|
||||
new_sample = clamp16(new_sample);
|
||||
hist1 = sample; /* must go before clamp, somehow */
|
||||
sample = sample >> 4;
|
||||
sample = clamp16(sample);
|
||||
|
||||
outbuf[samples_done * channelspacing] = new_sample;
|
||||
outbuf[samples_done * channelspacing] = sample;
|
||||
samples_done++;
|
||||
|
||||
sample_count++;
|
||||
|
@ -152,11 +175,13 @@ void decode_xa(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, i
|
|||
stream->adpcm_history2_32 = hist2;
|
||||
}
|
||||
|
||||
size_t xa_bytes_to_samples(size_t bytes, int channels, int is_blocked, int is_form2) {
|
||||
|
||||
size_t xa_bytes_to_samples(size_t bytes, int channels, int is_blocked, int is_form2, int bps) {
|
||||
int subframes = (bps == 8) ? 4 : 8;
|
||||
if (is_blocked) {
|
||||
return (bytes / 0x930) * (28*8/ channels) * (is_form2 ? 18 : 16);
|
||||
return (bytes / 0x930) * (28*subframes/ channels) * (is_form2 ? 18 : 16);
|
||||
}
|
||||
else {
|
||||
return (bytes / 0x80) * (28*8 / channels);
|
||||
return (bytes / 0x80) * (28*subframes / channels);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
@ -129,6 +133,10 @@ void seek_codec(VGMSTREAM* vgmstream) {
|
|||
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) {
|
||||
seek_ubi_adpcm(vgmstream->codec_data, vgmstream->loop_current_sample);
|
||||
}
|
||||
|
@ -231,6 +239,10 @@ void reset_codec(VGMSTREAM* vgmstream) {
|
|||
reset_hca(vgmstream->codec_data);
|
||||
}
|
||||
|
||||
if (vgmstream->coding_type == coding_TAC) {
|
||||
reset_tac(vgmstream->codec_data);
|
||||
}
|
||||
|
||||
if (vgmstream->coding_type == coding_UBI_ADPCM) {
|
||||
reset_ubi_adpcm(vgmstream->codec_data);
|
||||
}
|
||||
|
@ -427,6 +439,8 @@ int get_vgmstream_samples_per_frame(VGMSTREAM* vgmstream) {
|
|||
|
||||
case coding_XA:
|
||||
return 28*8 / vgmstream->channels; /* 8 subframes per frame, mono/stereo */
|
||||
case coding_XA8:
|
||||
return 28*4 / vgmstream->channels; /* 4 subframes per frame, mono/stereo */
|
||||
case coding_PSX:
|
||||
case coding_PSX_badflags:
|
||||
case coding_HEVAG:
|
||||
|
@ -509,6 +523,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;
|
||||
|
@ -635,6 +651,7 @@ int get_vgmstream_frame_size(VGMSTREAM* vgmstream) {
|
|||
return 0x00; /* variable (block-controlled) */
|
||||
|
||||
case coding_XA:
|
||||
case coding_XA8:
|
||||
return 0x80;
|
||||
case coding_PSX:
|
||||
case coding_PSX_badflags:
|
||||
|
@ -705,27 +722,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;
|
||||
}
|
||||
}
|
||||
|
@ -982,11 +988,14 @@ void decode_vgmstream(VGMSTREAM* vgmstream, int samples_written, int samples_to_
|
|||
}
|
||||
break;
|
||||
case coding_XA:
|
||||
case coding_XA8: {
|
||||
int is_xa8 = (vgmstream->coding_type == coding_XA8);
|
||||
for (ch = 0; ch < vgmstream->channels; ch++) {
|
||||
decode_xa(&vgmstream->ch[ch], buffer+ch,
|
||||
vgmstream->channels, vgmstream->samples_into_block, samples_to_do, ch);
|
||||
vgmstream->channels, vgmstream->samples_into_block, samples_to_do, ch, is_xa8);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case coding_EA_XA:
|
||||
case coding_EA_XA_int: {
|
||||
int is_stereo = (vgmstream->channels > 1 && vgmstream->coding_type == coding_EA_XA);
|
||||
|
@ -1038,6 +1047,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);
|
||||
|
|
|
@ -194,6 +194,7 @@ static const char* extension_list[] = {
|
|||
"genh",
|
||||
"gin",
|
||||
"gms",
|
||||
"grn",
|
||||
"gsb",
|
||||
"gsf",
|
||||
"gtd",
|
||||
|
@ -250,6 +251,7 @@ static const char* extension_list[] = {
|
|||
"kcey", //fake extension/header id for .pcm (renamed, to be removed)
|
||||
"km9",
|
||||
"kovs", //fake extension/header id for .kvs
|
||||
"kno",
|
||||
"kns",
|
||||
"kraw",
|
||||
"ktac",
|
||||
|
@ -717,6 +719,7 @@ static const coding_info coding_info_list[] = {
|
|||
{coding_G721, "CCITT G.721 4-bit ADPCM"},
|
||||
|
||||
{coding_XA, "CD-ROM XA 4-bit ADPCM"},
|
||||
{coding_XA8, "CD-ROM XA 8-bit ADPCM"},
|
||||
{coding_PSX, "Playstation 4-bit ADPCM"},
|
||||
{coding_PSX_badflags, "Playstation 4-bit ADPCM (bad flags)"},
|
||||
{coding_PSX_cfg, "Playstation 4-bit ADPCM (configurable)"},
|
||||
|
@ -803,7 +806,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 +1342,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) {
|
||||
|
|
|
@ -3,12 +3,12 @@
|
|||
#include "../vgmstream.h"
|
||||
|
||||
/* parse a CD-XA raw mode2/form2 sector */
|
||||
void block_update_xa(off_t block_offset, VGMSTREAM * vgmstream) {
|
||||
STREAMFILE* streamFile = vgmstream->ch[0].streamfile;
|
||||
void block_update_xa(off_t block_offset, VGMSTREAM* vgmstream) {
|
||||
STREAMFILE* sf = vgmstream->ch[0].streamfile;
|
||||
int i, is_audio;
|
||||
size_t block_samples;
|
||||
uint16_t xa_config, target_config;
|
||||
uint8_t xa_submode;
|
||||
uint8_t xa_submode, xa_header;
|
||||
|
||||
|
||||
/* XA mode2/form2 sector, size 0x930
|
||||
|
@ -22,8 +22,29 @@ void block_update_xa(off_t block_offset, VGMSTREAM * vgmstream) {
|
|||
* 0x930: end
|
||||
* Sectors with no data may exist near other with data
|
||||
*/
|
||||
xa_config = read_u16be(block_offset + 0x10, streamFile);
|
||||
target_config = vgmstream->codec_config;
|
||||
xa_config = read_u16be(block_offset + 0x10, sf); /* file + channel */
|
||||
|
||||
/* submode flag bits (typical audio value = 0x64 01100100)
|
||||
* - 7 (0x80 10000000): end of file (usually at last sector of a channel or at data end)
|
||||
* - 6 (0x40 01000000): real time sector (special control flag)
|
||||
* - 5 (0x20 00100000): sector form (0=form1, 1=form2)
|
||||
* - 4 (0x10 00010000): trigger (generates interrupt for the application)
|
||||
* - 3 (0x08 00001000): data sector
|
||||
* - 2 (0x04 00000100): audio sector
|
||||
* - 1 (0x02 00000010): video sector
|
||||
* - 0 (0x01 00000001): end of audio (optional for non-real time XAs)
|
||||
* Empty sectors with no flags may exist interleaved with other with audio/data.
|
||||
*/
|
||||
xa_submode = read_u8(block_offset + 0x12, sf);
|
||||
|
||||
/* header bits:
|
||||
* - 7 (0x80 10000000): reserved
|
||||
* - 6 (0x40 01000000): emphasis (applies filter, same as CD-DA emphasis)
|
||||
* - 4 (0x30 00110000): bits per sample (0=4-bit, 1=8-bit)
|
||||
* - 2 (0x0C 00001100): sample rate (0=37.8hz, 1=18.9hz)
|
||||
* - 0 (0x03 00000011): channels (0=mono, 1=stereo)
|
||||
*/
|
||||
xa_header = read_u8(block_offset + 0x13, sf);
|
||||
|
||||
/* Sector subheader's file+channel markers are used to interleave streams (music/sfx/voices)
|
||||
* by reading one target file+channel while ignoring the rest. This is needed to adjust
|
||||
|
@ -38,36 +59,23 @@ void block_update_xa(off_t block_offset, VGMSTREAM * vgmstream) {
|
|||
* Extractors deinterleave and split .xa using file + channel + EOF flags.
|
||||
* 'Channel' here doesn't mean "audio channel", just a fancy name for substreams (mono or stereo).
|
||||
* Files can go up to 255, normally file 0=sequential, 1+=interleaved */
|
||||
|
||||
|
||||
/* submode flag bits (typical audio value = 0x64 01100100)
|
||||
* - 7 (0x80 10000000): end of file (usually at last sector of a channel or at data end)
|
||||
* - 6 (0x40 01000000): real time sector (special control flag)
|
||||
* - 5 (0x20 00100000): sector form (0=form1, 1=form2)
|
||||
* - 4 (0x10 00010000): trigger (generates interrupt for the application)
|
||||
* - 3 (0x08 00001000): data sector
|
||||
* - 2 (0x04 00000100): audio sector
|
||||
* - 1 (0x02 00000010): video sector
|
||||
* - 0 (0x01 00000001): end of audio (optional for non-real time XAs)
|
||||
* Empty sectors with no flags may exist interleaved with other with audio/data.
|
||||
*/
|
||||
xa_submode = read_u8(block_offset + 0x12,streamFile);
|
||||
target_config = vgmstream->codec_config;
|
||||
|
||||
/* audio sector must set/not set certain flags, as per spec (in theory form2 only too) */
|
||||
is_audio = !(xa_submode & 0x08) && (xa_submode & 0x04) && !(xa_submode & 0x02);
|
||||
|
||||
|
||||
if (xa_config != target_config) {
|
||||
block_samples = 0; /* not a target sector */
|
||||
}
|
||||
else if (is_audio) {
|
||||
int subframes = ((xa_header >> 4) & 3) == 1 ? 4 : 8; /* 8-bit mode = 4 subframes */
|
||||
if (xa_submode & 0x20) {
|
||||
/* form2 audio: size 0x900, 18 frames of size 0x80 with 8 subframes of 28 samples */
|
||||
block_samples = (28*8 / vgmstream->channels) * 18;
|
||||
/* form2 audio: size 0x900, 18 frames of size 0x80 with N subframes of 28 samples */
|
||||
block_samples = (28*subframes / vgmstream->channels) * 18;
|
||||
}
|
||||
else { /* rare, found with empty audio [Glint Glitters (PS1), Dance! Dance! Dance! (PS1)] */
|
||||
/* form1 audio: size 0x800, 16 frames of size 0x80 with 8 subframes of 28 samples (rest is garbage/other data) */
|
||||
block_samples = (28*8 / vgmstream->channels) * 16;
|
||||
/* form1 audio: size 0x800, 16 frames of size 0x80 with N subframes of 28 samples (rest is garbage/other data) */
|
||||
block_samples = (28*subframes / vgmstream->channels) * 16;
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
|
|
@ -11,11 +11,15 @@
|
|||
#define ADX_KEY_MAX_TEST_FRAMES 32768
|
||||
#define ADX_KEY_TEST_BUFFER_SIZE 0x8000
|
||||
|
||||
static int find_adx_key(STREAMFILE *sf, uint8_t type, uint16_t *xor_start, uint16_t *xor_mult, uint16_t *xor_add);
|
||||
static int find_adx_key(STREAMFILE *sf, uint8_t type, uint16_t *xor_start, uint16_t *xor_mult, uint16_t *xor_add, uint16_t subkey);
|
||||
|
||||
VGMSTREAM* init_vgmstream_adx(STREAMFILE* sf) {
|
||||
return init_vgmstream_adx_subkey(sf, 0);
|
||||
}
|
||||
|
||||
/* ADX - CRI Middleware format */
|
||||
VGMSTREAM * init_vgmstream_adx(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
VGMSTREAM* init_vgmstream_adx_subkey(STREAMFILE* sf, uint16_t subkey) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
off_t start_offset, hist_offset = 0;
|
||||
int loop_flag = 0, channel_count;
|
||||
int32_t loop_start_sample = 0, loop_end_sample = 0;
|
||||
|
@ -32,18 +36,18 @@ VGMSTREAM * init_vgmstream_adx(STREAMFILE *streamFile) {
|
|||
/* checks*/
|
||||
/* .adx: standard
|
||||
* .adp: Headhunter (DC) */
|
||||
if (!check_extensions(streamFile,"adx,adp"))
|
||||
if (!check_extensions(sf,"adx,adp"))
|
||||
goto fail;
|
||||
|
||||
if ((uint16_t)read_16bitBE(0x00,streamFile) != 0x8000)
|
||||
if (read_u16be(0x00,sf) != 0x8000)
|
||||
goto fail;
|
||||
|
||||
start_offset = (uint16_t)read_16bitBE(0x02,streamFile) + 0x04;
|
||||
if ((uint16_t)read_16bitBE(start_offset - 0x06,streamFile) != 0x2863 || /* "(c" */
|
||||
(uint32_t)read_32bitBE(start_offset - 0x04,streamFile) != 0x29435249) /* ")CRI" */
|
||||
start_offset = read_u16be(0x02,sf) + 0x04;
|
||||
if (read_u16be(start_offset - 0x06,sf) != 0x2863 || /* "(c" */
|
||||
read_u32be(start_offset - 0x04,sf) != 0x29435249) /* ")CRI" */
|
||||
goto fail;
|
||||
|
||||
encoding_type = read_8bit(0x04, streamFile);
|
||||
encoding_type = read_u8(0x04, sf);
|
||||
switch (encoding_type) {
|
||||
case 0x02:
|
||||
coding_type = coding_CRI_ADX_fixed;
|
||||
|
@ -60,29 +64,29 @@ VGMSTREAM * init_vgmstream_adx(STREAMFILE *streamFile) {
|
|||
|
||||
/* ADX encoders can't set this value, but is honored by ADXPlay if changed and multiple of 0x12,
|
||||
* though output is unusual and may not be fully supported (works in mono so not an interleave) */
|
||||
frame_size = read_8bit(0x05, streamFile);
|
||||
frame_size = read_8bit(0x05, sf);
|
||||
|
||||
if (read_8bit(0x06,streamFile) != 4) /* bits per sample */
|
||||
if (read_u8(0x06,sf) != 4) /* bits per sample */
|
||||
goto fail;
|
||||
|
||||
/* older ADX (adxencd) up to 2ch, newer ADX (criatomencd) up to 8 */
|
||||
channel_count = read_8bit(0x07,streamFile);
|
||||
channel_count = read_u8(0x07,sf);
|
||||
/* 0x08: sample rate */
|
||||
/* 0x0c: samples */
|
||||
/* 0x10: high-pass frequency */
|
||||
|
||||
version = read_16bitBE(0x12,streamFile);
|
||||
version = read_u16be(0x12,sf);
|
||||
|
||||
/* encryption */
|
||||
if (version == 0x0408) {
|
||||
if (find_adx_key(streamFile, 8, &xor_start, &xor_mult, &xor_add)) {
|
||||
if (find_adx_key(sf, 8, &xor_start, &xor_mult, &xor_add, 0)) {
|
||||
coding_type = coding_CRI_ADX_enc_8;
|
||||
version = 0x0400;
|
||||
}
|
||||
VGM_ASSERT(version != 0x0400, "ADX: keystring not found\n");
|
||||
}
|
||||
else if (version == 0x0409) {
|
||||
if (find_adx_key(streamFile, 9, &xor_start, &xor_mult, &xor_add)) {
|
||||
if (find_adx_key(sf, 9, &xor_start, &xor_mult, &xor_add, subkey)) {
|
||||
coding_type = coding_CRI_ADX_enc_9;
|
||||
version = 0x0400;
|
||||
}
|
||||
|
@ -103,11 +107,11 @@ VGMSTREAM * init_vgmstream_adx(STREAMFILE *streamFile) {
|
|||
/* 0x00 (2): initial loop padding (the encoder adds a few blank samples so loop start is block-aligned; max 31)
|
||||
* ex. loop_start=12: enc_start=32, padding=20 (32-20=12); loop_start=35: enc_start=64, padding=29 (64-29=35)
|
||||
* 0x02 (2): loop sample(?) flag (always 1) */
|
||||
loop_flag = read_32bitBE(loops_offset+0x04,streamFile) != 0; /* loop offset(?) flag (always 1) */
|
||||
loop_start_sample = read_32bitBE(loops_offset+0x08,streamFile);
|
||||
//loop_start_offset = read_32bitBE(loops_offset+0x0c,streamFile);
|
||||
loop_end_sample = read_32bitBE(loops_offset+0x10,streamFile);
|
||||
//loop_end_offset = read_32bitBE(loops_offset+0x14,streamFile);
|
||||
loop_flag = read_32bitBE(loops_offset+0x04,sf) != 0; /* loop offset(?) flag (always 1) */
|
||||
loop_start_sample = read_32bitBE(loops_offset+0x08,sf);
|
||||
//loop_start_offset = read_32bitBE(loops_offset+0x0c,sf);
|
||||
loop_end_sample = read_32bitBE(loops_offset+0x10,sf);
|
||||
//loop_end_offset = read_32bitBE(loops_offset+0x14,sf);
|
||||
}
|
||||
}
|
||||
else if (version == 0x0400) { /* common */
|
||||
|
@ -120,8 +124,8 @@ VGMSTREAM * init_vgmstream_adx(STREAMFILE *streamFile) {
|
|||
hist_size = (channel_count > 1 ? 0x04 * channel_count : 0x04 + 0x04); /* min is 8, even in 1ch files */
|
||||
|
||||
ainf_offset = base_size + hist_size + 0x04; /* not seen with >2ch though */
|
||||
if ((uint32_t)read_32bitBE(ainf_offset+0x00,streamFile) == 0x41494E46) /* "AINF" */
|
||||
ainf_size = read_32bitBE(ainf_offset+0x04,streamFile);
|
||||
if (read_u32be(ainf_offset+0x00,sf) == 0x41494E46) /* "AINF" */
|
||||
ainf_size = read_32bitBE(ainf_offset+0x04,sf);
|
||||
|
||||
if (start_offset - ainf_size - 0x06 >= hist_offset + hist_size + loops_size) { /* enough space for loop info? */
|
||||
off_t loops_offset = base_size + hist_size;
|
||||
|
@ -129,11 +133,11 @@ VGMSTREAM * init_vgmstream_adx(STREAMFILE *streamFile) {
|
|||
/* 0x00 (2): initial loop padding (the encoder adds a few blank samples so loop start is block-aligned; max 31)
|
||||
* ex. loop_start=12: enc_start=32, padding=20 (32-20=12); loop_start=35: enc_start=64, padding=29 (64-29=35)
|
||||
* 0x02 (2): loop sample(?) flag (always 1) */
|
||||
loop_flag = read_32bitBE(loops_offset+0x04,streamFile) != 0; /* loop offset(?) flag (always 1) */
|
||||
loop_start_sample = read_32bitBE(loops_offset+0x08,streamFile);
|
||||
//loop_start_offset = read_32bitBE(loops_offset+0x0c,streamFile);
|
||||
loop_end_sample = read_32bitBE(loops_offset+0x10,streamFile);
|
||||
//loop_end_offset = read_32bitBE(loops_offset+0x14,streamFile);
|
||||
loop_flag = read_32bitBE(loops_offset+0x04,sf) != 0; /* loop offset(?) flag (always 1) */
|
||||
loop_start_sample = read_32bitBE(loops_offset+0x08,sf);
|
||||
//loop_start_offset = read_32bitBE(loops_offset+0x0c,sf);
|
||||
loop_end_sample = read_32bitBE(loops_offset+0x10,sf);
|
||||
//loop_end_offset = read_32bitBE(loops_offset+0x14,sf);
|
||||
}
|
||||
|
||||
/* AINF header info (may be inserted by CRI's tools but is rarely used)
|
||||
|
@ -165,8 +169,8 @@ VGMSTREAM * init_vgmstream_adx(STREAMFILE *streamFile) {
|
|||
vgmstream = allocate_vgmstream(channel_count,loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->sample_rate = read_32bitBE(0x08,streamFile);
|
||||
vgmstream->num_samples = read_32bitBE(0x0c,streamFile);
|
||||
vgmstream->sample_rate = read_32bitBE(0x08,sf);
|
||||
vgmstream->num_samples = read_32bitBE(0x0c,sf);
|
||||
vgmstream->loop_start_sample = loop_start_sample;
|
||||
vgmstream->loop_end_sample = loop_end_sample;
|
||||
|
||||
|
@ -195,7 +199,7 @@ VGMSTREAM * init_vgmstream_adx(STREAMFILE *streamFile) {
|
|||
double x,y,z,a,b,c;
|
||||
int i;
|
||||
/* high-pass cutoff frequency, always 500 that I've seen */
|
||||
uint16_t cutoff = (uint16_t)read_16bitBE(0x10,streamFile);
|
||||
uint16_t cutoff = read_u16be(0x10,sf);
|
||||
|
||||
x = cutoff;
|
||||
y = vgmstream->sample_rate;
|
||||
|
@ -222,8 +226,8 @@ VGMSTREAM * init_vgmstream_adx(STREAMFILE *streamFile) {
|
|||
/* 2 hist shorts per ch, corresponding to the very first original sample repeated (verified with CRI's encoders).
|
||||
* Not vital as their effect is small, after a few samples they don't matter, and most songs start in silence. */
|
||||
if (hist_offset) {
|
||||
vgmstream->ch[i].adpcm_history1_32 = read_16bitBE(hist_offset + i*4 + 0x00,streamFile);
|
||||
vgmstream->ch[i].adpcm_history2_32 = read_16bitBE(hist_offset + i*4 + 0x02,streamFile);
|
||||
vgmstream->ch[i].adpcm_history1_32 = read_16bitBE(hist_offset + i*4 + 0x00,sf);
|
||||
vgmstream->ch[i].adpcm_history2_32 = read_16bitBE(hist_offset + i*4 + 0x02,sf);
|
||||
}
|
||||
|
||||
if (coding_type == coding_CRI_ADX_enc_8 || coding_type == coding_CRI_ADX_enc_9) {
|
||||
|
@ -240,7 +244,7 @@ VGMSTREAM * init_vgmstream_adx(STREAMFILE *streamFile) {
|
|||
}
|
||||
|
||||
|
||||
if ( !vgmstream_open_stream(vgmstream,streamFile,start_offset) )
|
||||
if ( !vgmstream_open_stream(vgmstream, sf, start_offset) )
|
||||
goto fail;
|
||||
return vgmstream;
|
||||
|
||||
|
@ -252,7 +256,7 @@ fail:
|
|||
|
||||
/* ADX key detection works by reading XORed ADPCM scales in frames, and un-XORing with keys in
|
||||
* a list. If resulting values are within the expected range for N scales we accept that key. */
|
||||
static int find_adx_key(STREAMFILE *sf, uint8_t type, uint16_t *xor_start, uint16_t *xor_mult, uint16_t *xor_add) {
|
||||
static int find_adx_key(STREAMFILE* sf, uint8_t type, uint16_t *xor_start, uint16_t *xor_mult, uint16_t *xor_add, uint16_t subkey) {
|
||||
const int frame_size = 0x12;
|
||||
uint16_t *scales = NULL;
|
||||
uint16_t *prescales = NULL;
|
||||
|
@ -295,7 +299,17 @@ static int find_adx_key(STREAMFILE *sf, uint8_t type, uint16_t *xor_start, uint1
|
|||
return 1;
|
||||
}
|
||||
else if (type == 9 && key_size == 0x08) {
|
||||
uint64_t keycode = (uint64_t)get_64bitBE(keybuf);
|
||||
uint64_t keycode = get_u64be(keybuf);
|
||||
if (subkey) {
|
||||
keycode = keycode * ( ((uint64_t)subkey << 16u) | ((uint16_t)~subkey + 2u) );
|
||||
}
|
||||
derive_adx_key9(keycode, xor_start, xor_mult, xor_add);
|
||||
return 1;
|
||||
}
|
||||
else if (type == 9 && key_size == 0x08+0x02) {
|
||||
uint64_t file_key = get_u64be(keybuf+0x00);
|
||||
uint16_t file_sub = get_u16be(keybuf+0x08);
|
||||
uint64_t keycode = file_key * ( ((uint64_t)file_sub << 16u) | ((uint16_t)~file_sub + 2u) );
|
||||
derive_adx_key9(keycode, xor_start, xor_mult, xor_add);
|
||||
return 1;
|
||||
}
|
||||
|
@ -414,7 +428,11 @@ static int find_adx_key(STREAMFILE *sf, uint8_t type, uint16_t *xor_start, uint1
|
|||
derive_adx_key8(keys[key_id].key8, &key_xor, &key_mul, &key_add);
|
||||
}
|
||||
else if (type == 9 && keys[key_id].key9) {
|
||||
derive_adx_key9(keys[key_id].key9, &key_xor, &key_mul, &key_add);
|
||||
uint64_t keycode = keys[key_id].key9;
|
||||
if (subkey) {
|
||||
keycode = keycode * ( ((uint64_t)subkey << 16u) | ((uint16_t)~subkey + 2u) );
|
||||
}
|
||||
derive_adx_key9(keycode, &key_xor, &key_mul, &key_add);
|
||||
}
|
||||
else {
|
||||
VGM_LOG("ADX: incorrectly defined key id=%i\n", key_id);
|
||||
|
|
|
@ -244,7 +244,7 @@ static const adxkey_info adxkey9_list[] = {
|
|||
{0x0000,0x1c85,0x7043, NULL,29915170}, // 0000000001C87822
|
||||
|
||||
/* Assault Lily Last Bullet (Android) */
|
||||
{0x0aca,0x0ef5,0x05c9, NULL,0}, // guessed with VGAudio (possible key: 5650EF42E5 / 370725044965)
|
||||
{0x0000,0x0000,0x0000, NULL,6349046567469313}, // 00168E6C99510101 (+ AWB subkeys)
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -135,7 +135,7 @@ VGMSTREAM* init_vgmstream_awb_memory(STREAMFILE* sf, STREAMFILE* sf_acb) {
|
|||
if (!vgmstream) goto fail;
|
||||
break;
|
||||
case ADX: /* Okami HD (PS4) */
|
||||
vgmstream = init_vgmstream_adx(temp_sf);
|
||||
vgmstream = init_vgmstream_adx_subkey(temp_sf, subkey);
|
||||
if (!vgmstream) goto fail;
|
||||
break;
|
||||
case VAG: /* Ukiyo no Roushi (Vita) */
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -1,44 +1,45 @@
|
|||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
|
||||
VGMSTREAM * init_vgmstream_ktss(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
int loop_flag, channel_count, sample_rate;
|
||||
int8_t version, num_layers, codec_id;
|
||||
VGMSTREAM* init_vgmstream_ktss(STREAMFILE* sf) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
int loop_flag, channels, sample_rate;
|
||||
int8_t version, tracks, codec_id;
|
||||
int32_t num_samples, loop_start, loop_length, coef_offset, coef_spacing;
|
||||
off_t start_offset;
|
||||
size_t data_size, skip = 0;
|
||||
|
||||
if (!check_extensions(streamFile, "kns,ktss"))
|
||||
/* checks */
|
||||
/* .kns: Atelier Lydie & Suelle: The Alchemists and the Mysterious Paintings (Switch)
|
||||
* .kno: Ciel Nosurge DX (Switch)
|
||||
* .ktss: header id */
|
||||
if (!check_extensions(sf, "kns,kno,ktss"))
|
||||
goto fail;
|
||||
|
||||
if (read_32bitBE(0x00, streamFile) != 0x4B545353) /* "KTSS" */
|
||||
if (!is_id32be(0x00,sf, "KTSS"))
|
||||
goto fail;
|
||||
/* 0x04: data size */
|
||||
|
||||
codec_id = read_8bit(0x20, streamFile);
|
||||
codec_id = read_u8(0x20, sf);
|
||||
/* 0x01: null, part of codec? */
|
||||
version = read_8bit(0x22, streamFile);
|
||||
version = read_u8(0x22, sf);
|
||||
/* 0x03: same as version? */
|
||||
start_offset = read_32bitLE(0x24, streamFile) + 0x20;
|
||||
start_offset = read_u32le(0x24, sf) + 0x20;
|
||||
|
||||
// A layered stream/track model seems to be used in Hyrule Warriors (Switch).
|
||||
// It's also present in other Koei Tecmo KNS but the channel count was always
|
||||
// explicitly defined in the 0x29 byte and the number of layers was set to 1.
|
||||
// Here, 10 channel files are set up with 2 channels in 5 layers.
|
||||
// Super hacky on KT's part and ours to implement but it works.
|
||||
num_layers = read_8bit(0x28, streamFile);
|
||||
channel_count = read_8bit(0x29, streamFile) * num_layers;
|
||||
sample_rate = read_32bitLE(0x2c, streamFile);
|
||||
/* Layered tracks used in Hyrule Warriors (Switch), like 2*5 = 10 channels
|
||||
* Other games typically use 1 track. */
|
||||
tracks = read_u8(0x28, sf);
|
||||
channels = read_u8(0x29, sf) * tracks;
|
||||
sample_rate = read_u32le(0x2c, sf);
|
||||
|
||||
num_samples = read_32bitLE(0x30, streamFile);
|
||||
loop_start = read_32bitLE(0x34, streamFile);
|
||||
loop_length = read_32bitLE(0x38, streamFile);
|
||||
num_samples = read_s32le(0x30, sf);
|
||||
loop_start = read_s32le(0x34, sf);
|
||||
loop_length = read_s32le(0x38, sf);
|
||||
loop_flag = loop_length > 0;
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channel_count, loop_flag);
|
||||
vgmstream = allocate_vgmstream(channels, loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
|
@ -64,32 +65,32 @@ VGMSTREAM * init_vgmstream_ktss(STREAMFILE *streamFile) {
|
|||
vgmstream->coding_type = coding_NGC_DSP;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = 0x8;
|
||||
dsp_read_coefs_le(vgmstream, streamFile, coef_offset, coef_spacing);
|
||||
dsp_read_coefs_le(vgmstream, sf, coef_offset, coef_spacing);
|
||||
break;
|
||||
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
case 0x9: { /* Opus - Dead or Alive Xtreme 3: Scarlet, Fire Emblem: Three Houses */
|
||||
opus_config cfg = {0};
|
||||
|
||||
start_offset = read_32bitLE(0x40, streamFile); /* after seek table, if any */
|
||||
data_size = read_32bitLE(0x44, streamFile);
|
||||
start_offset = read_u32le(0x40, sf); /* after seek table, if any */
|
||||
data_size = read_u32le(0x44, sf);
|
||||
/* 0x48: seek table start (0 if no seek table) */
|
||||
/* 0x4c: number of frames */
|
||||
|
||||
/* 0x50: frame size, or 0 if VBR */
|
||||
/* 0x52: samples per frame */
|
||||
/* 0x54: sample rate */
|
||||
cfg.skip = read_32bitLE(0x58, streamFile);
|
||||
cfg.skip = read_s32le(0x58, sf);
|
||||
cfg.sample_rate = vgmstream->sample_rate;
|
||||
cfg.channels = vgmstream->channels;
|
||||
|
||||
/* this info seems always included even for stereo streams */
|
||||
if (vgmstream->channels <= 8) {
|
||||
int i;
|
||||
cfg.stream_count = read_8bit(0x5a,streamFile);
|
||||
cfg.coupled_count = read_8bit(0x5b,streamFile);
|
||||
cfg.stream_count = read_u8(0x5a,sf);
|
||||
cfg.coupled_count = read_u8(0x5b,sf);
|
||||
for (i = 0; i < vgmstream->channels; i++) {
|
||||
cfg.channel_mapping[i] = read_8bit(0x5c + i,streamFile);
|
||||
cfg.channel_mapping[i] = read_u8(0x5c + i,sf);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -97,7 +98,7 @@ VGMSTREAM * init_vgmstream_ktss(STREAMFILE *streamFile) {
|
|||
/* 0x70: seek table (0x02 * frames) if VBR */
|
||||
/* later games use VBR frames, hence the seek table [Warriors Orochi 4 Ultimate DLC (Switch)] */
|
||||
|
||||
vgmstream->codec_data = init_ffmpeg_switch_opus_config(streamFile, start_offset, data_size, &cfg);
|
||||
vgmstream->codec_data = init_ffmpeg_switch_opus_config(sf, start_offset, data_size, &cfg);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->coding_type = coding_FFmpeg;
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
@ -115,7 +116,7 @@ VGMSTREAM * init_vgmstream_ktss(STREAMFILE *streamFile) {
|
|||
break;
|
||||
}
|
||||
if (vgmstream->num_samples == 0) {
|
||||
vgmstream->num_samples = switch_opus_get_samples(start_offset, data_size, streamFile) - skip;
|
||||
vgmstream->num_samples = switch_opus_get_samples(start_offset, data_size, sf) - skip;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -124,7 +125,7 @@ VGMSTREAM * init_vgmstream_ktss(STREAMFILE *streamFile) {
|
|||
goto fail;
|
||||
}
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream, streamFile, start_offset))
|
||||
if (!vgmstream_open_stream(vgmstream, sf, start_offset))
|
||||
goto fail;
|
||||
return vgmstream;
|
||||
|
||||
|
|
|
@ -6,7 +6,8 @@
|
|||
VGMSTREAM* init_vgmstream_silence(int channels, int sample_rate, int32_t num_samples);
|
||||
|
||||
|
||||
VGMSTREAM * init_vgmstream_adx(STREAMFILE *streamFile);
|
||||
VGMSTREAM* init_vgmstream_adx(STREAMFILE* sf);
|
||||
VGMSTREAM* init_vgmstream_adx_subkey(STREAMFILE* sf, uint16_t subkey);
|
||||
|
||||
VGMSTREAM * init_vgmstream_afc(STREAMFILE *streamFile);
|
||||
|
||||
|
@ -944,4 +945,6 @@ VGMSTREAM* init_vgmstream_ktac(STREAMFILE* sf);
|
|||
|
||||
VGMSTREAM* init_vgmstream_mjb_mjh(STREAMFILE* sf);
|
||||
|
||||
VGMSTREAM* init_vgmstream_tac(STREAMFILE* sf);
|
||||
|
||||
#endif /*_META_H*/
|
||||
|
|
|
@ -2,18 +2,18 @@
|
|||
#include "../coding/coding.h"
|
||||
|
||||
|
||||
static void load_name(char *name, size_t name_size, STREAMFILE *sf, int big_endian, int total_subsongs, int target_subsong);
|
||||
static STREAMFILE* setup_nub_streamfile(STREAMFILE *sf, off_t header_offset, size_t header_size, off_t stream_offset, size_t stream_size, const char *fake_ext);
|
||||
static void load_name(char* name, size_t name_size, STREAMFILE* sf, int big_endian, int total_subsongs, int target_subsong);
|
||||
static STREAMFILE* setup_nub_streamfile(STREAMFILE *sf, off_t header_offset, size_t header_size, off_t stream_offset, size_t stream_size, const char* fake_ext);
|
||||
|
||||
/* .nub - Namco's nuSound2 audio container */
|
||||
VGMSTREAM * init_vgmstream_nub(STREAMFILE *streamFile) {
|
||||
VGMSTREAM *vgmstream = NULL;
|
||||
STREAMFILE *temp_sf = NULL;
|
||||
VGMSTREAM* init_vgmstream_nub(STREAMFILE* sf) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
STREAMFILE* temp_sf = NULL;
|
||||
int big_endian;
|
||||
int total_subsongs, target_subsong = streamFile->stream_index;
|
||||
int total_subsongs, target_subsong = sf->stream_index;
|
||||
uint32_t version, codec;
|
||||
const char* fake_ext;
|
||||
VGMSTREAM*(*init_vgmstream_function)(STREAMFILE *) = NULL;
|
||||
VGMSTREAM*(*init_vgmstream_function)(STREAMFILE*) = NULL;
|
||||
char name[STREAM_NAME_SIZE] = {0};
|
||||
int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL;
|
||||
|
||||
|
@ -21,19 +21,19 @@ VGMSTREAM * init_vgmstream_nub(STREAMFILE *streamFile) {
|
|||
/* checks */
|
||||
/* .nub: standard
|
||||
* .nub2: rare [iDOLM@STER - Gravure For You (PS3)] */
|
||||
if (!check_extensions(streamFile, "nub,nub2"))
|
||||
if (!check_extensions(sf, "nub,nub2"))
|
||||
goto fail;
|
||||
|
||||
version = read_32bitBE(0x00,streamFile);
|
||||
version = read_32bitBE(0x00,sf);
|
||||
if (version != 0x00020000 && /* v2.0 (rare, ex. Ridge Race 6 (X360)) */
|
||||
version != 0x00020100 && /* v2.1 (common) */
|
||||
version != 0x01020100) /* same but LE (seen in PSP/PC games, except PS4) */
|
||||
goto fail;
|
||||
if (read_32bitBE(0x04,streamFile) != 0x00000000) /* null */
|
||||
if (read_32bitBE(0x04,sf) != 0x00000000) /* null */
|
||||
goto fail;
|
||||
|
||||
/* sometimes LE [Soul Calibur: Broken Destiny (PSP), Tales of Vesperia (PS4) */
|
||||
big_endian = guess_endianness32bit(0x10, streamFile);
|
||||
big_endian = guess_endianness32bit(0x18, sf);
|
||||
if (big_endian) {
|
||||
read_32bit = read_32bitBE;
|
||||
} else{
|
||||
|
@ -48,12 +48,13 @@ VGMSTREAM * init_vgmstream_nub(STREAMFILE *streamFile) {
|
|||
|
||||
/* - base header */
|
||||
/* 0x08: file id (0 = first) */
|
||||
total_subsongs = read_32bit(0x0c, streamFile); /* .nub with 0 files do exist, and with 1 song only too */
|
||||
data_start = read_32bit(0x10, streamFile); /* exists even with 0 files */
|
||||
total_subsongs = read_32bit(0x0c, sf); /* .nub with 0 files do exist, and with 1 song only too */
|
||||
data_start = read_32bit(0x10, sf); /* exists even with 0 files */
|
||||
/* 0x14: data end (may have padding) */
|
||||
header_start = read_32bit(0x18, streamFile);
|
||||
header_start = read_32bit(0x18, sf); /* exists even with 0 files */
|
||||
/* 0x1c: header end */
|
||||
|
||||
|
||||
/* probably means "header end" in v2.0 */
|
||||
if (version == 0x00020000) {
|
||||
data_start = align_size_to_block(data_start, 0x800);
|
||||
|
@ -62,7 +63,7 @@ VGMSTREAM * init_vgmstream_nub(STREAMFILE *streamFile) {
|
|||
if (target_subsong == 0) target_subsong = 1;
|
||||
if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail;
|
||||
|
||||
offset = read_32bit(header_start + (target_subsong-1)*0x04, streamFile);
|
||||
offset = read_32bit(header_start + (target_subsong-1)*0x04, sf);
|
||||
|
||||
/* .nus have all headers first then all data, but extractors often just paste them together,
|
||||
* so we'll combine header+data on the fly to make them playable with existing parsers.
|
||||
|
@ -79,11 +80,11 @@ VGMSTREAM * init_vgmstream_nub(STREAMFILE *streamFile) {
|
|||
/* - tone header */
|
||||
/* 0x00: config? */
|
||||
/* 0x04: header id/number */
|
||||
codec = (uint32_t)read_32bit(offset + 0x08, streamFile);
|
||||
codec = (uint32_t)read_32bit(offset + 0x08, sf);
|
||||
/* 0x0c: null */
|
||||
stream_size = read_32bit(offset + 0x10, streamFile); /* 0x10 aligned */
|
||||
stream_offset = read_32bit(offset + 0x14, streamFile) + data_start;
|
||||
subheader_size = read_32bit(offset + 0x18, streamFile);
|
||||
stream_size = read_32bit(offset + 0x10, sf); /* 0x10 aligned */
|
||||
stream_offset = read_32bit(offset + 0x14, sf) + data_start;
|
||||
subheader_size = read_32bit(offset + 0x18, sf);
|
||||
/* 0x1c: extra info, size 0x10 (meaning varies but usually loop points) */
|
||||
/* rest until sub-header start looks like config/volumes/pan/etc in various floats */
|
||||
|
||||
|
@ -142,13 +143,13 @@ VGMSTREAM * init_vgmstream_nub(STREAMFILE *streamFile) {
|
|||
|
||||
//;VGM_LOG("NUB: subfile header=%lx + %x, offset=%lx + %x\n", header_offset, header_size, stream_offset, stream_size);
|
||||
|
||||
temp_sf = setup_nub_streamfile(streamFile, header_offset, header_size, stream_offset, stream_size, fake_ext);
|
||||
temp_sf = setup_nub_streamfile(sf, header_offset, header_size, stream_offset, stream_size, fake_ext);
|
||||
if (!temp_sf) goto fail;
|
||||
}
|
||||
|
||||
|
||||
/* get names from companion file, rarely [Noby Noby Boy (PS3), Wangan Midnight Maximum Tune (AC)] */
|
||||
load_name(name, sizeof(name), streamFile, big_endian, total_subsongs, target_subsong);
|
||||
load_name(name, sizeof(name), sf, big_endian, total_subsongs, target_subsong);
|
||||
|
||||
|
||||
/* init the VGMSTREAM */
|
||||
|
@ -171,7 +172,7 @@ fail:
|
|||
|
||||
/* *********************************************************** */
|
||||
|
||||
static void load_name(char *name, size_t name_size, STREAMFILE *sf, int big_endian, int total_subsongs, int target_subsong) {
|
||||
static void load_name(char* name, size_t name_size, STREAMFILE* sf, int big_endian, int total_subsongs, int target_subsong) {
|
||||
STREAMFILE *sf_names = NULL;
|
||||
char filename[PATH_LIMIT];
|
||||
char basename[255];
|
||||
|
@ -215,9 +216,9 @@ done:
|
|||
}
|
||||
|
||||
|
||||
static STREAMFILE* setup_nub_streamfile(STREAMFILE *sf, off_t header_offset, size_t header_size, off_t stream_offset, size_t stream_size, const char *fake_ext) {
|
||||
STREAMFILE *new_sf = NULL;
|
||||
STREAMFILE *multi_sf[2] = {0};
|
||||
static STREAMFILE* setup_nub_streamfile(STREAMFILE* sf, off_t header_offset, size_t header_size, off_t stream_offset, size_t stream_size, const char* fake_ext) {
|
||||
STREAMFILE* new_sf = NULL;
|
||||
STREAMFILE* multi_sf[2] = {0};
|
||||
|
||||
multi_sf[0] = open_wrap_streamfile(sf);
|
||||
multi_sf[0] = open_clamp_streamfile_f(multi_sf[0], header_offset, header_size);
|
||||
|
@ -233,8 +234,8 @@ static STREAMFILE* setup_nub_streamfile(STREAMFILE *sf, off_t header_offset, siz
|
|||
//todo could be simplified
|
||||
|
||||
/* .nub wav - from Namco NUB archives [Ridge Racer 7 (PS3)] */
|
||||
VGMSTREAM * init_vgmstream_nub_wav(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
VGMSTREAM* init_vgmstream_nub_wav(STREAMFILE* sf) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
off_t start_offset;
|
||||
int loop_flag, channel_count, sample_rate;
|
||||
size_t data_size, loop_start, loop_length;
|
||||
|
@ -243,12 +244,12 @@ VGMSTREAM * init_vgmstream_nub_wav(STREAMFILE *streamFile) {
|
|||
|
||||
|
||||
/* checks */
|
||||
if (!check_extensions(streamFile, "wav,lwav"))
|
||||
if (!check_extensions(sf, "wav,lwav"))
|
||||
goto fail;
|
||||
if (read_32bitBE(0x00,streamFile) != 0x77617600) /* "wav\0" "*/
|
||||
if (read_32bitBE(0x00,sf) != 0x77617600) /* "wav\0" "*/
|
||||
goto fail;
|
||||
|
||||
if (guess_endianness32bit(0x1c, streamFile)) {
|
||||
if (guess_endianness32bit(0x1c, sf)) {
|
||||
read_32bit = read_32bitBE;
|
||||
read_16bit = read_16bitBE;
|
||||
} else {
|
||||
|
@ -256,18 +257,18 @@ VGMSTREAM * init_vgmstream_nub_wav(STREAMFILE *streamFile) {
|
|||
read_16bit = read_16bitLE;
|
||||
}
|
||||
|
||||
data_size = read_32bit(0x14,streamFile);
|
||||
data_size = read_32bit(0x14,sf);
|
||||
/* info header */
|
||||
loop_start = read_32bit(0x20,streamFile);
|
||||
loop_length = read_32bit(0x24,streamFile);
|
||||
loop_flag = read_32bit(0x28,streamFile);
|
||||
loop_start = read_32bit(0x20,sf);
|
||||
loop_length = read_32bit(0x24,sf);
|
||||
loop_flag = read_32bit(0x28,sf);
|
||||
/* 0x2c: null */
|
||||
|
||||
/* format header: mini "fmt" chunk */
|
||||
if (read_16bit(0xBC + 0x00, streamFile) != 0x0001)
|
||||
if (read_16bit(0xBC + 0x00, sf) != 0x0001)
|
||||
goto fail;
|
||||
channel_count = read_16bit(0xBC + 0x02,streamFile);
|
||||
sample_rate = read_32bit(0xBC + 0x04,streamFile);
|
||||
channel_count = read_16bit(0xBC + 0x02,sf);
|
||||
sample_rate = read_32bit(0xBC + 0x04,sf);
|
||||
/* 0x08: bitrate */
|
||||
/* 0x0c: block align/bps */
|
||||
|
||||
|
@ -288,7 +289,7 @@ VGMSTREAM * init_vgmstream_nub_wav(STREAMFILE *streamFile) {
|
|||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = 0x02;
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream, streamFile, start_offset))
|
||||
if (!vgmstream_open_stream(vgmstream, sf, start_offset))
|
||||
goto fail;
|
||||
return vgmstream;
|
||||
|
||||
|
@ -298,7 +299,7 @@ fail:
|
|||
}
|
||||
|
||||
/* .nub vag - from Namco NUB archives [Ridge Racer 7 (PS3), Noby Noby Boy (PS3)] */
|
||||
VGMSTREAM * init_vgmstream_nub_vag(STREAMFILE *streamFile) {
|
||||
VGMSTREAM* init_vgmstream_nub_vag(STREAMFILE* sf) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
off_t start_offset;
|
||||
int loop_flag, channel_count, sample_rate;
|
||||
|
@ -307,26 +308,26 @@ VGMSTREAM * init_vgmstream_nub_vag(STREAMFILE *streamFile) {
|
|||
|
||||
|
||||
/* checks */
|
||||
if ( !check_extensions(streamFile, "vag"))
|
||||
if ( !check_extensions(sf, "vag"))
|
||||
goto fail;
|
||||
if (read_32bitBE(0x00,streamFile) != 0x76616700) /* "vag\0" */
|
||||
if (read_32bitBE(0x00,sf) != 0x76616700) /* "vag\0" */
|
||||
goto fail;
|
||||
|
||||
if (guess_endianness32bit(0x1c, streamFile)) {
|
||||
if (guess_endianness32bit(0x1c, sf)) {
|
||||
read_32bit = read_32bitBE;
|
||||
} else {
|
||||
read_32bit = read_32bitLE;
|
||||
}
|
||||
|
||||
data_size = read_32bit(0x14,streamFile);
|
||||
data_size = read_32bit(0x14,sf);
|
||||
/* info header */
|
||||
loop_start = read_32bit(0x20,streamFile);
|
||||
loop_length = read_32bit(0x24,streamFile);
|
||||
loop_flag = read_32bit(0x28,streamFile);
|
||||
loop_start = read_32bit(0x20,sf);
|
||||
loop_length = read_32bit(0x24,sf);
|
||||
loop_flag = read_32bit(0x28,sf);
|
||||
/* 0x2c: null */
|
||||
|
||||
/* format header */
|
||||
sample_rate = read_32bit(0xBC + 0x00,streamFile);
|
||||
sample_rate = read_32bit(0xBC + 0x00,sf);
|
||||
|
||||
channel_count = 1;
|
||||
start_offset = 0xC0;
|
||||
|
@ -346,7 +347,7 @@ VGMSTREAM * init_vgmstream_nub_vag(STREAMFILE *streamFile) {
|
|||
vgmstream->layout_type = layout_none;
|
||||
vgmstream->allow_dual_stereo = 1;
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream, streamFile, start_offset))
|
||||
if (!vgmstream_open_stream(vgmstream, sf, start_offset))
|
||||
goto fail;
|
||||
return vgmstream;
|
||||
|
||||
|
@ -356,17 +357,17 @@ fail:
|
|||
}
|
||||
|
||||
/* .nub at3 - from Namco NUB archives [Ridge Racer 7 (PS3), Katamari Forever (PS3)] */
|
||||
VGMSTREAM * init_vgmstream_nub_at3(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
STREAMFILE *temp_sf = NULL;
|
||||
VGMSTREAM* init_vgmstream_nub_at3(STREAMFILE* sf) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
STREAMFILE* temp_sf = NULL;
|
||||
off_t subfile_offset = 0;
|
||||
size_t subfile_size = 0;
|
||||
|
||||
|
||||
/* checks */
|
||||
if (!check_extensions(streamFile,"at3"))
|
||||
if (!check_extensions(sf,"at3"))
|
||||
goto fail;
|
||||
if (read_32bitBE(0x00,streamFile) != 0x61743300) /* "at3\0" */
|
||||
if (read_32bitBE(0x00,sf) != 0x61743300) /* "at3\0" */
|
||||
goto fail;
|
||||
|
||||
/* info header */
|
||||
|
@ -379,9 +380,9 @@ VGMSTREAM * init_vgmstream_nub_at3(STREAMFILE *streamFile) {
|
|||
/* we can just ignore and use RIFF at data start since it has the same info */
|
||||
|
||||
subfile_offset = 0x100;
|
||||
subfile_size = read_32bitLE(subfile_offset + 0x04, streamFile) + 0x08; /* RIFF size */
|
||||
subfile_size = read_32bitLE(subfile_offset + 0x04, sf) + 0x08; /* RIFF size */
|
||||
|
||||
temp_sf = setup_subfile_streamfile(streamFile, subfile_offset,subfile_size, NULL);
|
||||
temp_sf = setup_subfile_streamfile(sf, subfile_offset,subfile_size, NULL);
|
||||
if (!temp_sf) goto fail;
|
||||
|
||||
vgmstream = init_vgmstream_riff(temp_sf);
|
||||
|
@ -397,8 +398,8 @@ fail:
|
|||
|
||||
|
||||
/* .nub xma - from Namco NUB archives [Ridge Racer 6 (X360), Tekken 6 (X360), Galaga Legions DX (X360)] */
|
||||
VGMSTREAM * init_vgmstream_nub_xma(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
VGMSTREAM* init_vgmstream_nub_xma(STREAMFILE* sf) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
off_t start_offset, chunk_offset;
|
||||
size_t data_size, chunk_size, header_size;
|
||||
int loop_flag, channel_count, sample_rate, nus_codec;
|
||||
|
@ -406,27 +407,27 @@ VGMSTREAM * init_vgmstream_nub_xma(STREAMFILE *streamFile) {
|
|||
|
||||
|
||||
/* checks */
|
||||
if (!check_extensions(streamFile,"xma"))
|
||||
if (!check_extensions(sf,"xma"))
|
||||
goto fail;
|
||||
|
||||
if (read_32bitBE(0x00,streamFile) == 0x786D6100) { /* "xma\0" */
|
||||
if (read_32bitBE(0x00,sf) == 0x786D6100) { /* "xma\0" */
|
||||
/* nub v2.1 */
|
||||
nus_codec = read_32bitBE(0x0C,streamFile);
|
||||
data_size = read_32bitBE(0x14,streamFile);
|
||||
header_size = read_32bitBE(0x1c,streamFile);
|
||||
nus_codec = read_32bitBE(0x0C,sf);
|
||||
data_size = read_32bitBE(0x14,sf);
|
||||
header_size = read_32bitBE(0x1c,sf);
|
||||
chunk_offset = 0xBC;
|
||||
|
||||
/* info header */
|
||||
/* 0x20: null */
|
||||
chunk_size = read_32bitBE(0x24,streamFile);
|
||||
chunk_size = read_32bitBE(0x24,sf);
|
||||
/* 0x24: loop flag */
|
||||
/* 0x20: null */
|
||||
}
|
||||
else if (read_32bitBE(0x08,streamFile) == 0 && read_32bitBE(0x0c,streamFile) == 0) {
|
||||
else if (read_32bitBE(0x08,sf) == 0 && read_32bitBE(0x0c,sf) == 0) {
|
||||
/* nub v2.0 from Ridge Racer 6 */
|
||||
nus_codec = read_32bitBE(0x08,streamFile);
|
||||
data_size = read_32bitBE(0x10,streamFile);
|
||||
header_size = read_32bitBE(0x18,streamFile);
|
||||
nus_codec = read_32bitBE(0x08,sf);
|
||||
data_size = read_32bitBE(0x10,sf);
|
||||
header_size = read_32bitBE(0x18,sf);
|
||||
chunk_offset = 0xAC;
|
||||
|
||||
chunk_size = header_size;
|
||||
|
@ -440,7 +441,7 @@ VGMSTREAM * init_vgmstream_nub_xma(STREAMFILE *streamFile) {
|
|||
if (nus_codec == 0x00) { /* XMA1 "fmt " */
|
||||
int loop_start_b, loop_end_b, loop_subframe;
|
||||
|
||||
xma1_parse_fmt_chunk(streamFile, chunk_offset, &channel_count,&sample_rate, &loop_flag, &loop_start_b, &loop_end_b, &loop_subframe, 1);
|
||||
xma1_parse_fmt_chunk(sf, chunk_offset, &channel_count,&sample_rate, &loop_flag, &loop_start_b, &loop_end_b, &loop_subframe, 1);
|
||||
|
||||
{
|
||||
ms_sample_data msd = {0};
|
||||
|
@ -456,7 +457,7 @@ VGMSTREAM * init_vgmstream_nub_xma(STREAMFILE *streamFile) {
|
|||
msd.loop_end_subframe = loop_subframe >> 4; /* upper 4b: subframe where the loop ends, 0..3 */
|
||||
msd.chunk_offset= chunk_offset;
|
||||
|
||||
xma_get_samples(&msd, streamFile);
|
||||
xma_get_samples(&msd, sf);
|
||||
|
||||
num_samples = msd.num_samples;
|
||||
loop_start_sample = msd.loop_start_sample;
|
||||
|
@ -464,12 +465,12 @@ VGMSTREAM * init_vgmstream_nub_xma(STREAMFILE *streamFile) {
|
|||
}
|
||||
}
|
||||
else if (nus_codec == 0x04) { /* "XMA2" */
|
||||
xma2_parse_xma2_chunk(streamFile, chunk_offset, &channel_count,&sample_rate, &loop_flag, &num_samples, &loop_start_sample, &loop_end_sample);
|
||||
xma2_parse_xma2_chunk(sf, chunk_offset, &channel_count,&sample_rate, &loop_flag, &num_samples, &loop_start_sample, &loop_end_sample);
|
||||
}
|
||||
else if (nus_codec == 0x08) { /* XMA2 "fmt " */
|
||||
channel_count = read_16bitBE(chunk_offset+0x02,streamFile);
|
||||
sample_rate = read_32bitBE(chunk_offset+0x04,streamFile);
|
||||
xma2_parse_fmt_chunk_extra(streamFile, chunk_offset, &loop_flag, &num_samples, &loop_start_sample, &loop_end_sample, 1);
|
||||
channel_count = read_16bitBE(chunk_offset+0x02,sf);
|
||||
sample_rate = read_32bitBE(chunk_offset+0x04,sf);
|
||||
xma2_parse_fmt_chunk_extra(sf, chunk_offset, &loop_flag, &num_samples, &loop_start_sample, &loop_end_sample, 1);
|
||||
}
|
||||
else {
|
||||
goto fail;
|
||||
|
@ -492,22 +493,22 @@ VGMSTREAM * init_vgmstream_nub_xma(STREAMFILE *streamFile) {
|
|||
size_t bytes;
|
||||
|
||||
if (nus_codec == 0x04) {
|
||||
bytes = ffmpeg_make_riff_xma2_from_xma2_chunk(buf,0x100, chunk_offset,chunk_size, data_size, streamFile);
|
||||
bytes = ffmpeg_make_riff_xma2_from_xma2_chunk(buf,0x100, chunk_offset,chunk_size, data_size, sf);
|
||||
} else {
|
||||
bytes = ffmpeg_make_riff_xma_from_fmt_chunk(buf,0x100, chunk_offset,chunk_size, data_size, streamFile, 1);
|
||||
bytes = ffmpeg_make_riff_xma_from_fmt_chunk(buf,0x100, chunk_offset,chunk_size, data_size, sf, 1);
|
||||
}
|
||||
vgmstream->codec_data = init_ffmpeg_header_offset(streamFile, buf,bytes, start_offset,data_size);
|
||||
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;
|
||||
|
||||
xma_fix_raw_samples(vgmstream, streamFile, start_offset, data_size, chunk_offset, 1,1); /* samples needs adjustment */
|
||||
xma_fix_raw_samples(vgmstream, sf, start_offset, data_size, chunk_offset, 1,1); /* samples needs adjustment */
|
||||
}
|
||||
#else
|
||||
goto fail;
|
||||
#endif
|
||||
|
||||
if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) )
|
||||
if ( !vgmstream_open_stream(vgmstream, sf, start_offset) )
|
||||
goto fail;
|
||||
return vgmstream;
|
||||
|
||||
|
@ -517,26 +518,26 @@ fail:
|
|||
}
|
||||
|
||||
/* .nub dsp - from Namco NUB archives [Taiko no Tatsujin Wii Chou Goukanban (Wii)] */
|
||||
VGMSTREAM * init_vgmstream_nub_dsp(STREAMFILE *streamFile) {
|
||||
VGMSTREAM *vgmstream = NULL;
|
||||
STREAMFILE *temp_sf = NULL;
|
||||
VGMSTREAM* init_vgmstream_nub_dsp(STREAMFILE* sf) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
STREAMFILE* temp_sf = NULL;
|
||||
off_t header_offset, stream_offset;
|
||||
size_t header_size, stream_size;
|
||||
|
||||
|
||||
/* checks */
|
||||
if (!check_extensions(streamFile,"dsp"))
|
||||
if (!check_extensions(sf,"dsp"))
|
||||
goto fail;
|
||||
if (read_32bitBE(0x00,streamFile) != 0x64737000) /* "dsp\0" */
|
||||
if (read_32bitBE(0x00,sf) != 0x64737000) /* "dsp\0" */
|
||||
goto fail;
|
||||
|
||||
/* paste header+data together and pass to meta, which has loop info too */
|
||||
header_offset = 0xBC;
|
||||
stream_size = read_32bitBE(0x14, streamFile);
|
||||
header_size = read_32bitBE(0x1c, streamFile);
|
||||
stream_size = read_32bitBE(0x14, sf);
|
||||
header_size = read_32bitBE(0x1c, sf);
|
||||
stream_offset = align_size_to_block(header_offset + header_size, 0x10);
|
||||
|
||||
temp_sf = setup_nub_streamfile(streamFile, header_offset, header_size, stream_offset, stream_size, "dsp");
|
||||
temp_sf = setup_nub_streamfile(sf, header_offset, header_size, stream_offset, stream_size, "dsp");
|
||||
if (!temp_sf) goto fail;
|
||||
|
||||
vgmstream = init_vgmstream_ngc_dsp_std(temp_sf);
|
||||
|
@ -551,17 +552,17 @@ fail:
|
|||
}
|
||||
|
||||
/* .nub idsp - from Namco NUB archives [Soul Calibur Legends (Wii), Sky Crawlers: Innocent Aces (Wii)] */
|
||||
VGMSTREAM * init_vgmstream_nub_idsp(STREAMFILE *streamFile) {
|
||||
VGMSTREAM *vgmstream = NULL;
|
||||
STREAMFILE *temp_sf = NULL;
|
||||
VGMSTREAM* init_vgmstream_nub_idsp(STREAMFILE* sf) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
STREAMFILE* temp_sf = NULL;
|
||||
off_t header_offset, stream_offset;
|
||||
size_t header_size, stream_size;
|
||||
|
||||
|
||||
/* checks */
|
||||
if (!check_extensions(streamFile,"idsp"))
|
||||
if (!check_extensions(sf,"idsp"))
|
||||
goto fail;
|
||||
if (read_32bitBE(0x00,streamFile) != 0x69647370) /* "idsp" */
|
||||
if (read_32bitBE(0x00,sf) != 0x69647370) /* "idsp" */
|
||||
goto fail;
|
||||
|
||||
/* info header */
|
||||
|
@ -572,11 +573,11 @@ VGMSTREAM * init_vgmstream_nub_idsp(STREAMFILE *streamFile) {
|
|||
|
||||
/* paste header+data together and pass to meta, which has loop info too */
|
||||
header_offset = 0xBC;
|
||||
stream_size = read_32bitBE(0x14, streamFile);
|
||||
header_size = read_32bitBE(0x1c, streamFile);
|
||||
stream_size = read_32bitBE(0x14, sf);
|
||||
header_size = read_32bitBE(0x1c, sf);
|
||||
stream_offset = align_size_to_block(header_offset + header_size, 0x10);
|
||||
|
||||
temp_sf = setup_nub_streamfile(streamFile, header_offset, header_size, stream_offset, stream_size, "idsp");
|
||||
temp_sf = setup_nub_streamfile(sf, header_offset, header_size, stream_offset, stream_size, "idsp");
|
||||
if (!temp_sf) goto fail;
|
||||
|
||||
vgmstream = init_vgmstream_idsp_namco(temp_sf);
|
||||
|
@ -591,21 +592,21 @@ fail:
|
|||
}
|
||||
|
||||
/* .nub is14 - from Namco NUB archives [Tales of Vesperia (PS3), Mojipittan (Wii)] */
|
||||
VGMSTREAM * init_vgmstream_nub_is14(STREAMFILE *streamFile) {
|
||||
VGMSTREAM *vgmstream = NULL;
|
||||
STREAMFILE *temp_sf = NULL;
|
||||
VGMSTREAM* init_vgmstream_nub_is14(STREAMFILE* sf) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
STREAMFILE* temp_sf = NULL;
|
||||
off_t header_offset, stream_offset;
|
||||
size_t header_size, stream_size, sdat_size;
|
||||
int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL;
|
||||
|
||||
|
||||
/* checks */
|
||||
if (!check_extensions(streamFile,"is14"))
|
||||
if (!check_extensions(sf,"is14"))
|
||||
goto fail;
|
||||
if (read_32bitBE(0x00,streamFile) != 0x69733134) /* "is14" */
|
||||
if (read_32bitBE(0x00,sf) != 0x69733134) /* "is14" */
|
||||
goto fail;
|
||||
|
||||
if (guess_endianness32bit(0x1c, streamFile)) {
|
||||
if (guess_endianness32bit(0x1c, sf)) {
|
||||
read_32bit = read_32bitBE;
|
||||
} else{
|
||||
read_32bit = read_32bitLE;
|
||||
|
@ -615,16 +616,16 @@ VGMSTREAM * init_vgmstream_nub_is14(STREAMFILE *streamFile) {
|
|||
|
||||
/* paste header+data together and pass to meta */
|
||||
header_offset = 0xBC;
|
||||
header_size = read_32bit(0x1c, streamFile);
|
||||
header_size = read_32bit(0x1c, sf);
|
||||
|
||||
/* size at 0x14 is padded, find "sdat" size BE (may move around) */
|
||||
if (!find_chunk_riff_be(streamFile, 0x73646174, 0xbc+0x0c, header_size - 0x0c, NULL, &sdat_size))
|
||||
if (!find_chunk_riff_be(sf, 0x73646174, 0xbc+0x0c, header_size - 0x0c, NULL, &sdat_size))
|
||||
goto fail;
|
||||
stream_offset = align_size_to_block(header_offset + header_size, 0x10);
|
||||
stream_size = sdat_size;
|
||||
|
||||
|
||||
temp_sf = setup_nub_streamfile(streamFile, header_offset, header_size, stream_offset, stream_size, "bnsf");
|
||||
temp_sf = setup_nub_streamfile(sf, header_offset, header_size, stream_offset, stream_size, "bnsf");
|
||||
if (!temp_sf) goto fail;
|
||||
|
||||
vgmstream = init_vgmstream_bnsf(temp_sf);
|
||||
|
|
|
@ -82,6 +82,20 @@ static void rpgmvo_ogg_decryption_callback(void* ptr, size_t size, size_t nmemb,
|
|||
}
|
||||
}
|
||||
|
||||
static void at4_ogg_decryption_callback(void* ptr, size_t size, size_t nmemb, void* datasource) {
|
||||
static const uint8_t af4_key[0x10] = {
|
||||
0x00,0x0E,0x08,0x1E, 0x18,0x37,0x12,0x00, 0x48,0x87,0x46,0x0B, 0x9C,0x68,0xA8,0x4B
|
||||
};
|
||||
uint8_t *ptr8 = ptr;
|
||||
size_t bytes_read = size * nmemb;
|
||||
ogg_vorbis_io *io = datasource;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < bytes_read; i++) {
|
||||
ptr8[i] -= af4_key[(io->offset + i) % sizeof(af4_key)];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static const uint32_t xiph_mappings[] = {
|
||||
0,
|
||||
|
@ -152,31 +166,34 @@ VGMSTREAM* init_vgmstream_ogg_vorbis(STREAMFILE* sf) {
|
|||
}
|
||||
|
||||
if (is_ogg) {
|
||||
if (read_32bitBE(0x00,sf) == 0x2c444430) { /* Psychic Software [Darkwind: War on Wheels (PC)] */
|
||||
if (read_u32be(0x00,sf) == 0x2c444430) { /* Psychic Software [Darkwind: War on Wheels (PC)] */
|
||||
ovmi.decryption_callback = psychic_ogg_decryption_callback;
|
||||
}
|
||||
else if (read_32bitBE(0x00,sf) == 0x4C325344) { /* "L2SD" instead of "OggS" [Lineage II Chronicle 4 (PC)] */
|
||||
else if (read_u32be(0x00,sf) == 0x4C325344) { /* "L2SD" instead of "OggS" [Lineage II Chronicle 4 (PC)] */
|
||||
cfg.is_header_swap = 1;
|
||||
cfg.is_encrypted = 1;
|
||||
}
|
||||
else if (read_32bitBE(0x00,sf) == 0x048686C5) { /* "OggS" XOR'ed + bitswapped [Ys VIII (PC)] */
|
||||
else if (read_u32be(0x00,sf) == 0x048686C5) { /* "OggS" XOR'ed + bitswapped [Ys VIII (PC)] */
|
||||
cfg.key[0] = 0xF0;
|
||||
cfg.key_len = 1;
|
||||
cfg.is_nibble_swap = 1;
|
||||
cfg.is_encrypted = 1;
|
||||
|
||||
}
|
||||
else if (read_32bitBE(0x00,sf) == 0x00000000 && /* null instead of "OggS" [Yuppie Psycho (PC)] */
|
||||
read_32bitBE(0x3a,sf) == 0x4F676753) { /* "OggS" in next page */
|
||||
else if (read_u32be(0x00,sf) == 0x00000000 && /* null instead of "OggS" [Yuppie Psycho (PC)] */
|
||||
read_u32be(0x3a,sf) == 0x4F676753) { /* "OggS" in next page */
|
||||
cfg.is_header_swap = 1;
|
||||
cfg.is_encrypted = 1;
|
||||
}
|
||||
else if (read_32bitBE(0x00,sf) != 0x4F676753 && /* random(?) swap instead of "OggS" [Tobi Tsukihime (PC)] */
|
||||
read_32bitBE(0x3a,sf) == 0x4F676753) { /* "OggS" in next page */
|
||||
else if (read_u32be(0x00,sf) != 0x4F676753 && /* random(?) swap instead of "OggS" [Tobi Tsukihime (PC)] */
|
||||
read_u32be(0x3a,sf) == 0x4F676753) { /* "OggS" in next page */
|
||||
cfg.is_header_swap = 1;
|
||||
cfg.is_encrypted = 1;
|
||||
}
|
||||
else if (read_32bitBE(0x00,sf) == 0x4F676753) { /* "OggS" (standard) */
|
||||
else if (read_u32be(0x00,sf) == 0x4F756F71) { /* "OggS" encrypted [Adventure Field 4 (PC)]*/
|
||||
ovmi.decryption_callback = at4_ogg_decryption_callback;
|
||||
}
|
||||
else if (read_u32be(0x00,sf) == 0x4F676753) { /* "OggS" (standard) */
|
||||
;
|
||||
}
|
||||
else {
|
||||
|
@ -185,13 +202,13 @@ VGMSTREAM* init_vgmstream_ogg_vorbis(STREAMFILE* sf) {
|
|||
}
|
||||
|
||||
if (is_um3) { /* ["Ultramarine3" (???)] */
|
||||
if (read_32bitBE(0x00,sf) != 0x4f676753) { /* "OggS" (optionally encrypted) */
|
||||
if (read_u32be(0x00,sf) != 0x4f676753) { /* "OggS" (optionally encrypted) */
|
||||
ovmi.decryption_callback = um3_ogg_decryption_callback;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_kovs) { /* Koei Tecmo PC games */
|
||||
if (read_32bitBE(0x00,sf) != 0x4b4f5653) { /* "KOVS" */
|
||||
if (read_u32be(0x00,sf) != 0x4b4f5653) { /* "KOVS" */
|
||||
goto fail;
|
||||
}
|
||||
ovmi.loop_start = read_32bitLE(0x08,sf);
|
||||
|
@ -203,7 +220,7 @@ VGMSTREAM* init_vgmstream_ogg_vorbis(STREAMFILE* sf) {
|
|||
}
|
||||
|
||||
if (is_sngw) { /* [Capcom's MT Framework PC games] */
|
||||
if (read_32bitBE(0x00,sf) != 0x4f676753) { /* "OggS" (optionally encrypted) */
|
||||
if (read_u32be(0x00,sf) != 0x4f676753) { /* "OggS" (optionally encrypted) */
|
||||
cfg.key_len = read_streamfile(cfg.key, 0x00, 0x04, sf);
|
||||
cfg.is_header_swap = 1;
|
||||
cfg.is_nibble_swap = 1;
|
||||
|
@ -217,7 +234,7 @@ VGMSTREAM* init_vgmstream_ogg_vorbis(STREAMFILE* sf) {
|
|||
const char *isl_name = NULL;
|
||||
|
||||
/* check various encrypted "OggS" values */
|
||||
if (read_32bitBE(0x00,sf) == 0xAF678753) { /* Azure Striker Gunvolt (PC) */
|
||||
if (read_u32be(0x00,sf) == 0xAF678753) { /* Azure Striker Gunvolt (PC) */
|
||||
static const uint8_t isd_gv_key[16] = {
|
||||
0xe0,0x00,0xe0,0x00,0xa0,0x00,0x00,0x00,0xe0,0x00,0xe0,0x80,0x40,0x40,0x40,0x00
|
||||
};
|
||||
|
@ -225,7 +242,7 @@ VGMSTREAM* init_vgmstream_ogg_vorbis(STREAMFILE* sf) {
|
|||
memcpy(cfg.key, isd_gv_key, cfg.key_len);
|
||||
isl_name = "GV_steam.isl";
|
||||
}
|
||||
else if (read_32bitBE(0x00,sf) == 0x0FE787D3) { /* Mighty Gunvolt (PC) */
|
||||
else if (read_u32be(0x00,sf) == 0x0FE787D3) { /* Mighty Gunvolt (PC) */
|
||||
static const uint8_t isd_mgv_key[120] = {
|
||||
0x40,0x80,0xE0,0x80,0x40,0x40,0xA0,0x00,0xA0,0x40,0x00,0x80,0x00,0x40,0xA0,0x00,
|
||||
0xC0,0x40,0xE0,0x00,0x60,0x40,0x80,0x00,0xA0,0x00,0xE0,0x00,0x60,0x40,0xC0,0x00,
|
||||
|
@ -240,7 +257,7 @@ VGMSTREAM* init_vgmstream_ogg_vorbis(STREAMFILE* sf) {
|
|||
memcpy(cfg.key, isd_mgv_key, cfg.key_len);
|
||||
isl_name = "MGV_steam.isl";
|
||||
}
|
||||
else if (read_32bitBE(0x00,sf) == 0x0FA74753) { /* Blaster Master Zero (PC) */
|
||||
else if (read_u32be(0x00,sf) == 0x0FA74753) { /* Blaster Master Zero (PC) */
|
||||
static const uint8_t isd_bmz_key[120] = {
|
||||
0x40,0xC0,0x20,0x00,0x40,0xC0,0xC0,0x00,0x00,0x80,0xE0,0x80,0x80,0x40,0x20,0x00,
|
||||
0x60,0xC0,0xC0,0x00,0xA0,0x80,0x60,0x00,0x40,0x40,0x20,0x00,0x60,0x40,0xC0,0x00,
|
||||
|
@ -374,7 +391,7 @@ VGMSTREAM* init_vgmstream_ogg_vorbis(STREAMFILE* sf) {
|
|||
uint32_t xor_be;
|
||||
|
||||
put_32bitLE(key, (uint32_t)file_size);
|
||||
xor_be = (uint32_t)get_32bitBE(key);
|
||||
xor_be = get_u32be(key);
|
||||
if ((read_32bitBE(0x00,sf) ^ xor_be) == 0x4F676753) { /* "OggS" */
|
||||
int i;
|
||||
cfg.key_len = 4;
|
||||
|
|
|
@ -13,24 +13,24 @@ static VGMSTREAM* init_vgmstream_opus(STREAMFILE* sf, meta_t meta_type, off_t of
|
|||
size_t data_size, skip = 0;
|
||||
|
||||
|
||||
if ((uint32_t)read_32bitLE(offset + 0x00,sf) != 0x80000001)
|
||||
if (read_u32le(offset + 0x00,sf) != 0x80000001)
|
||||
goto fail;
|
||||
|
||||
channel_count = read_8bit(offset + 0x09, sf);
|
||||
channel_count = read_u8(offset + 0x09, sf);
|
||||
/* 0x0a: packet size if CBR, 0 if VBR */
|
||||
data_offset = offset + read_32bitLE(offset + 0x10, sf);
|
||||
skip = read_16bitLE(offset + 0x1c, sf);
|
||||
data_offset = offset + read_u32le(offset + 0x10, sf);
|
||||
skip = read_u16le(offset + 0x1c, sf);
|
||||
/* 0x1e: ? (seen in Lego Movie 2 (Switch)) */
|
||||
|
||||
/* recent >2ch info [Clannad (Switch)] */
|
||||
if ((uint32_t)read_32bitLE(offset + 0x20, sf) == 0x80000005) {
|
||||
if (read_u32le(offset + 0x20, sf) == 0x80000005) {
|
||||
multichannel_offset = offset + 0x20;
|
||||
}
|
||||
|
||||
if ((uint32_t)read_32bitLE(data_offset, sf) != 0x80000004)
|
||||
if (read_u32le(data_offset, sf) != 0x80000004)
|
||||
goto fail;
|
||||
|
||||
data_size = read_32bitLE(data_offset + 0x04, sf);
|
||||
data_size = read_u32le(data_offset + 0x04, sf);
|
||||
|
||||
start_offset = data_offset + 0x08;
|
||||
loop_flag = (loop_end > 0); /* -1 when not set */
|
||||
|
@ -41,7 +41,7 @@ static VGMSTREAM* init_vgmstream_opus(STREAMFILE* sf, meta_t meta_type, off_t of
|
|||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->meta_type = meta_type;
|
||||
vgmstream->sample_rate = read_32bitLE(offset + 0x0c,sf);
|
||||
vgmstream->sample_rate = read_u32le(offset + 0x0c,sf);
|
||||
if (vgmstream->sample_rate == 16000)
|
||||
vgmstream->sample_rate = 48000; // Grandia HD Collection contains a false sample_rate in header
|
||||
vgmstream->num_samples = num_samples;
|
||||
|
@ -59,10 +59,10 @@ static VGMSTREAM* init_vgmstream_opus(STREAMFILE* sf, meta_t meta_type, off_t of
|
|||
|
||||
if (multichannel_offset && vgmstream->channels <= 8) {
|
||||
int i;
|
||||
cfg.stream_count = read_8bit(multichannel_offset + 0x08,sf);
|
||||
cfg.coupled_count = read_8bit(multichannel_offset + 0x09,sf);
|
||||
cfg.stream_count = read_u8(multichannel_offset + 0x08,sf);
|
||||
cfg.coupled_count = read_u8(multichannel_offset + 0x09,sf);
|
||||
for (i = 0; i < vgmstream->channels; i++) {
|
||||
cfg.channel_mapping[i] = read_8bit(multichannel_offset + 0x0a + i,sf);
|
||||
cfg.channel_mapping[i] = read_u8(multichannel_offset + 0x0a + i,sf);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -80,7 +80,7 @@ static VGMSTREAM* init_vgmstream_opus(STREAMFILE* sf, meta_t meta_type, off_t of
|
|||
goto fail;
|
||||
#endif
|
||||
|
||||
if ( !vgmstream_open_stream(vgmstream, sf, start_offset) )
|
||||
if (!vgmstream_open_stream(vgmstream, sf, start_offset))
|
||||
goto fail;
|
||||
return vgmstream;
|
||||
|
||||
|
@ -92,24 +92,26 @@ fail:
|
|||
|
||||
/* standard Switch Opus, Nintendo header + raw data (generated by opus_test.c?) [Lego City Undercover (Switch)] */
|
||||
VGMSTREAM* init_vgmstream_opus_std(STREAMFILE* sf) {
|
||||
STREAMFILE * PSIFile = NULL;
|
||||
STREAMFILE* psi_sf = NULL;
|
||||
off_t offset;
|
||||
int num_samples, loop_start, loop_end;
|
||||
|
||||
/* checks */
|
||||
if (!check_extensions(sf,"opus,lopus"))
|
||||
/* .opus: standard
|
||||
* .bgm: Cotton Reboot (Switch) */
|
||||
if (!check_extensions(sf,"opus,lopus,bgm"))
|
||||
goto fail;
|
||||
|
||||
offset = 0x00;
|
||||
|
||||
/* BlazBlue: Cross Tag Battle (Switch) PSI Metadata for corresponding Opus */
|
||||
/* Maybe future Arc System Works games will use this too? */
|
||||
PSIFile = open_streamfile_by_ext(sf, "psi");
|
||||
if (PSIFile) {
|
||||
num_samples = read_32bitLE(0x8C, PSIFile);
|
||||
loop_start = read_32bitLE(0x84, PSIFile);
|
||||
loop_end = read_32bitLE(0x88, PSIFile);
|
||||
close_streamfile(PSIFile);
|
||||
psi_sf = open_streamfile_by_ext(sf, "psi");
|
||||
if (psi_sf) {
|
||||
num_samples = read_s32le(0x8C, psi_sf);
|
||||
loop_start = read_s32le(0x84, psi_sf);
|
||||
loop_end = read_s32le(0x88, psi_sf);
|
||||
close_streamfile(psi_sf);
|
||||
}
|
||||
else {
|
||||
num_samples = 0;
|
||||
|
|
|
@ -3,30 +3,30 @@
|
|||
|
||||
|
||||
/* 2PFS - from Konami Games [Mahoromatic: Moetto - KiraKira Maid-San (PS2), GANTZ The Game (PS2)] */
|
||||
VGMSTREAM * init_vgmstream_ps2_2pfs(STREAMFILE *streamFile) {
|
||||
VGMSTREAM* init_vgmstream_ps2_2pfs(STREAMFILE *sf) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
off_t start_offset;
|
||||
int loop_flag, channel_count, version, interleave;
|
||||
int loop_flag, channels, version, interleave;
|
||||
int loop_start_block, loop_end_block; /* block number */
|
||||
int loop_start_adjust, loop_end_adjust; /* loops start/end a few samples into the start/end block */
|
||||
|
||||
|
||||
/* checks */
|
||||
/* .sap: standard
|
||||
* .2psf: header id? (Mahoromatic) */
|
||||
if (!check_extensions(streamFile, "sap,2psf"))
|
||||
* .2pfs: header id? (Mahoromatic) */
|
||||
if (!check_extensions(sf, "sap,2pfs"))
|
||||
goto fail;
|
||||
|
||||
if (read_32bitBE(0x00,streamFile) != 0x32504653) /* "2PFS" */
|
||||
if (read_u32be(0x00,sf) != 0x32504653) /* "2PFS" */
|
||||
goto fail;
|
||||
|
||||
version = read_16bitLE(0x04,streamFile);
|
||||
version = read_u16le(0x04,sf);
|
||||
if (version != 0x01 && version != 0x02) /* v1: Mahoromatic, v2: Gantz */
|
||||
goto fail;
|
||||
|
||||
|
||||
channel_count = read_8bit(0x40,streamFile);
|
||||
loop_flag = read_8bit(0x41,streamFile);
|
||||
channels = read_u8(0x40,sf);
|
||||
loop_flag = read_u8(0x41,sf);
|
||||
start_offset = 0x800;
|
||||
interleave = 0x1000;
|
||||
|
||||
|
@ -41,44 +41,44 @@ VGMSTREAM * init_vgmstream_ps2_2pfs(STREAMFILE *streamFile) {
|
|||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channel_count,loop_flag);
|
||||
vgmstream = allocate_vgmstream(channels, loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->meta_type = meta_PS2_2PFS;
|
||||
vgmstream->num_samples = read_32bitLE(0x34,streamFile) * 28 / 16 / channel_count;
|
||||
vgmstream->num_samples = read_u32le(0x34,sf) * 28 / 16 / channels;
|
||||
vgmstream->coding_type = coding_PSX;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = interleave;
|
||||
|
||||
if (version == 0x01) {
|
||||
vgmstream->sample_rate = read_32bitLE(0x44,streamFile);
|
||||
loop_start_adjust = read_16bitLE(0x42,streamFile);
|
||||
loop_start_block = read_32bitLE(0x48,streamFile);
|
||||
loop_end_block = read_32bitLE(0x4c,streamFile);
|
||||
vgmstream->sample_rate = read_u32le(0x44,sf);
|
||||
loop_start_adjust = read_u16le(0x42,sf);
|
||||
loop_start_block = read_u32le(0x48,sf);
|
||||
loop_end_block = read_u32le(0x4c,sf);
|
||||
}
|
||||
else {
|
||||
vgmstream->sample_rate = read_32bitLE(0x48,streamFile);
|
||||
loop_start_adjust = read_32bitLE(0x44,streamFile);
|
||||
loop_start_block = read_32bitLE(0x50,streamFile);
|
||||
loop_end_block = read_32bitLE(0x54,streamFile);
|
||||
vgmstream->sample_rate = read_u32le(0x48,sf);
|
||||
loop_start_adjust = read_u32le(0x44,sf);
|
||||
loop_start_block = read_u32le(0x50,sf);
|
||||
loop_end_block = read_u32le(0x54,sf);
|
||||
}
|
||||
loop_end_adjust = interleave; /* loops end after all samples in the end_block AFAIK */
|
||||
|
||||
if (loop_flag) {
|
||||
/* block to offset > offset to sample + adjust (number of frames into the block) */
|
||||
vgmstream->loop_start_sample =
|
||||
ps_bytes_to_samples(loop_start_block * channel_count * interleave, channel_count)
|
||||
+ ps_bytes_to_samples(loop_start_adjust * channel_count, channel_count);
|
||||
ps_bytes_to_samples(loop_start_block * channels * interleave, channels)
|
||||
+ ps_bytes_to_samples(loop_start_adjust * channels, channels);
|
||||
vgmstream->loop_end_sample =
|
||||
ps_bytes_to_samples(loop_end_block * channel_count * interleave, channel_count)
|
||||
+ ps_bytes_to_samples(loop_end_adjust * channel_count, channel_count);
|
||||
ps_bytes_to_samples(loop_end_block * channels * interleave, channels)
|
||||
+ ps_bytes_to_samples(loop_end_adjust * channels, channels);
|
||||
}
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
|
||||
if (!vgmstream_open_stream(vgmstream, sf, start_offset))
|
||||
goto fail;
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
if (vgmstream) close_vgmstream(vgmstream);
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
|
|
|
@ -1,229 +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:
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
#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_last;
|
||||
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 tac_decoder_lib.h 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_last = 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); /* actual check may be loop_frame > 0? */
|
||||
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;
|
||||
|
||||
/* Frame at count/loop outputs less than full 1024 samples (thus loop or count-1 + extra).
|
||||
* A few files may pop when looping, but this seems to match game/emulator. */
|
||||
vgmstream->num_samples = (frame_count - 1) * 1024 + (frame_last + 1);
|
||||
vgmstream->loop_start_sample = (loop_frame - 1) * 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;
|
||||
}
|
|
@ -1051,7 +1051,7 @@ static int parse_values(ubi_bao_header* bao, STREAMFILE* sf) {
|
|||
}
|
||||
bao->codec = bao->cfg.codec_map[bao->stream_type];
|
||||
if (bao->codec == 0x00) {
|
||||
VGM_LOG("UBI BAO: unknown codec at %x\n", (uint32_t)bao->header_offset); goto fail;
|
||||
VGM_LOG("UBI BAO: unknown codec %x at %x\n", bao->stream_type, (uint32_t)bao->header_offset); goto fail;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
|
@ -1327,6 +1327,27 @@ fail:
|
|||
|
||||
/* ************************************************************************* */
|
||||
|
||||
/* These are all of the languages that were referenced in Assassin's Creed exe (out of each platform). */
|
||||
/* Also, additional languages were referenced in Shawn White Skateboarding (X360) exe in this order, there may be more. */
|
||||
static const char* language_bao_formats[] = {
|
||||
"English_BAO_0x%08x",
|
||||
"French_BAO_0x%08x",
|
||||
"Spanish_BAO_0x%08x",
|
||||
"Polish_BAO_0x%08x",
|
||||
"German_BAO_0x%08x",
|
||||
"Chinese_BAO_0x%08x",
|
||||
"Hungarian_BAO_0x%08x",
|
||||
"Italian_BAO_0x%08x",
|
||||
"Japanese_BAO_0x%08x",
|
||||
"Czech_BAO_0x%08x",
|
||||
"Korean_BAO_0x%08x",
|
||||
"Russian_BAO_0x%08x",
|
||||
"Dutch_BAO_0x%08x",
|
||||
"Danish_BAO_0x%08x",
|
||||
"Norwegian_BAO_0x%08x",
|
||||
"Swedish_BAO_0x%08x",
|
||||
};
|
||||
|
||||
/* opens a file BAO's companion BAO (memory or stream) */
|
||||
static STREAMFILE* open_atomic_bao(ubi_bao_file file_type, uint32_t file_id, int is_stream, STREAMFILE* sf) {
|
||||
STREAMFILE* sf_bao = NULL;
|
||||
|
@ -1349,47 +1370,17 @@ static STREAMFILE* open_atomic_bao(ubi_bao_file file_type, uint32_t file_id, int
|
|||
sf_bao = open_streamfile_by_filename(sf, buf);
|
||||
if (sf_bao) return sf_bao;
|
||||
|
||||
snprintf(buf,buf_size, "English_BAO_0x%08x", file_id);
|
||||
sf_bao = open_streamfile_by_filename(sf, buf);
|
||||
if (sf_bao) return sf_bao;
|
||||
{
|
||||
int i;
|
||||
int count = (sizeof(language_bao_formats) / sizeof(language_bao_formats[0]));
|
||||
for (i = 0; i < count; i++) {
|
||||
const char* format = language_bao_formats[i];
|
||||
|
||||
snprintf(buf,buf_size, "French_BAO_0x%08x", file_id);
|
||||
sf_bao = open_streamfile_by_filename(sf, buf);
|
||||
if (sf_bao) return sf_bao;
|
||||
|
||||
snprintf(buf,buf_size, "Spanish_BAO_0x%08x", file_id);
|
||||
sf_bao = open_streamfile_by_filename(sf, buf);
|
||||
if (sf_bao) return sf_bao;
|
||||
|
||||
snprintf(buf,buf_size, "German_BAO_0x%08x", file_id);
|
||||
sf_bao = open_streamfile_by_filename(sf, buf);
|
||||
if (sf_bao) return sf_bao;
|
||||
|
||||
snprintf(buf,buf_size, "Italian_BAO_0x%08x", file_id);
|
||||
sf_bao = open_streamfile_by_filename(sf, buf);
|
||||
if (sf_bao) return sf_bao;
|
||||
|
||||
snprintf(buf,buf_size, "Japanese_BAO_0x%08x", file_id);
|
||||
sf_bao = open_streamfile_by_filename(sf, buf);
|
||||
if (sf_bao) return sf_bao;
|
||||
|
||||
snprintf(buf,buf_size, "Korean_BAO_0x%08x", file_id);
|
||||
sf_bao = open_streamfile_by_filename(sf, buf);
|
||||
if (sf_bao) return sf_bao;
|
||||
|
||||
snprintf(buf,buf_size, "Russian_BAO_0x%08x", file_id);
|
||||
sf_bao = open_streamfile_by_filename(sf, buf);
|
||||
if (sf_bao) return sf_bao;
|
||||
|
||||
snprintf(buf,buf_size, "Czech_BAO_0x%08x", file_id);
|
||||
sf_bao = open_streamfile_by_filename(sf, buf);
|
||||
if (sf_bao) return sf_bao;
|
||||
|
||||
snprintf(buf,buf_size, "Polish_BAO_0x%08x", file_id);
|
||||
sf_bao = open_streamfile_by_filename(sf, buf);
|
||||
if (sf_bao) return sf_bao;
|
||||
|
||||
/* these are all of the languages that were referenced in Assassin's Creed exe (out of each platform), there may be more */
|
||||
snprintf(buf,buf_size, format, file_id);
|
||||
sf_bao = open_streamfile_by_filename(sf, buf);
|
||||
if (sf_bao) return sf_bao;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
snprintf(buf,buf_size, "BAO_0x%08x", file_id);
|
||||
|
@ -1746,9 +1737,11 @@ static int config_bao_version(ubi_bao_header* bao, STREAMFILE* sf) {
|
|||
|
||||
config_bao_silence_f(bao, 0x1c);
|
||||
|
||||
bao->cfg.codec_map[0x00] = RAW_XMA1;
|
||||
bao->cfg.codec_map[0x02] = RAW_PSX;
|
||||
bao->cfg.codec_map[0x03] = UBI_IMA;
|
||||
bao->cfg.codec_map[0x04] = FMT_OGG;
|
||||
bao->cfg.codec_map[0x05] = RAW_XMA1; /* same but streamed? */
|
||||
bao->cfg.codec_map[0x07] = RAW_AT3_105;
|
||||
|
||||
bao->cfg.file_type = UBI_FORGE;
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
#include "../coding/coding.h"
|
||||
|
||||
|
||||
typedef enum { PCM, UBI, PSX, DSP, XIMA, ATRAC3, XMA2 } ubi_hx_codec;
|
||||
typedef enum { PCM, UBI, PSX, DSP, XIMA, ATRAC3, XMA2, MP3 } ubi_hx_codec;
|
||||
|
||||
typedef struct {
|
||||
int big_endian;
|
||||
|
@ -36,14 +36,14 @@ typedef struct {
|
|||
} ubi_hx_header;
|
||||
|
||||
|
||||
static int parse_hx(ubi_hx_header * hx, STREAMFILE *sf, int target_subsong);
|
||||
static VGMSTREAM * init_vgmstream_ubi_hx_header(ubi_hx_header *hx, STREAMFILE *sf);
|
||||
static int parse_hx(ubi_hx_header* hx, STREAMFILE* sf, int target_subsong);
|
||||
static VGMSTREAM* init_vgmstream_ubi_hx_header(ubi_hx_header* hx, STREAMFILE* sf);
|
||||
|
||||
/* .HXx - banks from Ubisoft's HXAudio engine games [Rayman Arena, Rayman 3, XIII] */
|
||||
VGMSTREAM * init_vgmstream_ubi_hx(STREAMFILE *streamFile) {
|
||||
VGMSTREAM* init_vgmstream_ubi_hx(STREAMFILE* sf) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
ubi_hx_header hx = {0};
|
||||
int target_subsong = streamFile->stream_index;
|
||||
int target_subsong = sf->stream_index;
|
||||
|
||||
|
||||
/* checks */
|
||||
|
@ -53,7 +53,7 @@ VGMSTREAM * init_vgmstream_ubi_hx(STREAMFILE *streamFile) {
|
|||
* .hxg: Rayman 3 (GC), XIII (GC)
|
||||
* .hxx: Rayman 3 (Xbox), Rayman 3 HD (X360)
|
||||
* .hx3: Rayman 3 HD (PS3) */
|
||||
if (!check_extensions(streamFile, "hxd,hxc,hx2,hxg,hxx,hx3"))
|
||||
if (!check_extensions(sf, "hxd,hxc,hx2,hxg,hxx,hx3"))
|
||||
goto fail;
|
||||
|
||||
/* .HXx is a slightly less bizarre bank with various resource classes (events, streams, etc, not unlike other Ubi's engines)
|
||||
|
@ -61,14 +61,14 @@ VGMSTREAM * init_vgmstream_ubi_hx(STREAMFILE *streamFile) {
|
|||
* Game seems to play files by calling linked ids: EventResData (play/stop/etc) > Random/Program/Wav ResData (1..N refs) > FileIdObj */
|
||||
|
||||
/* HX CONFIG */
|
||||
hx.big_endian = guess_endianness32bit(0x00, streamFile);
|
||||
hx.big_endian = guess_endianness32bit(0x00, sf);
|
||||
|
||||
/* HX HEADER */
|
||||
if (!parse_hx(&hx, streamFile, target_subsong))
|
||||
if (!parse_hx(&hx, sf, target_subsong))
|
||||
goto fail;
|
||||
|
||||
/* CREATE VGMSTREAM */
|
||||
vgmstream = init_vgmstream_ubi_hx_header(&hx, streamFile);
|
||||
vgmstream = init_vgmstream_ubi_hx_header(&hx, sf);
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
|
@ -77,7 +77,7 @@ fail:
|
|||
}
|
||||
|
||||
|
||||
static void build_readable_name(char * buf, size_t buf_size, ubi_hx_header * hx) {
|
||||
static void build_readable_name(char* buf, size_t buf_size, ubi_hx_header* hx) {
|
||||
const char *grp_name;
|
||||
|
||||
if (hx->is_external)
|
||||
|
@ -94,8 +94,8 @@ static void build_readable_name(char * buf, size_t buf_size, ubi_hx_header * hx)
|
|||
#define TXT_LINE_MAX 0x1000
|
||||
|
||||
/* get name */
|
||||
static int parse_name_bnh(ubi_hx_header * hx, STREAMFILE *sf, uint32_t cuuid1, uint32_t cuuid2) {
|
||||
STREAMFILE *sf_t;
|
||||
static int parse_name_bnh(ubi_hx_header* hx, STREAMFILE* sf, uint32_t cuuid1, uint32_t cuuid2) {
|
||||
STREAMFILE* sf_t;
|
||||
off_t txt_offset = 0;
|
||||
char line[TXT_LINE_MAX];
|
||||
char cuuid[40];
|
||||
|
@ -132,15 +132,16 @@ fail:
|
|||
|
||||
|
||||
/* get referenced name from WavRes, using the index again (abridged) */
|
||||
static int parse_name(ubi_hx_header * hx, STREAMFILE *sf) {
|
||||
int32_t (*read_32bit)(off_t,STREAMFILE*) = hx->big_endian ? read_32bitBE : read_32bitLE;
|
||||
static int parse_name(ubi_hx_header* hx, STREAMFILE* sf) {
|
||||
uint32_t (*read_u32)(off_t,STREAMFILE*) = hx->big_endian ? read_u32be : read_u32le;
|
||||
int32_t (*read_s32)(off_t,STREAMFILE*) = hx->big_endian ? read_s32be : read_s32le;
|
||||
off_t index_offset, offset;
|
||||
int i, index_entries;
|
||||
char class_name[255];
|
||||
|
||||
|
||||
index_offset = read_32bit(0x00, sf);
|
||||
index_entries = read_32bit(index_offset + 0x08, sf);
|
||||
index_offset = read_u32(0x00, sf);
|
||||
index_entries = read_s32(index_offset + 0x08, sf);
|
||||
offset = index_offset + 0x0c;
|
||||
for (i = 0; i < index_entries; i++) {
|
||||
off_t header_offset;
|
||||
|
@ -149,25 +150,25 @@ static int parse_name(ubi_hx_header * hx, STREAMFILE *sf) {
|
|||
uint32_t cuuid1, cuuid2;
|
||||
|
||||
|
||||
class_size = read_32bit(offset + 0x00, sf);
|
||||
class_size = read_u32(offset + 0x00, sf);
|
||||
if (class_size > sizeof(class_name)+1) goto fail;
|
||||
read_string(class_name,class_size+1, offset + 0x04, sf); /* not null-terminated */
|
||||
offset += 0x04 + class_size;
|
||||
|
||||
cuuid1 = (uint32_t)read_32bit(offset + 0x00, sf);
|
||||
cuuid2 = (uint32_t)read_32bit(offset + 0x04, sf);
|
||||
cuuid1 = read_u32(offset + 0x00, sf);
|
||||
cuuid2 = read_u32(offset + 0x04, sf);
|
||||
|
||||
header_offset = read_32bit(offset + 0x08, sf);
|
||||
header_offset = read_u32(offset + 0x08, sf);
|
||||
offset += 0x10;
|
||||
|
||||
//unknown_count = read_32bit(offset + 0x00, sf);
|
||||
//unknown_count = read_s32(offset + 0x00, sf);
|
||||
offset += 0x04;
|
||||
|
||||
link_count = read_32bit(offset + 0x00, sf);
|
||||
link_count = read_s32(offset + 0x00, sf);
|
||||
offset += 0x04;
|
||||
for (j = 0; j < link_count; j++) {
|
||||
uint32_t link_id1 = (uint32_t)read_32bit(offset + 0x00, sf);
|
||||
uint32_t link_id2 = (uint32_t)read_32bit(offset + 0x04, sf);
|
||||
uint32_t link_id1 = read_u32(offset + 0x00, sf);
|
||||
uint32_t link_id2 = read_u32(offset + 0x04, sf);
|
||||
|
||||
if (link_id1 == hx->cuuid1 && link_id2 == hx->cuuid2) {
|
||||
is_found = 1;
|
||||
|
@ -175,11 +176,11 @@ static int parse_name(ubi_hx_header * hx, STREAMFILE *sf) {
|
|||
offset += 0x08;
|
||||
}
|
||||
|
||||
language_count = read_32bit(offset + 0x00, sf);
|
||||
language_count = read_s32(offset + 0x00, sf);
|
||||
offset += 0x04;
|
||||
for (j = 0; j < language_count; j++) {
|
||||
uint32_t link_id1 = (uint32_t)read_32bit(offset + 0x08, sf);
|
||||
uint32_t link_id2 = (uint32_t)read_32bit(offset + 0x0c, sf);
|
||||
uint32_t link_id1 = read_u32(offset + 0x08, sf);
|
||||
uint32_t link_id2 = read_u32(offset + 0x0c, sf);
|
||||
|
||||
if (link_id1 == hx->cuuid1 && link_id2 == hx->cuuid2) {
|
||||
is_found = 1;
|
||||
|
@ -199,10 +200,10 @@ static int parse_name(ubi_hx_header * hx, STREAMFILE *sf) {
|
|||
off_t wavres_offset = header_offset;
|
||||
|
||||
/* parse WavRes header */
|
||||
resclass_size = read_32bit(wavres_offset, sf);
|
||||
resclass_size = read_u32(wavres_offset, sf);
|
||||
wavres_offset += 0x04 + resclass_size + 0x08 + 0x04; /* skip class + cuiid + flags */
|
||||
|
||||
internal_size = read_32bit(wavres_offset + 0x00, sf);
|
||||
internal_size = read_u32(wavres_offset + 0x00, sf);
|
||||
/* Xbox has some kind of big size and "flags" has a value of 2, instead of 3/4 like other platforms */
|
||||
if (strcmp(class_name, "CXBoxWavResData") == 0 && internal_size > 0x100)
|
||||
return 1;
|
||||
|
@ -227,9 +228,10 @@ fail:
|
|||
|
||||
|
||||
/* parse a single known header resource at offset */
|
||||
static int parse_header(ubi_hx_header * hx, STREAMFILE *sf, off_t offset, size_t size, int index) {
|
||||
int32_t (*read_32bit)(off_t,STREAMFILE*) = hx->big_endian ? read_32bitBE : read_32bitLE;
|
||||
int16_t (*read_16bit)(off_t,STREAMFILE*) = hx->big_endian ? read_16bitBE : read_16bitLE;
|
||||
static int parse_header(ubi_hx_header* hx, STREAMFILE* sf, off_t offset, size_t size, int index) {
|
||||
uint32_t (*read_u32)(off_t,STREAMFILE*) = hx->big_endian ? read_u32be : read_u32le;
|
||||
int32_t (*read_s32)(off_t,STREAMFILE*) = hx->big_endian ? read_s32be : read_s32le;
|
||||
uint16_t (*read_u16)(off_t,STREAMFILE*) = hx->big_endian ? read_u16be : read_u16le;
|
||||
off_t riff_offset, riff_size, chunk_offset, stream_adjust = 0, resource_size;
|
||||
size_t chunk_size;
|
||||
int cue_flag = 0;
|
||||
|
@ -242,24 +244,28 @@ static int parse_header(ubi_hx_header * hx, STREAMFILE *sf, off_t offset, size_t
|
|||
hx->header_offset = offset;
|
||||
hx->header_size = size;
|
||||
|
||||
hx->class_size = read_32bit(offset + 0x00, sf);
|
||||
hx->class_size = read_u32(offset + 0x00, sf);
|
||||
if (hx->class_size > sizeof(hx->class_name)+1) goto fail;
|
||||
read_string(hx->class_name,hx->class_size+1, offset + 0x04, sf);
|
||||
offset += 0x04 + hx->class_size;
|
||||
|
||||
hx->cuuid1 = (uint32_t)read_32bit(offset + 0x00, sf);
|
||||
hx->cuuid2 = (uint32_t)read_32bit(offset + 0x04, sf);
|
||||
hx->cuuid1 = read_u32(offset + 0x00, sf);
|
||||
hx->cuuid2 = read_u32(offset + 0x04, sf);
|
||||
offset += 0x08;
|
||||
|
||||
if (strcmp(hx->class_name, "CPCWaveFileIdObj") == 0 ||
|
||||
strcmp(hx->class_name, "CPS2WaveFileIdObj") == 0 ||
|
||||
strcmp(hx->class_name, "CGCWaveFileIdObj") == 0 ||
|
||||
strcmp(hx->class_name, "CXBoxWaveFileIdObj") == 0) {
|
||||
uint32_t flag_type = read_32bit(offset + 0x00, sf);
|
||||
uint32_t flag_type = read_u32(offset + 0x00, sf);
|
||||
|
||||
if (flag_type == 0x01 || flag_type == 0x02) { /* Rayman Arena */
|
||||
if (read_32bit(offset + 0x04, sf) != 0x00) goto fail;
|
||||
hx->stream_mode = read_32bit(offset + 0x08, sf); /* flag: 0=internal, 1=external */
|
||||
uint32_t unk_value = read_u32(offset + 0x04, sf);
|
||||
if (unk_value != 0x00 && /* common */
|
||||
unk_value != 0xbe570a3d && /* Largo Winch: Empire Under Threat (PC)-most */
|
||||
unk_value != 0xbf8e147b) /* Largo Winch: Empire Under Threat (PC)-few */
|
||||
goto fail;
|
||||
hx->stream_mode = read_u32(offset + 0x08, sf); /* flag: 0=internal, 1=external */
|
||||
/* 0x0c: flag: 0=static, 1=stream */
|
||||
offset += 0x10;
|
||||
}
|
||||
|
@ -268,12 +274,12 @@ static int parse_header(ubi_hx_header * hx, STREAMFILE *sf, off_t offset, size_t
|
|||
offset += 0x08;
|
||||
|
||||
if (strcmp(hx->class_name, "CGCWaveFileIdObj") == 0) {
|
||||
if (read_32bit(offset + 0x00, sf) != read_32bit(offset + 0x04, sf)) goto fail; /* meaning? */
|
||||
hx->stream_mode = read_32bit(offset + 0x04, sf);
|
||||
if (read_u32(offset + 0x00, sf) != read_u32(offset + 0x04, sf)) goto fail; /* meaning? */
|
||||
hx->stream_mode = read_u32(offset + 0x04, sf);
|
||||
offset += 0x08;
|
||||
}
|
||||
else {
|
||||
hx->stream_mode = read_8bit(offset, sf);
|
||||
hx->stream_mode = read_u8(offset, sf);
|
||||
offset += 0x01;
|
||||
}
|
||||
}
|
||||
|
@ -284,7 +290,7 @@ static int parse_header(ubi_hx_header * hx, STREAMFILE *sf, off_t offset, size_t
|
|||
|
||||
/* get bizarro adjust (found in XIII external files) */
|
||||
if (hx->stream_mode == 0x0a) {
|
||||
stream_adjust = read_32bit(offset, sf); /* what */
|
||||
stream_adjust = read_u32(offset, sf); /* what */
|
||||
offset += 0x04;
|
||||
}
|
||||
|
||||
|
@ -292,66 +298,71 @@ static int parse_header(ubi_hx_header * hx, STREAMFILE *sf, off_t offset, size_t
|
|||
switch(hx->stream_mode) {
|
||||
case 0x00: /* memory (internal file) */
|
||||
riff_offset = offset;
|
||||
riff_size = read_32bit(riff_offset + 0x04, sf) + 0x08;
|
||||
riff_size = read_u32(riff_offset + 0x04, sf) + 0x08;
|
||||
break;
|
||||
|
||||
case 0x01: /* static (smaller external file) */
|
||||
case 0x03: /* stream (bigger external file) */
|
||||
case 0x07: /* static? */
|
||||
case 0x0a: /* static? */
|
||||
resource_size = read_32bit(offset + 0x00, sf);
|
||||
resource_size = read_u32(offset + 0x00, sf);
|
||||
if (resource_size > sizeof(hx->resource_name)+1) goto fail;
|
||||
read_string(hx->resource_name,resource_size+1, offset + 0x04, sf);
|
||||
|
||||
riff_offset = offset + 0x04 + resource_size;
|
||||
riff_size = read_32bit(riff_offset + 0x04, sf) + 0x08;
|
||||
riff_size = read_u32(riff_offset + 0x04, sf) + 0x08;
|
||||
|
||||
hx->is_external = 1;
|
||||
break;
|
||||
|
||||
default:
|
||||
VGM_LOG("UBI HX: %x\n", hx->stream_mode);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
|
||||
/* parse pseudo-RIFF "fmt" */
|
||||
if (read_32bit(riff_offset, sf) != 0x46464952) /* "RIFF" in machine endianness */
|
||||
if (read_u32(riff_offset, sf) != 0x46464952) /* "RIFF" in machine endianness */
|
||||
goto fail;
|
||||
|
||||
hx->codec_id = read_16bit(riff_offset + 0x14 , sf);
|
||||
hx->codec_id = read_u16(riff_offset + 0x14 , sf);
|
||||
switch(hx->codec_id) {
|
||||
case 0x01: hx->codec = PCM; break;
|
||||
case 0x02: hx->codec = UBI; break;
|
||||
case 0x03: hx->codec = PSX; break;
|
||||
case 0x04: hx->codec = DSP; break;
|
||||
case 0x05: hx->codec = XIMA; break;
|
||||
case 0x55: hx->codec = MP3; break; /* Largo Winch: Empire Under Threat (PC) */
|
||||
default:
|
||||
VGM_LOG("UBI HX: unknown codec %i\n", hx->codec_id);
|
||||
VGM_LOG("UBI HX: unknown codec %x\n", hx->codec_id);
|
||||
goto fail;
|
||||
}
|
||||
hx->channels = read_16bit(riff_offset + 0x16, sf);
|
||||
hx->sample_rate = read_32bit(riff_offset + 0x18, sf);
|
||||
hx->channels = read_u16(riff_offset + 0x16, sf);
|
||||
hx->sample_rate = read_u32(riff_offset + 0x18, sf);
|
||||
|
||||
/* find "datx" (external) or "data" (internal) also in machine endianness */
|
||||
if (hx->is_external) {
|
||||
|
||||
if (find_chunk_riff_ve(sf, 0x78746164,riff_offset + 0x0c,riff_size - 0x0c, &chunk_offset,NULL, hx->big_endian)) {
|
||||
hx->stream_size = read_32bit(chunk_offset + 0x00, sf);
|
||||
hx->stream_offset = read_32bit(chunk_offset + 0x04, sf) + stream_adjust;
|
||||
hx->stream_size = read_u32(chunk_offset + 0x00, sf);
|
||||
hx->stream_offset = read_u32(chunk_offset + 0x04, sf) + stream_adjust;
|
||||
}
|
||||
else if ((flag_type == 0x01 || flag_type == 0x02) && /* Rayman M (not Arena) uses "data" instead */
|
||||
find_chunk_riff_ve(sf, 0x61746164,riff_offset + 0x0c,riff_size - 0x0c, &chunk_offset,&chunk_size, hx->big_endian)) {
|
||||
hx->stream_size = chunk_size;
|
||||
hx->stream_offset = read_32bit(chunk_offset + 0x00, sf) + stream_adjust;
|
||||
hx->stream_offset = read_u32(chunk_offset + 0x00, sf) + stream_adjust;
|
||||
}
|
||||
else {
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!find_chunk_riff_ve(sf, 0x61746164,riff_offset + 0x0c,riff_size - 0x0c, &chunk_offset,NULL, hx->big_endian))
|
||||
if (!find_chunk_riff_ve(sf, 0x61746164,riff_offset + 0x0c,riff_size - 0x0c, &chunk_offset,&chunk_size, hx->big_endian))
|
||||
goto fail;
|
||||
hx->stream_offset = chunk_offset;
|
||||
hx->stream_size = riff_size - (chunk_offset - riff_offset);
|
||||
|
||||
if (chunk_size > riff_size - (chunk_offset - riff_offset) || !chunk_size)
|
||||
chunk_size = riff_size - (chunk_offset - riff_offset); /* just in case */
|
||||
hx->stream_size = chunk_size;
|
||||
}
|
||||
|
||||
/* can contain other RIFF stuff like "cue ", "labl" and "ump3"
|
||||
|
@ -362,13 +373,13 @@ static int parse_header(ubi_hx_header * hx, STREAMFILE *sf, off_t offset, size_t
|
|||
strcmp(hx->class_name, "CPS3StaticAC3WaveFileIdObj") == 0 ||
|
||||
strcmp(hx->class_name, "CPS3StreamAC3WaveFileIdObj") == 0) {
|
||||
|
||||
hx->stream_offset = read_32bit(offset + 0x00, sf);
|
||||
hx->stream_size = read_32bit(offset + 0x04, sf);
|
||||
hx->stream_offset = read_u32(offset + 0x00, sf);
|
||||
hx->stream_size = read_u32(offset + 0x04, sf);
|
||||
offset += 0x08;
|
||||
|
||||
//todo some dummy files have 0 size
|
||||
|
||||
if (read_32bit(offset + 0x00, sf) != 0x01) goto fail;
|
||||
if (read_u32(offset + 0x00, sf) != 0x01) goto fail;
|
||||
/* 0x04: some kind of parent id shared by multiple Waves, or 0 */
|
||||
offset += 0x08;
|
||||
|
||||
|
@ -385,27 +396,27 @@ static int parse_header(ubi_hx_header * hx, STREAMFILE *sf, off_t offset, size_t
|
|||
case 0x90: hx->channels = 2; break;
|
||||
default: goto fail;
|
||||
}
|
||||
hx->sample_rate = (uint16_t)(read_16bit(offset + 0x02, sf) & 0x7FFF) << 1; /* ??? */
|
||||
cue_flag = (uint8_t) read_8bit (offset + 0x03, sf) & (1<<7);
|
||||
hx->sample_rate = (read_u16(offset + 0x02, sf) & 0x7FFFu) << 1u; /* ??? */
|
||||
cue_flag = read_u8(offset + 0x03, sf) & (1<<7);
|
||||
offset += 0x04;
|
||||
}
|
||||
else if ((strcmp(hx->class_name, "CXBoxStaticHWWaveFileIdObj") == 0 ||
|
||||
strcmp(hx->class_name, "CXBoxStreamHWWaveFileIdObj") == 0) && hx->big_endian) {
|
||||
/* fake fmt chunk */
|
||||
hx->codec = XMA2;
|
||||
hx->channels = (uint16_t)read_16bit(offset + 0x02, sf);
|
||||
hx->sample_rate = read_32bit(offset + 0x04, sf);
|
||||
hx->num_samples = read_32bit(offset + 0x18, sf) / 0x02 / hx->channels;
|
||||
cue_flag = read_32bit(offset + 0x34, sf);
|
||||
hx->channels = read_u16(offset + 0x02, sf);
|
||||
hx->sample_rate = read_u32(offset + 0x04, sf);
|
||||
hx->num_samples = read_s32(offset + 0x18, sf) / 0x02 / hx->channels;
|
||||
cue_flag = read_u32(offset + 0x34, sf);
|
||||
offset += 0x38;
|
||||
}
|
||||
else {
|
||||
/* MSFC header */
|
||||
hx->codec = ATRAC3;
|
||||
hx->codec_id = read_32bit(offset + 0x04, sf);
|
||||
hx->channels = read_32bit(offset + 0x08, sf);
|
||||
hx->sample_rate = read_32bit(offset + 0x10, sf);
|
||||
cue_flag = read_32bit(offset + 0x40, sf);
|
||||
hx->codec_id = read_u32(offset + 0x04, sf);
|
||||
hx->channels = read_u32(offset + 0x08, sf);
|
||||
hx->sample_rate = read_u32(offset + 0x10, sf);
|
||||
cue_flag = read_u32(offset + 0x40, sf);
|
||||
offset += 0x44;
|
||||
}
|
||||
|
||||
|
@ -413,11 +424,11 @@ static int parse_header(ubi_hx_header * hx, STREAMFILE *sf, off_t offset, size_t
|
|||
if (cue_flag) {
|
||||
int j;
|
||||
|
||||
size_t cue_count = read_32bit(offset, sf);
|
||||
size_t cue_count = read_s32(offset, sf);
|
||||
offset += 0x04;
|
||||
for (j = 0; j < cue_count; j++) {
|
||||
/* 0x00: id? */
|
||||
size_t description_size = read_32bit(offset + 0x04, sf); /* for next string */
|
||||
size_t description_size = read_u32(offset + 0x04, sf); /* for next string */
|
||||
offset += 0x08 + description_size;
|
||||
}
|
||||
}
|
||||
|
@ -427,7 +438,7 @@ static int parse_header(ubi_hx_header * hx, STREAMFILE *sf, off_t offset, size_t
|
|||
case 0x01: /* static (smaller external file) */
|
||||
case 0x03: /* stream (bigger external file) */
|
||||
case 0x07: /* stream? */
|
||||
resource_size = read_32bit(offset + 0x00, sf);
|
||||
resource_size = read_u32(offset + 0x00, sf);
|
||||
if (resource_size > sizeof(hx->resource_name)+1) goto fail;
|
||||
read_string(hx->resource_name,resource_size+1, offset + 0x04, sf);
|
||||
|
||||
|
@ -450,22 +461,23 @@ fail:
|
|||
|
||||
|
||||
/* parse a bank index and its possible audio headers (some info from Droolie's .bms) */
|
||||
static int parse_hx(ubi_hx_header * hx, STREAMFILE *sf, int target_subsong) {
|
||||
int32_t (*read_32bit)(off_t,STREAMFILE*) = hx->big_endian ? read_32bitBE : read_32bitLE;
|
||||
static int parse_hx(ubi_hx_header* hx, STREAMFILE* sf, int target_subsong) {
|
||||
uint32_t (*read_u32)(off_t,STREAMFILE*) = hx->big_endian ? read_u32be : read_u32le;
|
||||
int32_t (*read_s32)(off_t,STREAMFILE*) = hx->big_endian ? read_s32be : read_s32le;
|
||||
off_t index_offset, offset;
|
||||
int i, index_entries;
|
||||
char class_name[255];
|
||||
|
||||
|
||||
index_offset = read_32bit(0x00, sf);
|
||||
if (read_32bit(index_offset + 0x00, sf) != 0x58444E49) /* "XDNI" (INDX in given endianness) */
|
||||
index_offset = read_u32(0x00, sf);
|
||||
if (read_u32(index_offset + 0x00, sf) != 0x58444E49) /* "XDNI" (INDX in given endianness) */
|
||||
goto fail;
|
||||
if (read_32bit(index_offset + 0x04, sf) != 0x02) /* type? */
|
||||
if (read_u32(index_offset + 0x04, sf) != 0x02) /* type? */
|
||||
goto fail;
|
||||
|
||||
if (target_subsong == 0) target_subsong = 1;
|
||||
|
||||
index_entries = read_32bit(index_offset + 0x08, sf);
|
||||
index_entries = read_s32(index_offset + 0x08, sf);
|
||||
offset = index_offset + 0x0c;
|
||||
for (i = 0; i < index_entries; i++) {
|
||||
off_t header_offset;
|
||||
|
@ -476,19 +488,19 @@ static int parse_hx(ubi_hx_header * hx, STREAMFILE *sf, int target_subsong) {
|
|||
|
||||
/* parse index entries: offset to actual header plus some extra info also in the header */
|
||||
|
||||
class_size = read_32bit(offset + 0x00, sf);
|
||||
class_size = read_u32(offset + 0x00, sf);
|
||||
if (class_size > sizeof(class_name)+1) goto fail;
|
||||
|
||||
read_string(class_name,class_size+1, offset + 0x04, sf); /* not null-terminated */
|
||||
offset += 0x04 + class_size;
|
||||
|
||||
/* 0x00: id1+2 */
|
||||
header_offset = read_32bit(offset + 0x08, sf);
|
||||
header_size = read_32bit(offset + 0x0c, sf);
|
||||
header_offset = read_u32(offset + 0x08, sf);
|
||||
header_size = read_u32(offset + 0x0c, sf);
|
||||
offset += 0x10;
|
||||
|
||||
/* not seen */
|
||||
unknown_count = read_32bit(offset + 0x00, sf);
|
||||
unknown_count = read_s32(offset + 0x00, sf);
|
||||
if (unknown_count != 0) {
|
||||
VGM_LOG("UBI HX: found unknown near %lx\n", offset);
|
||||
goto fail;
|
||||
|
@ -496,18 +508,18 @@ static int parse_hx(ubi_hx_header * hx, STREAMFILE *sf, int target_subsong) {
|
|||
offset += 0x04;
|
||||
|
||||
/* ids that this object directly points to (ex. Event > Random) */
|
||||
link_count = read_32bit(offset + 0x00, sf);
|
||||
link_count = read_s32(offset + 0x00, sf);
|
||||
offset += 0x04 + 0x08 * link_count;
|
||||
|
||||
/* localized id list of WavRes (can use this list instead of the prev one) */
|
||||
language_count = read_32bit(offset + 0x00, sf);
|
||||
language_count = read_s32(offset + 0x00, sf);
|
||||
offset += 0x04;
|
||||
for (j = 0; j < language_count; j++) {
|
||||
/* 0x00: lang code, in reverse endianness: "en ", "fr ", etc */
|
||||
/* 0x04: possibly count of ids for this lang */
|
||||
/* 0x08: id1+2 */
|
||||
|
||||
if (read_32bit(offset + 0x04, sf) != 1) {
|
||||
if (read_u32(offset + 0x04, sf) != 1) {
|
||||
VGM_LOG("UBI HX: wrong lang count near %lx\n", offset);
|
||||
goto fail; /* WavRes doesn't have this field */
|
||||
}
|
||||
|
@ -575,39 +587,39 @@ fail:
|
|||
}
|
||||
|
||||
|
||||
static STREAMFILE * open_hx_streamfile(ubi_hx_header *hx, STREAMFILE *sf) {
|
||||
STREAMFILE *streamData = NULL;
|
||||
static STREAMFILE* open_hx_streamfile(ubi_hx_header* hx, STREAMFILE* sf) {
|
||||
STREAMFILE* sb = NULL;
|
||||
|
||||
|
||||
if (!hx->is_external)
|
||||
return NULL;
|
||||
|
||||
streamData = open_streamfile_by_filename(sf, hx->resource_name);
|
||||
if (streamData == NULL) {
|
||||
sb = open_streamfile_by_filename(sf, hx->resource_name);
|
||||
if (sb == NULL) {
|
||||
VGM_LOG("UBI HX: external stream '%s' not found\n", hx->resource_name);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* streams often have a "RIFF" with "fmt" and "data" but stream offset/size is already adjusted to skip them */
|
||||
|
||||
return streamData;
|
||||
return sb;
|
||||
fail:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static VGMSTREAM * init_vgmstream_ubi_hx_header(ubi_hx_header *hx, STREAMFILE *streamFile) {
|
||||
STREAMFILE *streamTemp = NULL;
|
||||
STREAMFILE *streamData = NULL;
|
||||
static VGMSTREAM* init_vgmstream_ubi_hx_header(ubi_hx_header* hx, STREAMFILE* sf) {
|
||||
STREAMFILE* temp_sf = NULL;
|
||||
STREAMFILE* sb = NULL;
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
|
||||
|
||||
if (hx->is_external) {
|
||||
streamTemp = open_hx_streamfile(hx, streamFile);
|
||||
if (streamTemp == NULL) goto fail;
|
||||
streamData = streamTemp;
|
||||
temp_sf = open_hx_streamfile(hx, sf);
|
||||
if (temp_sf == NULL) goto fail;
|
||||
sb = temp_sf;
|
||||
}
|
||||
else {
|
||||
streamData = streamFile;
|
||||
sb = sf;
|
||||
}
|
||||
|
||||
|
||||
|
@ -630,7 +642,7 @@ static VGMSTREAM * init_vgmstream_ubi_hx_header(ubi_hx_header *hx, STREAMFILE *s
|
|||
break;
|
||||
|
||||
case UBI:
|
||||
vgmstream->codec_data = init_ubi_adpcm(streamData, hx->stream_offset, vgmstream->channels);
|
||||
vgmstream->codec_data = init_ubi_adpcm(sb, hx->stream_offset, vgmstream->channels);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->coding_type = coding_UBI_ADPCM;
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
@ -653,9 +665,9 @@ static VGMSTREAM * init_vgmstream_ubi_hx_header(ubi_hx_header *hx, STREAMFILE *s
|
|||
vgmstream->interleave_block_size = 0x08;
|
||||
|
||||
/* dsp header at start offset */
|
||||
vgmstream->num_samples = read_32bitBE(hx->stream_offset + 0x00, streamData);
|
||||
dsp_read_coefs_be(vgmstream, streamData, hx->stream_offset + 0x1c, 0x60);
|
||||
dsp_read_hist_be (vgmstream, streamData, hx->stream_offset + 0x40, 0x60);
|
||||
vgmstream->num_samples = read_s32be(hx->stream_offset + 0x00, sb);
|
||||
dsp_read_coefs_be(vgmstream, sb, hx->stream_offset + 0x1c, 0x60);
|
||||
dsp_read_hist_be (vgmstream, sb, hx->stream_offset + 0x40, 0x60);
|
||||
hx->stream_offset += 0x60 * hx->channels;
|
||||
hx->stream_size -= 0x60 * hx->channels;
|
||||
break;
|
||||
|
@ -676,14 +688,14 @@ static VGMSTREAM * init_vgmstream_ubi_hx_header(ubi_hx_header *hx, STREAMFILE *s
|
|||
block_count = hx->stream_size / block_size;
|
||||
|
||||
bytes = ffmpeg_make_riff_xma2(buf,0x200, hx->num_samples, hx->stream_size, hx->channels, hx->sample_rate, block_count, block_size);
|
||||
vgmstream->codec_data = init_ffmpeg_header_offset(streamData, buf,bytes, hx->stream_offset,hx->stream_size);
|
||||
vgmstream->codec_data = init_ffmpeg_header_offset(sb, buf,bytes, hx->stream_offset,hx->stream_size);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->coding_type = coding_FFmpeg;
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
||||
vgmstream->num_samples = hx->num_samples;
|
||||
|
||||
xma_fix_raw_samples_ch(vgmstream, streamData, hx->stream_offset,hx->stream_size, hx->channels, 0,0);
|
||||
xma_fix_raw_samples_ch(vgmstream, sb, hx->stream_offset,hx->stream_size, hx->channels, 0,0);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -700,7 +712,7 @@ static VGMSTREAM * init_vgmstream_ubi_hx_header(ubi_hx_header *hx, STREAMFILE *s
|
|||
|
||||
vgmstream->num_samples = atrac3_bytes_to_samples(hx->stream_size, block_align) - encoder_delay;
|
||||
|
||||
vgmstream->codec_data = init_ffmpeg_atrac3_raw(streamData, hx->stream_offset,hx->stream_size, vgmstream->num_samples,vgmstream->channels,vgmstream->sample_rate, block_align, encoder_delay);
|
||||
vgmstream->codec_data = init_ffmpeg_atrac3_raw(sb, hx->stream_offset, hx->stream_size, vgmstream->num_samples,vgmstream->channels,vgmstream->sample_rate, block_align, encoder_delay);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->coding_type = coding_FFmpeg;
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
@ -708,19 +720,34 @@ static VGMSTREAM * init_vgmstream_ubi_hx_header(ubi_hx_header *hx, STREAMFILE *s
|
|||
}
|
||||
#endif
|
||||
|
||||
#ifdef VGM_USE_MPEG
|
||||
case MP3: {
|
||||
mpeg_custom_config cfg = {0};
|
||||
|
||||
cfg.skip_samples = 0; /* ? */
|
||||
|
||||
vgmstream->codec_data = init_mpeg_custom(sb, hx->stream_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_STANDARD, &cfg);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
||||
vgmstream->num_samples = mpeg_get_samples_clean(sb, hx->stream_offset, hx->stream_size, NULL, NULL, 1);
|
||||
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
default:
|
||||
goto fail;
|
||||
}
|
||||
|
||||
strcpy(vgmstream->stream_name, hx->readable_name);
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream, streamData, hx->stream_offset))
|
||||
if (!vgmstream_open_stream(vgmstream, sb, hx->stream_offset))
|
||||
goto fail;
|
||||
close_streamfile(streamTemp);
|
||||
close_streamfile(temp_sf);
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_streamfile(streamTemp);
|
||||
close_streamfile(temp_sf);
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
#include <math.h>
|
||||
#include "meta.h"
|
||||
#include "../layout/layout.h"
|
||||
#include "../coding/coding.h"
|
||||
|
|
|
@ -3,17 +3,17 @@
|
|||
#include "../coding/coding.h"
|
||||
|
||||
|
||||
static int xa_read_subsongs(STREAMFILE *sf, int target_subsong, off_t start, uint16_t *p_stream_config, off_t *p_stream_offset, size_t *p_stream_size, int *p_form2);
|
||||
static int xa_check_format(STREAMFILE *sf, off_t offset, int is_blocked);
|
||||
static int xa_read_subsongs(STREAMFILE* sf, int target_subsong, off_t start, uint16_t* p_stream_config, off_t* p_stream_offset, size_t* p_stream_size, int* p_form2);
|
||||
static int xa_check_format(STREAMFILE* sf, off_t offset, int is_blocked);
|
||||
|
||||
/* XA - from Sony PS1 and Philips CD-i CD audio, also Saturn streams */
|
||||
VGMSTREAM * init_vgmstream_xa(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
VGMSTREAM* init_vgmstream_xa(STREAMFILE* sf) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
off_t start_offset;
|
||||
int loop_flag = 0, channel_count, sample_rate;
|
||||
int loop_flag = 0, channels, sample_rate, bps;
|
||||
int is_riff = 0, is_blocked = 0, is_form2 = 0;
|
||||
size_t stream_size = 0;
|
||||
int total_subsongs = 0, target_subsong = streamFile->stream_index;
|
||||
int total_subsongs = 0, target_subsong = sf->stream_index;
|
||||
uint16_t target_config = 0;
|
||||
|
||||
|
||||
|
@ -23,26 +23,27 @@ VGMSTREAM * init_vgmstream_xa(STREAMFILE *streamFile) {
|
|||
* .str: often videos and sometimes speech/music
|
||||
* .adp: Phantasy Star Collection (SAT) raw XA
|
||||
* .pxa: Mortal Kombat 4 (PS1)
|
||||
* .grn: Micro Machines (CDi)
|
||||
* (extensionless): bigfiles [Castlevania: Symphony of the Night (PS1)] */
|
||||
if (!check_extensions(streamFile,"xa,str,adp,pxa,"))
|
||||
if (!check_extensions(sf,"xa,str,adp,pxa,grn,"))
|
||||
goto fail;
|
||||
|
||||
/* Proper XA comes in raw (BIN 2352 mode2/form2) CD sectors, that contain XA subheaders.
|
||||
* Also has minimal support for headerless (ISO 2048 mode1/data) mode. */
|
||||
|
||||
/* check RIFF header = raw (optional, added when ripping and not part of the CD data) */
|
||||
if (read_u32be(0x00,streamFile) == 0x52494646 && /* "RIFF" */
|
||||
read_u32be(0x08,streamFile) == 0x43445841 && /* "CDXA" */
|
||||
read_u32be(0x0C,streamFile) == 0x666D7420) { /* "fmt " */
|
||||
if (read_u32be(0x00,sf) == 0x52494646 && /* "RIFF" */
|
||||
read_u32be(0x08,sf) == 0x43445841 && /* "CDXA" */
|
||||
read_u32be(0x0C,sf) == 0x666D7420) { /* "fmt " */
|
||||
is_blocked = 1;
|
||||
is_riff = 1;
|
||||
start_offset = 0x2c; /* after "data", ignore RIFF values as often are wrong */
|
||||
}
|
||||
else {
|
||||
/* sector sync word = raw */
|
||||
if (read_u32be(0x00,streamFile) == 0x00FFFFFF &&
|
||||
read_u32be(0x04,streamFile) == 0xFFFFFFFF &&
|
||||
read_u32be(0x08,streamFile) == 0xFFFFFF00) {
|
||||
if (read_u32be(0x00,sf) == 0x00FFFFFF &&
|
||||
read_u32be(0x04,sf) == 0xFFFFFFFF &&
|
||||
read_u32be(0x08,sf) == 0xFFFFFF00) {
|
||||
is_blocked = 1;
|
||||
start_offset = 0x00;
|
||||
}
|
||||
|
@ -53,26 +54,26 @@ VGMSTREAM * init_vgmstream_xa(STREAMFILE *streamFile) {
|
|||
}
|
||||
|
||||
/* test for XA data, since format is raw-ish (with RIFF it's assumed to be ok) */
|
||||
if (!is_riff && !xa_check_format(streamFile, start_offset, is_blocked))
|
||||
if (!is_riff && !xa_check_format(sf, start_offset, is_blocked))
|
||||
goto fail;
|
||||
|
||||
/* find subsongs as XA can interleave sectors using 'file' and 'channel' makers (see blocked_xa.c) */
|
||||
if (/*!is_riff &&*/ is_blocked) {
|
||||
total_subsongs = xa_read_subsongs(streamFile, target_subsong, start_offset, &target_config, &start_offset, &stream_size, &is_form2);
|
||||
total_subsongs = xa_read_subsongs(sf, target_subsong, start_offset, &target_config, &start_offset, &stream_size, &is_form2);
|
||||
if (total_subsongs <= 0) goto fail;
|
||||
}
|
||||
else {
|
||||
stream_size = get_streamfile_size(streamFile) - start_offset;
|
||||
stream_size = get_streamfile_size(sf) - start_offset;
|
||||
}
|
||||
|
||||
/* data is ok: parse header */
|
||||
if (is_blocked) {
|
||||
/* parse 0x18 sector header (also see blocked_xa.c) */
|
||||
uint8_t xa_header = read_u8(start_offset + 0x13,streamFile);
|
||||
uint8_t xa_header = read_u8(start_offset + 0x13,sf);
|
||||
|
||||
switch((xa_header >> 0) & 3) { /* 0..1: mono/stereo */
|
||||
case 0: channel_count = 1; break;
|
||||
case 1: channel_count = 2; break;
|
||||
case 0: channels = 1; break;
|
||||
case 1: channels = 2; break;
|
||||
default: goto fail;
|
||||
}
|
||||
switch((xa_header >> 2) & 3) { /* 2..3: sample rate */
|
||||
|
@ -80,11 +81,10 @@ VGMSTREAM * init_vgmstream_xa(STREAMFILE *streamFile) {
|
|||
case 1: sample_rate = 18900; break;
|
||||
default: goto fail;
|
||||
}
|
||||
switch((xa_header >> 4) & 3) { /* 4..5: bits per sample (0=4-bit ADPCM, 1=8-bit ADPCM) */
|
||||
case 0: break;
|
||||
default: /* PS1 games only do 4-bit */
|
||||
VGM_LOG("XA: unknown bits per sample found\n");
|
||||
goto fail;
|
||||
switch((xa_header >> 4) & 3) { /* 4..5: bits per sample */
|
||||
case 0: bps = 4; break; /* PS1 games only do 4-bit ADPCM */
|
||||
case 1: bps = 8; break; /* Micro Machines (CDi) */
|
||||
default: goto fail;
|
||||
}
|
||||
switch((xa_header >> 6) & 1) { /* 6: emphasis (applies a filter) */
|
||||
case 0: break;
|
||||
|
@ -101,38 +101,44 @@ VGMSTREAM * init_vgmstream_xa(STREAMFILE *streamFile) {
|
|||
}
|
||||
else {
|
||||
/* headerless */
|
||||
if (check_extensions(streamFile,"adp")) {
|
||||
if (check_extensions(sf,"adp")) {
|
||||
/* Phantasy Star Collection (SAT) raw files */
|
||||
/* most are stereo, though a few (mainly sfx banks, sometimes using .bin) are mono */
|
||||
|
||||
char filename[PATH_LIMIT] = {0};
|
||||
get_streamfile_filename(streamFile, filename,PATH_LIMIT);
|
||||
get_streamfile_filename(sf, filename,PATH_LIMIT);
|
||||
|
||||
/* detect PS1 mono files, very lame but whatevs, no way to detect XA mono/stereo */
|
||||
if (filename[0]=='P' && filename[1]=='S' && filename[2]=='1' && filename[3]=='S') {
|
||||
channel_count = 1;
|
||||
channels = 1;
|
||||
sample_rate = 22050;
|
||||
}
|
||||
else {
|
||||
channel_count = 2;
|
||||
channels = 2;
|
||||
sample_rate = 44100;
|
||||
}
|
||||
bps = 4;
|
||||
}
|
||||
else {
|
||||
/* incorrectly ripped standard XA */
|
||||
channel_count = 2;
|
||||
channels = 2;
|
||||
sample_rate = 37800;
|
||||
bps = 4;
|
||||
}
|
||||
}
|
||||
|
||||
/* untested */
|
||||
if (bps == 8 && channels == 1)
|
||||
goto fail;
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channel_count,loop_flag);
|
||||
vgmstream = allocate_vgmstream(channels, loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->meta_type = meta_XA;
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
vgmstream->coding_type = coding_XA;
|
||||
vgmstream->coding_type = bps == 8 ? coding_XA8 : coding_XA;
|
||||
vgmstream->layout_type = is_blocked ? layout_blocked_xa : layout_none;
|
||||
if (is_blocked) {
|
||||
vgmstream->codec_config = target_config;
|
||||
|
@ -144,9 +150,9 @@ VGMSTREAM * init_vgmstream_xa(STREAMFILE *streamFile) {
|
|||
}
|
||||
}
|
||||
|
||||
vgmstream->num_samples = xa_bytes_to_samples(stream_size, channel_count, is_blocked, is_form2);
|
||||
vgmstream->num_samples = xa_bytes_to_samples(stream_size, channels, is_blocked, is_form2, bps);
|
||||
|
||||
if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) )
|
||||
if (!vgmstream_open_stream(vgmstream, sf, start_offset))
|
||||
goto fail;
|
||||
return vgmstream;
|
||||
|
||||
|
@ -230,7 +236,7 @@ typedef struct xa_subsong_t {
|
|||
*
|
||||
* Bigfiles that paste tons of XA together are slow to parse since we need to read every sector to
|
||||
* count totals, but XA subsong handling is mainly for educational purposes. */
|
||||
static int xa_read_subsongs(STREAMFILE *sf, int target_subsong, off_t start, uint16_t *p_stream_config, off_t *p_stream_offset, size_t *p_stream_size, int *p_form2) {
|
||||
static int xa_read_subsongs(STREAMFILE* sf, int target_subsong, off_t start, uint16_t* p_stream_config, off_t* p_stream_offset, size_t* p_stream_size, int* p_form2) {
|
||||
xa_subsong_t *cur_subsong = NULL;
|
||||
xa_subsong_t subsongs[XA_SUBSONG_MAX] = {0};
|
||||
const size_t sector_size = 0x930;
|
||||
|
|
|
@ -351,10 +351,11 @@ VGMSTREAM* init_vgmstream_xwb(STREAMFILE* sf) {
|
|||
/* Stardew Valley (Switch), Skulls of the Shogun (Switch): full interleaved DSPs (including headers) */
|
||||
xwb.codec = DSP;
|
||||
}
|
||||
else if (xwb.version == XACT3_0_MAX && xwb.codec == XMA2
|
||||
&& xwb.bits_per_sample == 0x01 && xwb.block_align == 0x04
|
||||
&& xwb.data_size == 0x4e0a1000) { /* some kind of id? */
|
||||
/* Stardew Valley (Vita), standard RIFF with ATRAC9 */
|
||||
else if (xwb.version == XACT3_0_MAX && (xwb.codec == XMA2 || xwb.codec == PCM)
|
||||
&& xwb.bits_per_sample == 0x01 && xwb.block_align == 0x02*xwb.channels
|
||||
&& is_id32be(xwb.stream_offset, sf, "RIFF") /* clashes with XMA2 */
|
||||
/*&& xwb.data_size == 0x4e0a1000*/) { /* some kind of id in Stardew Valley? */
|
||||
/* Stardew Valley (Vita), Owlboy (PS4): standard RIFF with ATRAC9 */
|
||||
xwb.codec = ATRAC9_RIFF;
|
||||
}
|
||||
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -107,7 +107,8 @@ typedef enum {
|
|||
|
||||
coding_G721, /* CCITT G.721 */
|
||||
|
||||
coding_XA, /* CD-ROM XA */
|
||||
coding_XA, /* CD-ROM XA 4-bit */
|
||||
coding_XA8, /* CD-ROM XA 8-bit */
|
||||
coding_PSX, /* Sony PS ADPCM (VAG) */
|
||||
coding_PSX_badflags, /* Sony PS ADPCM with custom flag byte */
|
||||
coding_PSX_cfg, /* Sony PS ADPCM with configurable frame size (int math) */
|
||||
|
@ -198,6 +199,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) */
|
||||
|
@ -759,6 +761,7 @@ typedef enum {
|
|||
meta_KTAC,
|
||||
meta_MJB_MJH,
|
||||
meta_BSNF,
|
||||
meta_TAC,
|
||||
} meta_t;
|
||||
|
||||
/* standard WAVEFORMATEXTENSIBLE speaker positions */
|
||||
|
@ -1053,7 +1056,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)
|
||||
|
||||
|
@ -1066,7 +1069,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;
|
||||
|
@ -1078,7 +1081,7 @@ typedef struct {
|
|||
|
||||
// FFmpeg context used for metadata
|
||||
AVCodec *codec;
|
||||
|
||||
|
||||
// FFmpeg decoder state
|
||||
unsigned char *buffer;
|
||||
AVIOContext *ioCtx;
|
||||
|
|
Loading…
Reference in New Issue