Updated VGMStream to r1690-35-gc38c09fb

CQTexperiment
Christopher Snowhill 2021-12-30 23:45:02 -08:00
parent 16b1bfea03
commit 78fa5accc0
32 changed files with 1400 additions and 1056 deletions

View File

@ -27,6 +27,7 @@
83031EDB243C510500C3F3E0 /* xnb_lz4mg.h in Headers */ = {isa = PBXBuildFile; fileRef = 83031ED6243C510400C3F3E0 /* xnb_lz4mg.h */; };
83031EDC243C510500C3F3E0 /* vid1.c in Sources */ = {isa = PBXBuildFile; fileRef = 83031ED7243C510400C3F3E0 /* vid1.c */; };
83031EDD243C510500C3F3E0 /* xnb_streamfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 83031ED8243C510500C3F3E0 /* xnb_streamfile.h */; };
830595D8277EEAA500EBFAAE /* ffmpeg_decoder_custom_mp4.c in Sources */ = {isa = PBXBuildFile; fileRef = 830595D7277EEAA500EBFAAE /* ffmpeg_decoder_custom_mp4.c */; };
8306B08420984518000302D4 /* at3plus_decoder.c in Sources */ = {isa = PBXBuildFile; fileRef = 8306B08120984517000302D4 /* at3plus_decoder.c */; };
8306B08520984518000302D4 /* yamaha_decoder.c in Sources */ = {isa = PBXBuildFile; fileRef = 8306B08220984517000302D4 /* yamaha_decoder.c */; };
8306B08620984518000302D4 /* fadpcm_decoder.c in Sources */ = {isa = PBXBuildFile; fileRef = 8306B08320984517000302D4 /* fadpcm_decoder.c */; };
@ -377,13 +378,12 @@
836F6FF218BDC2190095E648 /* ps2_rnd.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EB618BDC2180095E648 /* ps2_rnd.c */; };
836F6FF318BDC2190095E648 /* ps2_rstm.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EB718BDC2180095E648 /* ps2_rstm.c */; };
836F6FF418BDC2190095E648 /* rws.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EB818BDC2180095E648 /* rws.c */; };
836F6FF618BDC2190095E648 /* ps2_sfs.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EBA18BDC2180095E648 /* ps2_sfs.c */; };
836F6FF618BDC2190095E648 /* ster.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EBA18BDC2180095E648 /* ster.c */; };
836F6FF718BDC2190095E648 /* ps2_sl3.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EBB18BDC2180095E648 /* ps2_sl3.c */; };
836F6FF818BDC2190095E648 /* ps2_smpl.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EBC18BDC2180095E648 /* ps2_smpl.c */; };
836F6FF918BDC2190095E648 /* ps2_snd.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EBD18BDC2180095E648 /* ps2_snd.c */; };
836F6FFA18BDC2190095E648 /* spm.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EBE18BDC2190095E648 /* spm.c */; };
836F6FFB18BDC2190095E648 /* ps2_sps.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EBF18BDC2190095E648 /* ps2_sps.c */; };
836F6FFC18BDC2190095E648 /* ps2_ster.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EC018BDC2190095E648 /* ps2_ster.c */; };
836F700118BDC2190095E648 /* ps2_tec.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EC518BDC2190095E648 /* ps2_tec.c */; };
836F700218BDC2190095E648 /* ps2_tk5.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EC618BDC2190095E648 /* ps2_tk5.c */; };
836F700418BDC2190095E648 /* ps2_vas.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EC818BDC2190095E648 /* ps2_vas.c */; };
@ -846,6 +846,7 @@
83031ED6243C510400C3F3E0 /* xnb_lz4mg.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = xnb_lz4mg.h; sourceTree = "<group>"; };
83031ED7243C510400C3F3E0 /* vid1.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = vid1.c; sourceTree = "<group>"; };
83031ED8243C510500C3F3E0 /* xnb_streamfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = xnb_streamfile.h; sourceTree = "<group>"; };
830595D7277EEAA500EBFAAE /* ffmpeg_decoder_custom_mp4.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ffmpeg_decoder_custom_mp4.c; sourceTree = "<group>"; };
8306B08120984517000302D4 /* at3plus_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = at3plus_decoder.c; sourceTree = "<group>"; };
8306B08220984517000302D4 /* yamaha_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = yamaha_decoder.c; sourceTree = "<group>"; };
8306B08320984517000302D4 /* fadpcm_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = fadpcm_decoder.c; sourceTree = "<group>"; };
@ -1195,13 +1196,12 @@
836F6EB618BDC2180095E648 /* ps2_rnd.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_rnd.c; sourceTree = "<group>"; };
836F6EB718BDC2180095E648 /* ps2_rstm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_rstm.c; sourceTree = "<group>"; };
836F6EB818BDC2180095E648 /* rws.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = rws.c; sourceTree = "<group>"; };
836F6EBA18BDC2180095E648 /* ps2_sfs.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_sfs.c; sourceTree = "<group>"; };
836F6EBA18BDC2180095E648 /* ster.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ster.c; sourceTree = "<group>"; };
836F6EBB18BDC2180095E648 /* ps2_sl3.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_sl3.c; sourceTree = "<group>"; };
836F6EBC18BDC2180095E648 /* ps2_smpl.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_smpl.c; sourceTree = "<group>"; };
836F6EBD18BDC2180095E648 /* ps2_snd.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_snd.c; sourceTree = "<group>"; };
836F6EBE18BDC2190095E648 /* spm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = spm.c; sourceTree = "<group>"; };
836F6EBF18BDC2190095E648 /* ps2_sps.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_sps.c; sourceTree = "<group>"; };
836F6EC018BDC2190095E648 /* ps2_ster.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_ster.c; sourceTree = "<group>"; };
836F6EC518BDC2190095E648 /* ps2_tec.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_tec.c; sourceTree = "<group>"; };
836F6EC618BDC2190095E648 /* ps2_tk5.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_tk5.c; sourceTree = "<group>"; };
836F6EC818BDC2190095E648 /* ps2_vas.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_vas.c; sourceTree = "<group>"; };
@ -1676,6 +1676,7 @@
83AA5D0E1F6E2F5F0020821C /* ea_xa_decoder.c */,
83AA5D151F6E2F600020821C /* ea_xas_decoder.c */,
8306B08320984517000302D4 /* fadpcm_decoder.c */,
830595D7277EEAA500EBFAAE /* ffmpeg_decoder_custom_mp4.c */,
834FE0AB215C798A000A5D3D /* ffmpeg_decoder_custom_opus.c */,
837CEA7723487E2400E62A4A /* ffmpeg_decoder_utils.c */,
838BDB6B1D3AFAB10022CA6F /* ffmpeg_decoder.c */,
@ -2086,12 +2087,10 @@
836F6EB318BDC2180095E648 /* ps2_pnb.c */,
836F6EB618BDC2180095E648 /* ps2_rnd.c */,
836F6EB718BDC2180095E648 /* ps2_rstm.c */,
836F6EBA18BDC2180095E648 /* ps2_sfs.c */,
836F6EBB18BDC2180095E648 /* ps2_sl3.c */,
836F6EBC18BDC2180095E648 /* ps2_smpl.c */,
836F6EBD18BDC2180095E648 /* ps2_snd.c */,
836F6EBF18BDC2190095E648 /* ps2_sps.c */,
836F6EC018BDC2190095E648 /* ps2_ster.c */,
836F6EC518BDC2190095E648 /* ps2_tec.c */,
836F6EC618BDC2190095E648 /* ps2_tk5.c */,
832BF80D21E05148006F50F1 /* ps2_va3.c */,
@ -2163,6 +2162,7 @@
837CEAF023487F2C00E62A4A /* sqex_sead_streamfile.h */,
83A21F84201D8981000F04B9 /* sqex_sead.c */,
8317C24826982CC1007DD0B8 /* sspr.c */,
836F6EBA18BDC2180095E648 /* ster.c */,
8306B0C12098458C000302D4 /* sthd.c */,
83AA5D231F6E2F9C0020821C /* stm.c */,
836F6EF718BDC2190095E648 /* str_snds.c */,
@ -2700,7 +2700,6 @@
8322ECE7240268BB009E9429 /* raw_al.c in Sources */,
83EDE5D91A70951A005F5D84 /* btsnd.c in Sources */,
8306B0E420984590000302D4 /* wave.c in Sources */,
836F6FFC18BDC2190095E648 /* ps2_ster.c in Sources */,
8349A9171FE6258200E26435 /* pc_adp_otns.c in Sources */,
836F701E18BDC2190095E648 /* redspark.c in Sources */,
8306B0ED20984590000302D4 /* txtp.c in Sources */,
@ -2795,6 +2794,7 @@
835B9B912730BF2D00F87EE3 /* lopu_fb.c in Sources */,
8346D97B25BF838C00D1A8B0 /* ktac.c in Sources */,
8349A8ED1FE6253900E26435 /* blocked_ea_sns.c in Sources */,
830595D8277EEAA500EBFAAE /* ffmpeg_decoder_custom_mp4.c in Sources */,
836F6F6F18BDC2190095E648 /* akb.c in Sources */,
83EED5D6203A8BD7008BEB45 /* blocked_ea_swvr.c in Sources */,
8349A9181FE6258200E26435 /* ea_1snh.c in Sources */,
@ -3007,7 +3007,7 @@
836F702A18BDC2190095E648 /* sd9.c in Sources */,
836F6FB418BDC2190095E648 /* ngc_nst_dsp.c in Sources */,
836F6FDB18BDC2190095E648 /* ps2_hsf.c in Sources */,
836F6FF618BDC2190095E648 /* ps2_sfs.c in Sources */,
836F6FF618BDC2190095E648 /* ster.c in Sources */,
834FE10E215C79ED000A5D3D /* ahv.c in Sources */,
8306B08420984518000302D4 /* at3plus_decoder.c in Sources */,
8349A90E1FE6258200E26435 /* scd_pcm.c in Sources */,

View File

@ -338,7 +338,7 @@ typedef struct {
} hca_keytest_t;
void test_hca_key(hca_codec_data* data, hca_keytest_t* hk);
void hca_set_encryption_key(hca_codec_data* data, uint64_t keycode);
void hca_set_encryption_key(hca_codec_data* data, uint64_t keycode, uint64_t subkey);
STREAMFILE* hca_get_streamfile(hca_codec_data* data);
@ -639,6 +639,26 @@ size_t switch_opus_get_encoder_delay(off_t offset, STREAMFILE* sf);
size_t ue4_opus_get_encoder_delay(off_t offset, STREAMFILE* sf);
size_t ea_opus_get_encoder_delay(off_t offset, STREAMFILE* sf);
size_t fsb_opus_get_encoder_delay(off_t offset, STREAMFILE* sf);
/* ffmpeg_decoder_custom_mp4.c*/
typedef struct {
int channels;
int sample_rate;
int32_t num_samples;
uint32_t stream_offset;
uint32_t stream_size;
uint32_t table_offset;
uint32_t table_entries;
int encoder_delay;
int end_padding;
int frame_samples;
} mp4_custom_t;
ffmpeg_codec_data* init_ffmpeg_mp4_custom_std(STREAMFILE* sf, mp4_custom_t* mp4);
ffmpeg_codec_data* init_ffmpeg_mp4_custom_lyn(STREAMFILE* sf, mp4_custom_t* mp4);
#endif

View File

@ -0,0 +1,538 @@
#include "coding.h"
#include "../streamfile.h"
#include "../meta/deblock_streamfile.h"
#ifdef VGM_USE_FFMPEG
typedef enum { MP4_STD, MP4_LYN } mp4_type_t;
/**
* Makes a MP4 header for MP4 raw data with a separate frame table, simulating a real MP4 that
* also has such table embedded in their custom chunks.
*/
//TODO: segfaults with certain audio files (ffmpeg?)
/* *********************************************************** */
/* Helpers to make a M4A header, an insane soup of chunks (AKA "atoms").
* Needs *A LOT* of atoms and fields so this is more elaborate than usual.
* - https://developer.apple.com/library/archive/documentation/QuickTime/QTFF/QTFFPreface/qtffPreface.html
*/
/* generic additions */
typedef struct {
uint8_t* out;
int bytes;
} m4a_state_t;
typedef struct {
STREAMFILE* sf;
mp4_custom_t* mp4; /* config */
mp4_type_t type;
uint8_t* out; /* current position */
int bytes; /* written bytes */
m4a_state_t chunks; /* chunks offsets are absolute, save position until we know header size */
} m4a_header_t;
static void add_u32b(m4a_header_t* h, uint32_t value) {
put_u32be(h->out, value);
h->out += 0x04;
h->bytes += 0x04;
}
static void add_u24b(m4a_header_t* h, uint32_t value) {
put_u16be(h->out + 0x00, (value >> 8u) & 0xFFFF);
put_u8 (h->out + 0x02, (value >> 0u) & 0xFF);
h->out += 0x03;
h->bytes += 0x03;
}
static void add_u16b(m4a_header_t* h, uint16_t value) {
put_u16be(h->out, value);
h->out += 0x02;
h->bytes += 0x02;
}
static void add_u8(m4a_header_t* h, uint32_t value) {
put_u8(h->out, value);
h->out += 0x01;
h->bytes += 0x01;
}
static void add_name(m4a_header_t* h, const char* name) {
memcpy(h->out, name, 0x4);
h->out += 0x04;
h->bytes += 0x04;
}
static void add_atom(m4a_header_t* h, const char* name, uint32_t size) {
add_u32b(h, size);
add_name(h, name);
}
/* register + write final size for atoms of variable/complex size */
static void save_atom(m4a_header_t* h, m4a_state_t* s) {
s->out = h->out;
s->bytes = h->bytes;
}
static void load_atom(m4a_header_t* h, m4a_state_t* s) {
put_u32be(s->out, h->bytes - s->bytes);
}
/* common atoms */
static void add_ftyp(m4a_header_t* h) {
add_atom(h, "ftyp", 0x18);
add_name(h, "M4A "); /* major brand */
add_u32b(h, 512); /* minor version */
add_name(h, "isom"); /* compatible brands */
add_name(h, "iso2"); /* compatible brands */
}
static void add_free(m4a_header_t* h) {
add_atom(h, "free", 0x08);
}
static void add_mdat(m4a_header_t* h) {
add_atom(h, "mdat", 0x08 + h->mp4->stream_size);
}
/* variable atoms */
static void add_stco(m4a_header_t* h) {
add_atom(h, "stco", 0x10 + 1 * 0x04);
add_u32b(h, 0); /* Version (1 byte) + Flags (3 byte) */
add_u32b(h, 1); /* Number of entries */
/* there may be an entry per frame, but only first seems needed */
save_atom(h, &h->chunks);
add_u32b(h, 0); /* Absolute offset N */
}
static void add_stsz(m4a_header_t* h) {
int i;
uint32_t size;
add_atom(h, "stsz", 0x14 + h->mp4->table_entries * 0x04);
add_u32b(h, 0); /* Version (1 byte) + Flags (3 byte) */
add_u32b(h, 0); /* Sample size (CBR) */
add_u32b(h, h->mp4->table_entries); /* Number of entries (VBR) */
switch(h->type) {
case MP4_LYN: {
uint32_t curr_size, next_size;
/* LyN has a seek table with every frame, and frames are preprended by a 0x02
* frame header with frame size, so we can reconstruct a frame table */
for (i = 0; i < h->mp4->table_entries - 1; i++) {
curr_size = read_u32le(h->mp4->table_offset + (i + 0) * 0x04, h->sf);
next_size = read_u32le(h->mp4->table_offset + (i + 1) * 0x04, h->sf);
size = next_size - curr_size - 0x02;
add_u32b(h, size); /* Sample N */
//;VGM_LOG("%i: %x (%x: %x - %x - 0x02)\n", i, size, h->mp4->table_offset + (i + 1) * 0x04, next_size, curr_size);
}
curr_size = read_u32le(h->mp4->table_offset + (i + 0) * 0x04, h->sf);
next_size = h->mp4->stream_size; /* no last offset */
size = next_size - curr_size - 0x02;
add_u32b(h, size); /* Sample N */
//;VGM_LOG("%i: %x\n", i, size);
break;
}
default: {
for (i = 0; i < h->mp4->table_entries; i++) {
size = read_u32le(h->mp4->table_offset + i * 0x04, h->sf);
add_u32b(h, size); /* Sample N */
}
break;
}
}
}
static void add_stsc(m4a_header_t* h) {
add_atom(h, "stsc", 0x1c);
add_u32b(h, 0); /* Version (1 byte) + Flags (3 byte) */
add_u32b(h, 1); /* Number of entries */
add_u32b(h, 1); /* First chunk */
add_u32b(h, h->mp4->table_entries); /* Samples per chunk */
add_u32b(h, 1); /* Sample description ID */
}
static void add_stts(m4a_header_t* h) {
add_atom(h, "stts", 0x18);
add_u32b(h, 0); /* Version (1 byte) + Flags (3 byte) */
add_u32b(h, 1); /* Number of entries */
add_u32b(h, h->mp4->table_entries); /* Sample count */
add_u32b(h, h->mp4->frame_samples); /* Sample duration */
}
/* from mpeg4audio.c (also see ff_mp4_read_dec_config_descr) */
static const int m4a_sample_rates[16] = {
96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350
};
static const uint8_t m4a_channels[14] = {
0,
1, // mono (1/0)
2, // stereo (2/0)
3, // 3/0
4, // 3/1
5, // 3/2
6, // 3/2.1
8, // 5/2.1
//0,
//0,
//0,
//7, // 3/3.1
//8, // 3/2/2.1
//24 // 3/3/3 - 5/2/3 - 3/0/0.2
};
static void add_esds(m4a_header_t* h) {
uint16_t config = 0;
/* ES_descriptor (TLV format see ISO 14496-1) and DecSpecificInfoTag define actual decoding
- config (channels/rate/etc), other atoms with the same stuff is just info
* - http://ecee.colorado.edu/~ecen5653/ecen5653/papers/ISO%2014496-1%202004.PDF */
{
uint8_t object_type = 0x02; /* 0x00=none, 0x01=AAC main, 0x02=AAC LC */
uint8_t sr_index = 0;
uint8_t ch_index = 0;
uint8_t unknown = 0;
int i;
for (i = 0; i < 16; i++) {
if (m4a_sample_rates[i] == h->mp4->sample_rate) {
sr_index = i;
break;
}
}
for (i = 0; i < 8; i++) {
if (m4a_channels[i] == h->mp4->channels) {
ch_index = i;
break;
}
}
config |= (object_type & 0x1F) << 11; /* 5b */
config |= (sr_index & 0x0F) << 7; /* 4b */
config |= (ch_index & 0x0F) << 3; /* 4b */
config |= (unknown & 0x07) << 0; /* 3b */
}
add_atom(h, "esds", 0x33);
add_u32b(h, 0); /* Version (1 byte) + Flags (3 byte) */
add_u8 (h, 0x03); /* ES_DescrTag */
add_u32b(h, 0x80808022); /* size 0x22 */
add_u16b(h, 0x0000); /* stream Id */
add_u8 (h, 0x00); /* flags */
add_u8 (h, 0x04); /* DecoderConfigDescrTag */
add_u32b(h, 0x80808014); /* size 0x14 */
add_u8 (h, 0x40); /* object type (0x40=audio) */
add_u8 (h, 0x15); /* stream type (6b: 0x5=audio) + upstream (1b) + reserved (1b: const 1) */
add_u24b(h, 0x000000); /* buffer size */
add_u32b(h, 0); /* max bitrate (256000?)*/
add_u32b(h, 0); /* average bitrate (256000?) */
add_u8 (h, 0x05); /* DecSpecificInfoTag */
add_u32b(h, 0x80808002); /* size 0x02 */
add_u16b(h, config); /* actual decoder info */
add_u8 (h, 0x06); /* SLConfigDescrTag */
add_u32b(h, 0x80808001); /* size 0x01 */
add_u8 (h, 0x02); /* predefined (2=default) */
}
static void add_mp4a(m4a_header_t* h) {
add_atom(h, "mp4a", 0x57);
add_u32b(h, 0); /* ? */
add_u32b(h, 1); /* Data reference index */
add_u32b(h, 0); /* Reserved */
add_u32b(h, 0); /* Reserved 2 */
add_u16b(h, h->mp4->channels); /* Channel count */
add_u16b(h, 16); /* Sample size */
add_u32b(h, 0); /* Pre-defined */
add_u16b(h, h->mp4->sample_rate); /* Sample rate */
add_u16b(h, 0); /* ? */
add_esds(h); /* elementary stream descriptor */
}
static void add_stsd(m4a_header_t* h) {
add_atom(h, "stsd", 0x67);
add_u32b(h, 0); /* Version (1 byte) + Flags (3 byte) */
add_u32b(h, 1); /* Number of entries */
add_mp4a(h);
}
static void add_stbl(m4a_header_t* h) {
m4a_state_t s;
save_atom(h, &s);
add_atom(h, "stbl", 0x00);
add_stsd(h); /* Sample description */
add_stts(h); /* Time-to-sample */
add_stsc(h); /* Sample-to-chunk */
add_stsz(h); /* Sample size */
add_stco(h); /* Chunk offset */
load_atom(h, &s);
}
static void add_dinf(m4a_header_t* h) {
add_atom(h, "dinf", 0x24);
add_atom(h, "dref", 0x1c);
add_u32b(h, 0); /* Version (1 byte) + Flags (3 byte) */
add_u32b(h, 1); /* Number of entries */
add_atom(h, "url ", 0x0c);
add_u32b(h, 1); /* Version (1 byte) + Flags (3 byte) */
}
static void add_smhd(m4a_header_t* h) {
add_atom(h, "smhd", 0x10);
add_u32b(h, 0); /* Version (1 byte) + Flags (3 byte) */
add_u16b(h, 0); /* Balance */
add_u16b(h, 0); /* Reserved */
}
static void add_minf(m4a_header_t* h) {
m4a_state_t s;
save_atom(h, &s);
add_atom(h, "minf", 0x00);
add_smhd(h);
add_dinf(h);
add_stbl(h);
load_atom(h, &s);
}
static void add_hdlr(m4a_header_t* h) {
add_atom(h, "hdlr", 0x22);
add_u32b(h, 0); /* version (1 byte) + flags (3 byte) */
add_u32b(h, 0); /* Component type */
add_name(h, "soun"); /* Component subtype */
add_u32b(h, 0); /* Component manufacturer */
add_u32b(h, 0); /* Component flags */
add_u32b(h, 0); /* Component flags mask */
add_u16b(h, 0); /* Component name */
}
static void add_mdhd(m4a_header_t* h) {
add_atom(h, "mdhd", 0x20);
add_u32b(h, 0); /* Version (1 byte) + Flags (3 byte) */
add_u32b(h, 0); /* Creation time */
add_u32b(h, 0); /* Modification time */
add_u32b(h, h->mp4->sample_rate); /* Time scale */
add_u32b(h, h->mp4->num_samples); /* Duration */
add_u16b(h, 0); /* Language (0xC455=eng?) */
add_u16b(h, 0); /* Quality */
}
static void add_mdia(m4a_header_t* h) {
m4a_state_t s;
save_atom(h, &s);
add_atom(h, "mdia", 0x00);
add_mdhd(h);
add_hdlr(h);
add_minf(h);
load_atom(h, &s);
}
static void add_tkhd(m4a_header_t* h) {
add_atom(h, "tkhd", 0x5C);
add_u32b(h, 0x00000001); /* Version (1 byte) + Flags (3 byte), 1=track enabled */
add_u32b(h, 0); /* Creation time */
add_u32b(h, 0); /* Modification time */
add_u32b(h, 1); /* Track ID */
add_u32b(h, 0); /* Reserved 1 */
add_u32b(h, h->mp4->num_samples); /* Duration */
add_u32b(h, 0); /* Reserved 1 */
add_u32b(h, 0); /* Reserved 2 */
add_u16b(h, 0); /* Layer */
add_u16b(h, 0); /* Alternate group (1?) */
add_u16b(h, 0x0100); /* Volume */
add_u16b(h, 0); /* Reserved */
add_u32b(h, 0x00010000); /* matrix_A */
add_u32b(h, 0); /* matrix_B */
add_u32b(h, 0); /* matrix_U */
add_u32b(h, 0); /* matrix_C */
add_u32b(h, 0x00010000); /* matrix_D */
add_u32b(h, 0); /* matrix_V */
add_u32b(h, 0); /* matrix_X */
add_u32b(h, 0); /* matrix_Y */
add_u32b(h, 0x40000000); /* matrix_W */
add_u32b(h, 0); /* Width */
add_u32b(h, 0); /* Height */
}
static void add_trak(m4a_header_t* h) {
m4a_state_t s;
save_atom(h, &s);
add_atom(h, "trak", 0x00);
add_tkhd(h);
add_mdia(h);
load_atom(h, &s);
}
static void add_mvhd(m4a_header_t* h) {
add_atom(h, "mvhd", 0x6c);
add_u32b(h, 0); /* Version (1 byte) + Flags (3 byte) */
add_u32b(h, 0); /* Creation time */
add_u32b(h, 0); /* Modification time */
add_u32b(h, h->mp4->sample_rate); /* Time scale */
add_u32b(h, h->mp4->num_samples); /* Duration */
add_u32b(h, 0x00010000); /* Preferred rate */
add_u16b(h, 0x0100); /* Preferred volume */
add_u32b(h, 0); /* Reserved 1 */
add_u32b(h, 0); /* Reserved 2 */
add_u16b(h, 0); /* Reserved 3 */
add_u32b(h, 0x00010000); /* matrix_A */
add_u32b(h, 0); /* matrix_B */
add_u32b(h, 0); /* matrix_U */
add_u32b(h, 0); /* matrix_C */
add_u32b(h, 0x00010000); /* matrix_D */
add_u32b(h, 0); /* matrix_V */
add_u32b(h, 0); /* matrix_X */
add_u32b(h, 0); /* matrix_Y */
add_u32b(h, 0x40000000); /* matrix_W */
add_u32b(h, 0); /* Preview time */
add_u32b(h, 0); /* Preview duration */
add_u32b(h, 0); /* Poster time */
add_u32b(h, 0); /* Selection time */
add_u32b(h, 0); /* Selection duration */
add_u32b(h, 0); /* Current time */
add_u32b(h, 2); /* Next track ID */
}
static void add_moov(m4a_header_t* h) {
m4a_state_t s;
save_atom(h, &s);
add_atom(h, "moov", 0x00);
add_mvhd(h);
add_trak(h);
//add_udta(h);
load_atom(h, &s);
}
/* *** */
static int make_m4a_header(uint8_t* buf, int buf_len, mp4_custom_t* mp4, STREAMFILE* sf, mp4_type_t type) {
m4a_header_t h = {0};
if (buf_len < 0x400 + mp4->table_entries * 0x4) /* approx */
goto fail;
h.sf = sf;
h.mp4 = mp4;
h.type = type;
h.out = buf;
add_ftyp(&h);
add_free(&h);
add_moov(&h);
add_mdat(&h);
/* define absolute chunk offset after all calcs */
put_u32be(h.chunks.out, h.bytes);
return h.bytes;
fail:
return 0;
}
/* ************************************************************************* */
static void block_callback(STREAMFILE* sf, deblock_io_data* data) {
data->data_size = read_u16be(data->physical_offset, sf);
data->skip_size = 0x02;
data->block_size = data->skip_size + data->data_size;
}
static STREAMFILE* setup_mp4_streamfile(STREAMFILE* sf, mp4_custom_t* mp4, mp4_type_t type) {
STREAMFILE* new_sf = NULL;
deblock_config_t cfg = {0};
cfg.stream_start = mp4->stream_offset;
cfg.stream_size = mp4->stream_size;
cfg.block_callback = block_callback;
switch(type) {
case MP4_LYN: /* each frame has a 0x02 header */
cfg.logical_size = mp4->stream_size - (mp4->table_entries * 0x02);
break;
default:
return NULL;
}
/* setup sf */
new_sf = open_wrap_streamfile(sf);
new_sf = open_io_deblock_streamfile_f(new_sf, &cfg);
//new_sf = open_clamp_streamfile_f(new_sf, 0x00, clean_size);
return new_sf;
}
/* ************************************************************************* */
static ffmpeg_codec_data* init_ffmpeg_mp4_custom(STREAMFILE* sf, mp4_custom_t* mp4, mp4_type_t type) {
ffmpeg_codec_data* ffmpeg_data = NULL;
STREAMFILE* temp_sf = NULL;
int bytes;
uint8_t* buf = NULL;
int buf_len = 0x800 + mp4->table_entries * 0x4; /* approx max sum of atom chunks is ~0x400 */
if (buf_len > 0x100000) /* ??? */
goto fail;
buf = malloc(buf_len);
if (!buf) goto fail;
bytes = make_m4a_header(buf, buf_len, mp4, sf, type); /* before changing stream_offset/size */
switch(type) {
case MP4_STD: /* regular raw data */
temp_sf = sf;
break;
case MP4_LYN: /* frames have size before them, but also a seek table */
temp_sf = setup_mp4_streamfile(sf, mp4, type);
mp4->stream_offset = 0;
mp4->stream_size = get_streamfile_size(temp_sf);
break;
default:
goto fail;
}
if (!temp_sf) goto fail;
ffmpeg_data = init_ffmpeg_header_offset(temp_sf, buf, bytes, mp4->stream_offset, mp4->stream_size);
if (!ffmpeg_data) goto fail;
/* not part of fake header since it's kinda complex to add (iTunes string comment) */
ffmpeg_set_skip_samples(ffmpeg_data, mp4->encoder_delay);
free(buf);
if (sf != temp_sf) close_streamfile(temp_sf);
return ffmpeg_data;
fail:
free(buf);
if (sf != temp_sf) close_streamfile(temp_sf);
free_ffmpeg(ffmpeg_data);
return NULL;
}
ffmpeg_codec_data* init_ffmpeg_mp4_custom_std(STREAMFILE* sf, mp4_custom_t* mp4) {
return init_ffmpeg_mp4_custom(sf, mp4, MP4_STD);
}
ffmpeg_codec_data* init_ffmpeg_mp4_custom_lyn(STREAMFILE* sf, mp4_custom_t* mp4) {
//TODO: most LyN files seem to give FFmpeg error in some frame, mono or stereo files,
// seek table correct and complete, no observed frame size/format/etc oddities.
// No audible issues though so maybe it's must some FFmpeg issue to be fixed there.
// (ex. frame 272 of 1162 in VO_ACT2_M12_FD_54_GILLI_PLS_0008479.Cafe_00000006.son)
return init_ffmpeg_mp4_custom(sf, mp4, MP4_LYN);
}
#endif

View File

@ -209,7 +209,7 @@ STREAMFILE* hca_get_streamfile(hca_codec_data* data) {
/* Test a number of frames if key decrypts correctly.
* Returns score: <0: error/wrong, 0: unknown/silent file, >0: good (the closest to 1 the better). */
static int test_hca_score(hca_codec_data* data, hca_keytest_t* hk, unsigned long long keycode) {
static int test_hca_score(hca_codec_data* data, hca_keytest_t* hk) {
size_t test_frames = 0, current_frame = 0, blank_frames = 0;
int total_score = 0;
const unsigned int block_size = data->info.blockSize;
@ -222,7 +222,7 @@ static int test_hca_score(hca_codec_data* data, hca_keytest_t* hk, unsigned long
* Buffered IO seems fast enough (not very different reading a large block once vs frame by frame).
* clHCA_TestBlock could be optimized a bit more. */
clHCA_SetKey(data->handle, keycode);
hca_set_encryption_key(data, hk->key, hk->subkey);
/* Test up to N non-blank frames or until total frames. */
/* A final score of 0 (=silent) is only possible for short files with all blank frames */
@ -289,35 +289,26 @@ static int test_hca_score(hca_codec_data* data, hca_keytest_t* hk, unsigned long
void test_hca_key(hca_codec_data* data, hca_keytest_t* hk) {
int score;
uint64_t key = hk->key;
uint16_t subkey = hk->subkey;
//;VGM_LOG("HCA: test key=%08x%08x, subkey=%04x\n",
// (uint32_t)((key >> 32) & 0xFFFFFFFF), (uint32_t)(key & 0xFFFFFFFF), subkey);
if (subkey) {
key = key * ( ((uint64_t)subkey << 16u) | ((uint16_t)~subkey + 2u) );
}
score = test_hca_score(data, hk, (unsigned long long)key);
score = test_hca_score(data, hk);
//;VGM_LOG("HCA: test key=%08x%08x, subkey=%04x, score=%i\n",
// (uint32_t)((key >> 32) & 0xFFFFFFFF), (uint32_t)(key & 0xFFFFFFFF), subkey, score);
// (uint32_t)((hk->key >> 32) & 0xFFFFFFFF), (uint32_t)(hk->key & 0xFFFFFFFF), hk->subkey, score);
/* wrong key */
if (score < 0)
return;
//;VGM_LOG("HCA: ok key=%08x%08x, subkey=%04x, score=%i\n",
// (uint32_t)((key >> 32) & 0xFFFFFFFF), (uint32_t)(key & 0xFFFFFFFF), subkey, score);
/* update if something better is found */
if (hk->best_score <= 0 || (score < hk->best_score && score > 0)) {
hk->best_score = score;
hk->best_key = key;
hk->best_key = hk->key; /* base */
}
}
void hca_set_encryption_key(hca_codec_data* data, uint64_t keycode) {
void hca_set_encryption_key(hca_codec_data* data, uint64_t keycode, uint64_t subkey) {
if (subkey) {
keycode = keycode * ( ((uint64_t)subkey << 16u) | ((uint16_t)~subkey + 2u) );
}
clHCA_SetKey(data->handle, (unsigned long long)keycode);
}

View File

@ -62,10 +62,10 @@ mpeg_codec_data* init_mpeg(STREAMFILE* sf, off_t start_offset, coding_t* coding_
} while (rc != MPG123_NEW_FORMAT);
/* check first frame header and validate */
rc = mpg123_getformat(main_m,&sample_rate_per_frame,&channels_per_frame,&encoding);
rc = mpg123_getformat(main_m, &sample_rate_per_frame, &channels_per_frame, &encoding);
if (rc != MPG123_OK) goto fail;
mpg123_info(main_m,&mi);
mpg123_info(main_m, &mi);
if (encoding != MPG123_ENC_SIGNED_16)
goto fail;
@ -89,12 +89,11 @@ mpeg_codec_data* init_mpeg(STREAMFILE* sf, off_t start_offset, coding_t* coding_
samples_per_frame = 1152;
else if (mi.layer == 3)
samples_per_frame = 576;
else goto fail;
else
goto fail;
data->channels_per_frame = channels_per_frame;
data->samples_per_frame = samples_per_frame;
if (channels_per_frame != channels)
goto fail;
/* copy current as open_feed may invalidate until data is fed */
memcpy(&data->mi, &mi, sizeof(struct mpg123_frameinfo));

View File

@ -138,7 +138,7 @@ static const char* extension_list[] = {
"cps",
"csa", //txth/reserved [LEGO Racers 2 (PS2)]
"csmp",
"cvs",
"cvs", //txth/reserved [Aladdin in Nasira's Revenge (PS1)]
"cwav",
"cxs",
@ -401,7 +401,6 @@ static const char* extension_list[] = {
"psf",
"psh", //fake extension for .vsv (to be removed)
"psnd",
"psw", //fake extension for .wam (renamed, to be removed)
"r",
"rac", //txth/reserved [Manhunt (Xbox)]
@ -599,7 +598,7 @@ static const char* extension_list[] = {
"wic", //txth/reserved [Road Rash (SAT)-videos]
"wip", //txth/reserved [Colin McRae DiRT (PC)]
"wlv", //txth/reserved [ToeJam & Earl III: Mission to Earth (DC)]
"wmus",
"wmus", //fake extension (to be removed)
"wp2",
"wpd",
"wsd",
@ -618,7 +617,7 @@ static const char* extension_list[] = {
"xa",
"xa2",
"xa30",
"xag",
"xag", //txth/reserved [Tamsoft's PS2 games]
"xau",
"xav",
"xb", //txth/reserved [Scooby-Doo! Unmasked (Xbox)]
@ -937,7 +936,7 @@ static const meta_info meta_info_list[] = {
{meta_AGSC, "Retro Studios AGSC header"},
{meta_CSMP, "Retro Studios CSMP header"},
{meta_RFRM, "Retro Studios RFRM header"},
{meta_NGC_ADPDTK, "Nintendo ADP raw header"},
{meta_DTK, "Nintendo DTK raw header"},
{meta_RSF, "Retro Studios RSF raw header"},
{meta_AFC, "Nintendo .AFC header"},
{meta_AST, "Nintendo AST header"},
@ -976,7 +975,7 @@ static const meta_info meta_info_list[] = {
{meta_PS2_VAGp_AAAP, "Acclaim Austin AAAp VAG header"},
{meta_SEB, "Game Arts .SEB header"},
{meta_STR_WAV, "Blitz Games .STR+WAV header"},
{meta_PS2_ILD, "ILD header"},
{meta_ILD, "Tose ILD header"},
{meta_PS2_PNB, "assumed PNB (PsychoNauts Bgm File) by .pnb extension"},
{meta_RAW_WAVM, "Xbox .wavm raw header"},
{meta_DSP_STR, "assumed Conan Gamecube STR File by .str extension"},
@ -1021,7 +1020,7 @@ static const meta_info meta_info_list[] = {
{meta_LEG, "Legaia 2 - Duel Saga LEG Header"},
{meta_FILP, "Bio Hazard - Gun Survivor FILp Header"},
{meta_IKM, "MiCROViSiON IKM header"},
{meta_SFS, "String .SFS header"},
{meta_STER, "ALCHEMY STER header"},
{meta_SAT_DVI, "Konami KCEN DVI. header"},
{meta_DC_KCEY, "Konami KCEY KCEYCOMP header"},
{meta_BG00, "Falcom BG00 Header"},
@ -1140,7 +1139,6 @@ static const meta_info meta_info_list[] = {
{meta_PONA_PSX, "Policenauts BGM header"},
{meta_NGC_DSP_AAAP, "Acclaim Austin AAAp DSP header"},
{meta_NGC_DSP_KONAMI, "Konami DSP header"},
{meta_PS2_STER, "STER Header"},
{meta_BNSF, "Namco Bandai BNSF header"},
{meta_PS2_WB, "Shooting Love. ~TRIZEAL~ WB header"},
{meta_S14, "Namco .S14 raw header"},
@ -1383,6 +1381,7 @@ static const meta_info meta_info_list[] = {
{meta_LOPU_FB, "French-Bread LOPU header"},
{meta_LPCM_FB, "French-Bread LPCM header"},
{meta_WBK, "Treyarch WBK header"},
{meta_WBK_NSLB, "Treyarch NSLB header"},
{meta_DSP_APEX, "Koei Tecmo APEX header"},
};

View File

@ -159,7 +159,7 @@ void block_update_ea_schl(off_t block_offset, VGMSTREAM * vgmstream) {
flush_ea_mt(vgmstream);
break;
#ifdef VGM_USE_MPEG
#ifdef VGM_USE_MPEG
/* id, size, samples, offsets, unknown (null for MP2, some size/config for EALayer3; only if not >2ch) */
case coding_MPEG_custom:
case coding_MPEG_layer1:
@ -179,7 +179,7 @@ void block_update_ea_schl(off_t block_offset, VGMSTREAM * vgmstream) {
vgmstream->ch[i].offset = block_offset + 0x0C + (0x04*vgmstream->channels) + channel_start;
}
break;
#endif
#endif
/* id, size, samples, offsets-per-channel, interleaved data (w/ optional hist per channel) */
default:
for (i = 0; i < vgmstream->channels; i++) {

View File

@ -3,51 +3,53 @@
#include "../coding/acm_decoder_libacm.h"
/* ACM - InterPlay infinity engine games [Planescape: Torment (PC), Baldur's Gate (PC)] */
VGMSTREAM * init_vgmstream_acm(STREAMFILE *streamFile) {
VGMSTREAM* init_vgmstream_acm(STREAMFILE* sf) {
VGMSTREAM * vgmstream = NULL;
int loop_flag = 0, channel_count, sample_rate, num_samples;
int loop_flag = 0, channels, sample_rate, num_samples;
int force_channel_number = 0;
acm_codec_data *data = NULL;
/* checks */
/* .acm: plain ACM extension (often but not always paired with .mus, parsed elsewhere)
* .tun: Descent to Undermountain (PC)
* .wavc: header id for WAVC sfx (from bigfiles, extensionless) */
if (!check_extensions(streamFile, "acm,wavc"))
if (!check_extensions(sf, "acm,tun,wavc"))
goto fail;
if (read_32bitBE(0x00,streamFile) != 0x97280301 && /* header id (music) */
read_32bitBE(0x00,streamFile) != 0x57415643) /* "WAVC" (sfx) */
if (read_u32be(0x00,sf) != 0x97280301 && /* header id (music) */
!is_id32be(0x00,sf, "WAVC")) /* sfx */
goto fail;
/* Plain ACM "channels" in the header may be set to 2 for mono voices or 1 for music,
/* Plain ACM "channels" in the header (at 0x08) may be set to 2 for mono voices [FO1] or 1 for music [P:T],
* but actually seem related to ACM rows/cols and have nothing to do with channels.
*
* libacm will set plain ACM (not WAVC) to 2ch unless changes unless changed, but
* only Fallout (PC) seems to use plain ACM for sfx, others are WAVC (which do have channels).
* libacm will set plain ACM (not WAVC) to 2ch unless changed, but only Fallout (PC)
* and Descent to Undermountain (PC) seems to use plain ACM for sfx/voices,
* others are WAVC (which do have channels).
* DtU seems to use the field
*
* Doesn't look like there is any way to detect mono/stereo, so as a quick hack if
* we have a plain ACM (not WAVC) named .wavc we will force 1ch. */
if (check_extensions(streamFile, "wavc")
&& read_32bitBE(0x00,streamFile) == 0x97280301) {
if (check_extensions(sf, "wavc") && read_u32be(0x00,sf) == 0x97280301) {
force_channel_number = 1;
}
/* init decoder */
{
ACMStream *handle;
data = init_acm(streamFile, force_channel_number);
data = init_acm(sf, force_channel_number);
if (!data) goto fail;
handle = data->handle;
channel_count = handle->info.channels;
channels = handle->info.channels;
sample_rate = handle->info.rate;
num_samples = handle->total_values / handle->info.channels;
}
/* 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;

View File

@ -41,6 +41,7 @@ VGMSTREAM * init_vgmstream_bwav(STREAMFILE *streamFile) {
vgmstream->loop_start_sample = read_32bitLE(0x50, streamFile);
vgmstream->loop_end_sample = read_32bitLE(0x4C, streamFile);
vgmstream->meta_type = meta_BWAV;
vgmstream->allow_dual_stereo = 1;
switch(codec) {
case 0x0000:

View File

@ -1479,6 +1479,8 @@ static size_t calculate_eaac_size(STREAMFILE *sf, eaac_header *ea, uint32_t num_
while (block_offset < file_size) {
block_id = read_8bit(block_offset, sf);
block_size = read_32bitBE(block_offset, sf) & 0x00FFFFFF;
if (block_size == 0 || block_size == 0x00FFFFFF)
goto fail;
/* stop when we reach the end marker */
if (ea->version == EAAC_VERSION_V0) {

View File

@ -4,96 +4,106 @@
#include "ea_schl_streamfile.h"
/* header version */
#define EA_VERSION_NONE -1
#define EA_VERSION_V0 0x00 /* ~early PC (when codec1 was used) */
#define EA_VERSION_V1 0x01 /* ~PC */
#define EA_VERSION_V2 0x02 /* ~PS1 */
#define EA_VERSION_V3 0x03 /* ~PS2 */
#define EA_VERSION_NONE -1
#define EA_VERSION_V0 0x00 /* ~early PC (when codec1 was used) */
#define EA_VERSION_V1 0x01 /* ~PC */
#define EA_VERSION_V2 0x02 /* ~PS1 */
#define EA_VERSION_V3 0x03 /* ~PS2 */
/* platform constants (unassigned values seem internal only) */
#define EA_PLATFORM_PC 0x00
#define EA_PLATFORM_PSX 0x01
#define EA_PLATFORM_N64 0x02
#define EA_PLATFORM_MAC 0x03
#define EA_PLATFORM_SAT 0x04
#define EA_PLATFORM_PS2 0x05
#define EA_PLATFORM_GC 0x06 /* also used on Wii */
#define EA_PLATFORM_XBOX 0x07
#define EA_PLATFORM_GENERIC 0x08 /* typically Wii/X360/PS3/videos */
#define EA_PLATFORM_X360 0x09
#define EA_PLATFORM_PSP 0x0A
#define EA_PLATFORM_PS3 0x0E /* very rare [Need for Speed: Carbon (PS3)] */
#define EA_PLATFORM_WII 0x10 /* not seen so far (sx.exe samples ok) */
#define EA_PLATFORM_3DS 0x14
#define EA_PLATFORM_PC 0x00
#define EA_PLATFORM_PSX 0x01
#define EA_PLATFORM_N64 0x02
#define EA_PLATFORM_MAC 0x03
#define EA_PLATFORM_SAT 0x04
#define EA_PLATFORM_PS2 0x05
#define EA_PLATFORM_GC 0x06 /* also used on Wii */
#define EA_PLATFORM_XBOX 0x07
#define EA_PLATFORM_GENERIC 0x08 /* typically Wii/X360/PS3/videos */
#define EA_PLATFORM_X360 0x09
#define EA_PLATFORM_PSP 0x0A
//#define EA_PLATFORM_PC_EAAC 0x0B /* not used (sx.exe internal defs) */
//#define EA_PLATFORM_X360_EAAC 0x0C /* not used (sx.exe internal defs) */
//#define EA_PLATFORM_PSP_EAAC 0x0D /* not used (sx.exe internal defs) */
#define EA_PLATFORM_PS3 0x0E /* very rare [Need for Speed: Carbon (PS3)] */
//#define EA_PLATFORM_PS3_EAAC 0x0F
#define EA_PLATFORM_WII 0x10 /* not seen so far (sx.exe samples ok) */
//#define EA_PLATFORM_WII_EAAC 0x11 /* not used (sx.exe internal defs) */
//#define EA_PLATFORM_PC64_EAAC 0x12 /* not used (sx.exe internal defs) */
//#define EA_PLATFORM_MOBILE_EAAC 0x13 /* not used (sx.exe internal defs) */
#define EA_PLATFORM_3DS 0x14
/* codec constants (undefined are probably reserved, ie.- sx.exe encodes PCM24/DVI but no platform decodes them) */
/* CODEC1 values were used early, then they migrated to CODEC2 values */
#define EA_CODEC1_NONE -1
#define EA_CODEC1_PCM 0x00
//#define EA_CODEC1_IMA 0x02 /* not used (sx.exe internal defs) */
#define EA_CODEC1_N64 0x05
#define EA_CODEC1_VAG 0x06
#define EA_CODEC1_EAXA 0x07
#define EA_CODEC1_MT10 0x09
#define EA_CODEC1_NONE -1
#define EA_CODEC1_PCM 0x00
//#define EA_CODEC1_IMA 0x02 /* not used (sx.exe internal defs) */
#define EA_CODEC1_N64 0x05
#define EA_CODEC1_VAG 0x06
#define EA_CODEC1_EAXA 0x07
#define EA_CODEC1_MT10 0x09
#define EA_CODEC2_NONE -1
#define EA_CODEC2_S16LE_INT 0x00
#define EA_CODEC2_S16BE_INT 0x01
#define EA_CODEC2_S8_INT 0x02
#define EA_CODEC2_EAXA_INT 0x03
#define EA_CODEC2_MT10 0x04
#define EA_CODEC2_VAG 0x05
#define EA_CODEC2_N64 0x06
#define EA_CODEC2_S16BE 0x07
#define EA_CODEC2_S16LE 0x08
#define EA_CODEC2_S8 0x09
#define EA_CODEC2_EAXA 0x0A
//#define EA_CODEC2_U8_INT 0x0B /* not used (sx.exe internal defs) */
//#define EA_CODEC2_CDXA 0x0C /* not used (sx.exe internal defs) */
//#define EA_CODEC2_IMA 0x0D /* not used (sx.exe internal defs) */
//#define EA_CODEC2_LAYER1 0x0E /* not used (sx.exe internal defs) */
#define EA_CODEC2_LAYER2 0x0F
#define EA_CODEC2_LAYER3 0x10 /* not seen so far but may be used somewhere */
#define EA_CODEC2_GCADPCM 0x12
//#define EA_CODEC2_S24LE_INT 0x13 /* not used (sx.exe internal defs) */
#define EA_CODEC2_XBOXADPCM 0x14
//#define EA_CODEC2_S24BE_INT 0x15 /* not used (sx.exe internal defs) */
#define EA_CODEC2_MT5 0x16
#define EA_CODEC2_EALAYER3 0x17
#define EA_CODEC2_ATRAC3 0x1A /* not seen so far (sx.exe samples ok) */
#define EA_CODEC2_ATRAC3PLUS 0x1B
#define EA_CODEC2_NONE -1
#define EA_CODEC2_S16LE_INT 0x00
#define EA_CODEC2_S16BE_INT 0x01
#define EA_CODEC2_S8_INT 0x02
#define EA_CODEC2_EAXA_INT 0x03
#define EA_CODEC2_MT10 0x04
#define EA_CODEC2_VAG 0x05
#define EA_CODEC2_N64 0x06
#define EA_CODEC2_S16BE 0x07
#define EA_CODEC2_S16LE 0x08
#define EA_CODEC2_S8 0x09
#define EA_CODEC2_EAXA 0x0A
//#define EA_CODEC2_U8_INT 0x0B /* not used (sx.exe internal defs) */
//#define EA_CODEC2_CDXA 0x0C /* not used (sx.exe internal defs) */
//#define EA_CODEC2_IMA_INT 0x0D /* not used (sx.exe internal defs) */
//#define EA_CODEC2_LAYER1 0x0E /* not used (sx.exe internal defs) */
#define EA_CODEC2_LAYER2 0x0F
#define EA_CODEC2_LAYER3 0x10 /* not seen so far but may be used somewhere */
#define EA_CODEC2_GCADPCM 0x12
//#define EA_CODEC2_S24LE_INT 0x13 /* not used (sx.exe internal defs) */
#define EA_CODEC2_XBOXADPCM 0x14
//#define EA_CODEC2_S24BE_INT 0x15 /* not used (sx.exe internal defs) */
#define EA_CODEC2_MT5 0x16
#define EA_CODEC2_EALAYER3 0x17
//#define EA_CODEC2_XAS0_INT 0x18 /* not used (sx.exe internal defs) */
//#define EA_CODEC2_EALAYER3_INT 0x19 /* not used (sx.exe internal defs) */
#define EA_CODEC2_ATRAC3 0x1A /* not seen so far (sx.exe samples ok) */
#define EA_CODEC2_ATRAC3PLUS 0x1B
/* EAAC (SND10) codecs begin after this point */
/* Block headers, SCxy - where x is block ID and y is endianness flag (always 'l'?) */
#define EA_BLOCKID_HEADER 0x5343486C /* "SCHl" */
#define EA_BLOCKID_COUNT 0x5343436C /* "SCCl" */
#define EA_BLOCKID_DATA 0x5343446C /* "SCDl" */
#define EA_BLOCKID_LOOP 0x53434C6C /* "SCLl */
#define EA_BLOCKID_END 0x5343456C /* "SCEl" */
#define EA_BLOCKID_HEADER 0x5343486C /* "SCHl" */
#define EA_BLOCKID_COUNT 0x5343436C /* "SCCl" */
#define EA_BLOCKID_DATA 0x5343446C /* "SCDl" */
#define EA_BLOCKID_LOOP 0x53434C6C /* "SCLl */
#define EA_BLOCKID_END 0x5343456C /* "SCEl" */
/* Localized block headers, Sxyy - where x is block ID and yy is lang code (e.g. "SHEN"), used in videos */
#define EA_BLOCKID_LOC_HEADER 0x53480000 /* "SH" */
#define EA_BLOCKID_LOC_COUNT 0x53430000 /* "SC" */
#define EA_BLOCKID_LOC_DATA 0x53440000 /* "SD" */
#define EA_BLOCKID_LOC_END 0x53450000 /* "SE" */
#define EA_BLOCKID_LOC_HEADER 0x53480000 /* "SH" */
#define EA_BLOCKID_LOC_COUNT 0x53430000 /* "SC" */
#define EA_BLOCKID_LOC_DATA 0x53440000 /* "SD" */
#define EA_BLOCKID_LOC_END 0x53450000 /* "SE" */
#define EA_BLOCKID_LOC_EN 0x0000454E /* English */
#define EA_BLOCKID_LOC_FR 0x00004652 /* French */
#define EA_BLOCKID_LOC_GE 0x00004745 /* German, older */
#define EA_BLOCKID_LOC_DE 0x00004445 /* German, newer */
#define EA_BLOCKID_LOC_IT 0x00004954 /* Italian */
#define EA_BLOCKID_LOC_SP 0x00005350 /* Castilian Spanish, older */
#define EA_BLOCKID_LOC_ES 0x00004553 /* Castilian Spanish, newer */
#define EA_BLOCKID_LOC_MX 0x00004D58 /* Mexican Spanish */
#define EA_BLOCKID_LOC_RU 0x00005255 /* Russian */
#define EA_BLOCKID_LOC_JA 0x00004A41 /* Japanese, older */
#define EA_BLOCKID_LOC_JP 0x00004A50 /* Japanese, newer */
#define EA_BLOCKID_LOC_PL 0x0000504C /* Polish */
#define EA_BLOCKID_LOC_BR 0x00004252 /* Brazilian Portuguese */
#define EA_BLOCKID_LOC_EN 0x0000454E /* English */
#define EA_BLOCKID_LOC_FR 0x00004652 /* French */
#define EA_BLOCKID_LOC_GE 0x00004745 /* German, older */
#define EA_BLOCKID_LOC_DE 0x00004445 /* German, newer */
#define EA_BLOCKID_LOC_IT 0x00004954 /* Italian */
#define EA_BLOCKID_LOC_SP 0x00005350 /* Castilian Spanish, older */
#define EA_BLOCKID_LOC_ES 0x00004553 /* Castilian Spanish, newer */
#define EA_BLOCKID_LOC_MX 0x00004D58 /* Mexican Spanish */
#define EA_BLOCKID_LOC_RU 0x00005255 /* Russian */
#define EA_BLOCKID_LOC_JA 0x00004A41 /* Japanese, older */
#define EA_BLOCKID_LOC_JP 0x00004A50 /* Japanese, newer */
#define EA_BLOCKID_LOC_PL 0x0000504C /* Polish */
#define EA_BLOCKID_LOC_BR 0x00004252 /* Brazilian Portuguese */
#define EA_BNK_HEADER_LE 0x424E4B6C /* "BNKl" */
#define EA_BNK_HEADER_BE 0x424E4B62 /* "BNKb" */
#define EA_BNK_HEADER_LE 0x424E4B6C /* "BNKl" */
#define EA_BNK_HEADER_BE 0x424E4B62 /* "BNKb" */
#define EA_MAX_CHANNELS 6
#define EA_MAX_CHANNELS 6
typedef struct {
int32_t num_samples;

View File

@ -49,14 +49,10 @@ VGMSTREAM* init_vgmstream_hca_subkey(STREAMFILE* sf, uint16_t subkey) {
keysize = read_key_file(keybuf, 0x08+0x04, sf);
if (keysize == 0x08) { /* standard */
keycode = get_u64be(keybuf+0x00);
if (subkey) {
keycode = keycode * ( ((uint64_t)subkey << 16u) | ((uint16_t)~subkey + 2u) );
}
}
else if (keysize == 0x08+0x02) { /* seed key + AWB subkey */
uint64_t file_key = get_u64be(keybuf+0x00);
uint16_t file_sub = get_u16be(keybuf+0x08);
keycode = file_key * ( ((uint64_t)file_sub << 16u) | ((uint16_t)~file_sub + 2u) );
keycode = get_u64be(keybuf+0x00);
subkey = get_u16be(keybuf+0x08);
}
#ifdef HCA_BRUTEFORCE
else if (1) {
@ -69,7 +65,7 @@ VGMSTREAM* init_vgmstream_hca_subkey(STREAMFILE* sf, uint16_t subkey) {
find_hca_key(hca_data, &keycode, subkey);
}
hca_set_encryption_key(hca_data, keycode);
hca_set_encryption_key(hca_data, keycode, subkey);
}

View File

@ -418,6 +418,7 @@ static const hcakey_info hcakey_list[] = {
{393410674916959300}, // 0575ACECA945A444
/* D4DJ Groovy Mix (Android) [music_* files, per-song later mixed with subkey] */
{0x59f449354d063308}, //music_0000001
{0xf19d4cb84172f7ab}, //music_0000004
{0x52d065d9ccdb8696}, //music_0110001
{0xba26e58923a5da5d}, //music_0110002
{0x5b877af6e52af19b}, //music_0110003
@ -454,6 +455,7 @@ static const hcakey_info hcakey_list[] = {
{0xb96786621e27daf3}, //music_0120014
{0xa2c543b227b8e5e2}, //music_0120015
{0x845437ec4e367a13}, //music_0120016
{0xadfecfaf25cfe2ce}, //music_0120017
{0x3674aba8da7bc84b}, //music_0120018
{0xfd61f2c3b89f3888}, //music_0120019
{0x4fffee4065d22bec}, //music_0210001
@ -484,6 +486,7 @@ static const hcakey_info hcakey_list[] = {
{0x4aa31e0c4f787a8}, //music_0220014
{0x94466db0d3c10f4b}, //music_0220015
{0xe6d1fd6effa46736}, //music_0220017
{0xd23bdacd616fc4c9}, //music_0220018
{0xfceaa73248868ec5}, //music_0220019
{0x6a15a9610d10d210}, //music_0310001
{0x57111c24801b44a1}, //music_0310002
@ -498,6 +501,7 @@ static const hcakey_info hcakey_list[] = {
{0xc86f8564e0b9078c}, //music_0310011
{0xcc5610c09f472ce9}, //music_0310012
{0xd447a497c5547a1c}, //music_0310013
{0x227b85948bb3d899}, //music_0310014
{0xb921c3992807dadd}, //music_0320001
{0x38ad99a045dc971f}, //music_0320002
{0xf616642579ba5850}, //music_0320003
@ -512,6 +516,7 @@ static const hcakey_info hcakey_list[] = {
{0x244a92885ab77b7c}, //music_0320012
{0xfc3fa77fc33460d4}, //music_0320013
{0x26ee13598091b548}, //music_0320014
{0xf06a6bfdd00c8286}, //music_0320015
{0x2df608ef06aca41c}, //music_0320016
{0x776c4aded0bca5d1}, //music_0410001
{0xb7bff4fbf66be43f}, //music_0410002
@ -583,6 +588,7 @@ static const hcakey_info hcakey_list[] = {
{0xa01c597d1aa13358}, //music_0610010
{0x6492e7708204838}, //music_0610011
{0x957e4d3948427952}, //music_0610012
{0x7081f083ac3d6f0a}, //music_0610013
{0x8258ddd6a1d0849b}, //music_0620001
{0x1dd21a1244ca12f1}, //music_0620002
{0xfdec74b23d8b494b}, //music_0620003
@ -663,7 +669,10 @@ static const hcakey_info hcakey_list[] = {
{0xd0471c163265ca1b}, //music_5030041
{0xd689966609595d7d}, //music_5030042
{0x172171a4ff10fdc1}, //music_5030043
{0x53c2bddb0a15d322}, //music_5030044
{0xcb2c44d594252491}, //music_5030045
{0xbdc220ba31087591}, //music_5030046
{0xe2346e5f5d18228e}, //music_5030047
{0x458b73844ed5219e}, //music_5030048
{0x7d83b8da9023ef26}, //music_5030049
{0x32cb728ddab4d956}, //music_5030050
@ -746,6 +755,7 @@ static const hcakey_info hcakey_list[] = {
{0x138df0b866e902e0}, //music_5050052
{0xc076e8604740ff5f}, //music_5050053
{0x69fe38ae5970d450}, //music_5050054
{0x414200bd8ac11b40}, //music_5050055
{0xbce9e85d31089fb2}, //music_5050056
{0x817b919679c96d7}, //music_5050057
{0x3e0e51043bd7d5e5}, //music_5050058
@ -789,6 +799,11 @@ static const hcakey_info hcakey_list[] = {
{0xba4484d824fb61af}, //music_5050099
{0xb70fe5c5e12c7a1c}, //music_5050100
{0x7f5d26ba72161054}, //music_5050101
{0x79c1f27fa0f8c937}, //music_5050103
{0xe1e4f9125646aa8a}, //music_5050104
{0xd5cf3ce581c59e40}, //music_5050105
{0x5ecb21ac94aa4b8f}, //music_5050107
{0x3786b3940e98628a}, //music_5050108
{0x52c250eade92393b}, //music_9010001
{0xf66e6bb5b0599b07}, //music_9010002
{0xfea0d6adff136868}, //music_9050001
@ -843,6 +858,8 @@ static const hcakey_info hcakey_list[] = {
{0x0a5d0fc8cc5c4502}, // Sng018
{0x198ea1a17416050b}, // Sng019
{0x2aa3b8abad207a1e}, // Sng020
{0x1175edbbacc1fc18}, // Sng024
{0x0e14d06d7f7a6c8c}, // Sng025
{0x33d98a3a9f9bfdef}, // Sng026
{0x2284fd5ca82c78f4}, // Sng027
{0x178a76b6436d20f0}, // Sng028
@ -857,6 +874,9 @@ static const hcakey_info hcakey_list[] = {
// Super Robot Wars 30 (PC)
{6734488621090458}, // 0017ECFB5201069A
// CHUNITHM NEW (AC)
{32931609366120192}, // 0074FF1FCE264700
};
#endif/*_HCA_KEYS_H_*/

View File

@ -3,26 +3,16 @@
typedef struct {
int channels;
int sample_rate;
int loop_flag;
int32_t num_samples;
int32_t loop_start;
int32_t loop_end;
uint32_t file_size;
uint32_t stream_offset;
uint32_t stream_size;
uint32_t table_offset;
uint32_t table_entries;
mp4_custom_t mp4;
int type;
int encoder_delay;
int end_padding;
int frame_samples;
} ktac_header_t;
static int make_m4a_header(uint8_t* buf, int buf_len, ktac_header_t* ktac, STREAMFILE* sf);
/* KTAC - Koei Tecmo custom AAC [Kin'iro no Corda 3 (Vita), Shingeki no Kyojin: Shichi kara no Dasshutsu (3DS), Dynasty Warriors (PS4)] */
VGMSTREAM* init_vgmstream_ktac(STREAMFILE* sf) {
VGMSTREAM* vgmstream = NULL;
@ -40,21 +30,21 @@ VGMSTREAM* init_vgmstream_ktac(STREAMFILE* sf) {
ktac.file_size = read_u32le(0x08,sf);
if (ktac.file_size != get_streamfile_size(sf))
goto fail;
ktac.stream_offset = read_u32le(0x0c,sf);
ktac.stream_size = read_u32le(0x10,sf);
ktac.type = read_u32le(0x14,sf);
ktac.sample_rate = read_u32le(0x18,sf);
ktac.num_samples = read_u32le(0x1c,sf); /* full samples */
ktac.channels = read_u16le(0x20,sf);
ktac.frame_samples = read_u16le(0x22,sf);
ktac.encoder_delay = read_u16le(0x24,sf);
ktac.end_padding = read_u16le(0x26,sf);
ktac.loop_start = read_u32le(0x28,sf);
ktac.loop_end = read_u32le(0x2c,sf);
ktac.mp4.stream_offset = read_u32le(0x0c,sf);
ktac.mp4.stream_size = read_u32le(0x10,sf);
ktac.type = read_u32le(0x14,sf);
ktac.mp4.sample_rate = read_u32le(0x18,sf);
ktac.mp4.num_samples = read_u32le(0x1c,sf); /* full samples */
ktac.mp4.channels = read_u16le(0x20,sf);
ktac.mp4.frame_samples = read_u16le(0x22,sf);
ktac.mp4.encoder_delay = read_u16le(0x24,sf);
ktac.mp4.end_padding = read_u16le(0x26,sf);
ktac.loop_start = read_u32le(0x28,sf);
ktac.loop_end = read_u32le(0x2c,sf);
/* 0x30: ? (big, related to loops) */
/* 0x34: ? (always null) */
ktac.table_offset = read_u32le(0x38,sf);
ktac.table_entries= read_u32le(0x3c,sf);
ktac.mp4.table_offset = read_u32le(0x38,sf);
ktac.mp4.table_entries = read_u32le(0x3c,sf);
ktac.loop_flag = (ktac.loop_end > 0);
@ -65,41 +55,23 @@ VGMSTREAM* init_vgmstream_ktac(STREAMFILE* sf) {
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(ktac.channels, ktac.loop_flag);
vgmstream = allocate_vgmstream(ktac.mp4.channels, ktac.loop_flag);
if (!vgmstream) goto fail;
vgmstream->meta_type = meta_KTAC;
vgmstream->sample_rate = ktac.sample_rate;
vgmstream->num_samples = ktac.num_samples - ktac.encoder_delay - ktac.end_padding;
vgmstream->loop_start_sample = ktac.loop_start * ktac.frame_samples - ktac.encoder_delay;
vgmstream->loop_end_sample = ktac.loop_end * ktac.frame_samples - ktac.encoder_delay;
vgmstream->sample_rate = ktac.mp4.sample_rate;
vgmstream->num_samples = ktac.mp4.num_samples - ktac.mp4.encoder_delay - ktac.mp4.end_padding;
vgmstream->loop_start_sample = ktac.loop_start * ktac.mp4.frame_samples - ktac.mp4.encoder_delay;
vgmstream->loop_end_sample = ktac.loop_end * ktac.mp4.frame_samples - ktac.mp4.encoder_delay;
/* KTAC uses AAC, but not type found in .aac (that has headered frames, like mp3) but raw
* packets + frame size table (similar to .mp4/m4a). We make a fake M4A header to feed FFmpeg */
* packets + frame size table (similar to .mp4/m4a). We set config for FFmpeg's fake M4A header */
#ifdef VGM_USE_FFMPEG
{
ffmpeg_codec_data* ffmpeg_data = NULL;
int bytes;
uint8_t* buf = NULL;
int buf_len = 0x400 + ktac.table_entries * 0x4;
if (buf_len > 0x100000) /* ??? */
goto fail;
buf = malloc(buf_len);
if (!buf) goto fail;
bytes = make_m4a_header(buf, buf_len, &ktac, sf);
ffmpeg_data = init_ffmpeg_header_offset(sf, buf, bytes, ktac.stream_offset, ktac.stream_size);
free(buf);
if (!ffmpeg_data) goto fail;
vgmstream->codec_data = ffmpeg_data;
vgmstream->codec_data = init_ffmpeg_mp4_custom_std(sf, &ktac.mp4);
if (!vgmstream->codec_data) goto fail;
vgmstream->coding_type = coding_FFmpeg;
vgmstream->layout_type = layout_none;
/* not part of fake header since it's kinda complex to add (iTunes string comment) */
ffmpeg_set_skip_samples(ffmpeg_data, ktac.encoder_delay);
}
#else
goto fail;
@ -110,405 +82,3 @@ fail:
close_vgmstream(vgmstream);
return NULL;
}
/* *********************************************************** */
/* Helpers for M4A headers, an insane soup of chunks (AKA "atoms").
* Needs *A LOT* of atoms and fields so this is more elaborate than usual.
* - https://developer.apple.com/library/archive/documentation/QuickTime/QTFF/QTFFPreface/qtffPreface.html
*/
/* generic additions */
typedef struct {
uint8_t* out;
int bytes;
} m4a_state_t;
typedef struct {
STREAMFILE* sf;
ktac_header_t* ktac; /* config */
uint8_t* out; /* current position */
int bytes; /* written bytes */
m4a_state_t chunks; /* chunks offsets are absolute, save position until we know header size */
} m4a_header_t;
static void add_u32b(m4a_header_t* h, uint32_t value) {
put_u32be(h->out, value);
h->out += 0x04;
h->bytes += 0x04;
}
static void add_u24b(m4a_header_t* h, uint32_t value) {
put_u16be(h->out + 0x00, (value >> 8u) & 0xFFFF);
put_u8 (h->out + 0x02, (value >> 0u) & 0xFF);
h->out += 0x03;
h->bytes += 0x03;
}
static void add_u16b(m4a_header_t* h, uint16_t value) {
put_u16be(h->out, value);
h->out += 0x02;
h->bytes += 0x02;
}
static void add_u8(m4a_header_t* h, uint32_t value) {
put_u8(h->out, value);
h->out += 0x01;
h->bytes += 0x01;
}
static void add_name(m4a_header_t* h, const char* name) {
memcpy(h->out, name, 0x4);
h->out += 0x04;
h->bytes += 0x04;
}
static void add_atom(m4a_header_t* h, const char* name, uint32_t size) {
add_u32b(h, size);
add_name(h, name);
}
/* register + write final size for atoms of variable/complex size */
static void save_atom(m4a_header_t* h, m4a_state_t* s) {
s->out = h->out;
s->bytes = h->bytes;
}
static void load_atom(m4a_header_t* h, m4a_state_t* s) {
put_u32be(s->out, h->bytes - s->bytes);
}
/* common atoms */
static void add_ftyp(m4a_header_t* h) {
add_atom(h, "ftyp", 0x18);
add_name(h, "M4A "); /* major brand */
add_u32b(h, 512); /* minor version */
add_name(h, "isom"); /* compatible brands */
add_name(h, "iso2"); /* compatible brands */
}
static void add_free(m4a_header_t* h) {
add_atom(h, "free", 0x08);
}
static void add_mdat(m4a_header_t* h) {
add_atom(h, "mdat", 0x08 + h->ktac->stream_size);
}
/* variable atoms */
static void add_stco(m4a_header_t* h) {
add_atom(h, "stco", 0x10 + 1 * 0x04);
add_u32b(h, 0); /* Version (1 byte) + Flags (3 byte) */
add_u32b(h, 1); /* Number of entries */
/* there may be an entry per frame, but only first seems needed */
save_atom(h, &h->chunks);
add_u32b(h, 0); /* Absolute offset N */
}
static void add_stsz(m4a_header_t* h) {
int i;
add_atom(h, "stsz", 0x14 + h->ktac->table_entries * 0x04);
add_u32b(h, 0); /* Version (1 byte) + Flags (3 byte) */
add_u32b(h, 0); /* Sample size (CBR) */
add_u32b(h, h->ktac->table_entries); /* Number of entries (VBR) */
for (i = 0; i < h->ktac->table_entries; i++) {
uint32_t size = read_u32le(h->ktac->table_offset + i*0x04, h->sf);
add_u32b(h, size); /* Sample N */
}
}
static void add_stsc(m4a_header_t* h) {
add_atom(h, "stsc", 0x1c);
add_u32b(h, 0); /* Version (1 byte) + Flags (3 byte) */
add_u32b(h, 1); /* Number of entries */
add_u32b(h, 1); /* First chunk */
add_u32b(h, h->ktac->table_entries); /* Samples per chunk */
add_u32b(h, 1); /* Sample description ID */
}
static void add_stts(m4a_header_t* h) {
add_atom(h, "stts", 0x18);
add_u32b(h, 0); /* Version (1 byte) + Flags (3 byte) */
add_u32b(h, 1); /* Number of entries */
add_u32b(h, h->ktac->table_entries); /* Sample count */
add_u32b(h, h->ktac->frame_samples); /* Sample duration */
}
/* from mpeg4audio.c (also see ff_mp4_read_dec_config_descr) */
static const int m4a_sample_rates[16] = {
96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350
};
static const uint8_t m4a_channels[14] = {
0,
1, // mono (1/0)
2, // stereo (2/0)
3, // 3/0
4, // 3/1
5, // 3/2
6, // 3/2.1
8, // 5/2.1
//0,
//0,
//0,
//7, // 3/3.1
//8, // 3/2/2.1
//24 // 3/3/3 - 5/2/3 - 3/0/0.2
};
static void add_esds(m4a_header_t* h) {
uint16_t config = 0;
/* ES_descriptor (TLV format see ISO 14496-1) and DecSpecificInfoTag define actual decoding
- config (channels/rate/etc), other atoms with the same stuff is just info
* - http://ecee.colorado.edu/~ecen5653/ecen5653/papers/ISO%2014496-1%202004.PDF */
{
uint8_t object_type = 0x02; /* 0x00=none, 0x01=AAC main, 0x02=AAC LC */
uint8_t sr_index = 0;
uint8_t ch_index = 0;
uint8_t unknown = 0;
int i;
for (i = 0; i < 16; i++) {
if (m4a_sample_rates[i] == h->ktac->sample_rate) {
sr_index = i;
break;
}
}
for (i = 0; i < 8; i++) {
if (m4a_channels[i] == h->ktac->channels) {
ch_index = i;
break;
}
}
config |= (object_type & 0x1F) << 11; /* 5b */
config |= (sr_index & 0x0F) << 7; /* 4b */
config |= (ch_index & 0x0F) << 3; /* 4b */
config |= (unknown & 0x07) << 0; /* 3b */
}
add_atom(h, "esds", 0x33);
add_u32b(h, 0); /* Version (1 byte) + Flags (3 byte) */
add_u8 (h, 0x03); /* ES_DescrTag */
add_u32b(h, 0x80808022); /* size 0x22 */
add_u16b(h, 0x0000); /* stream Id */
add_u8 (h, 0x00); /* flags */
add_u8 (h, 0x04); /* DecoderConfigDescrTag */
add_u32b(h, 0x80808014); /* size 0x14 */
add_u8 (h, 0x40); /* object type (0x40=audio) */
add_u8 (h, 0x15); /* stream type (6b: 0x5=audio) + upstream (1b) + reserved (1b: const 1) */
add_u24b(h, 0x000000); /* buffer size */
add_u32b(h, 0); /* max bitrate (256000?)*/
add_u32b(h, 0); /* average bitrate (256000?) */
add_u8 (h, 0x05); /* DecSpecificInfoTag */
add_u32b(h, 0x80808002); /* size 0x02 */
add_u16b(h, config); /* actual decoder info */
add_u8 (h, 0x06); /* SLConfigDescrTag */
add_u32b(h, 0x80808001); /* size 0x01 */
add_u8 (h, 0x02); /* predefined (2=default) */
}
static void add_mp4a(m4a_header_t* h) {
add_atom(h, "mp4a", 0x57);
add_u32b(h, 0); /* ? */
add_u32b(h, 1); /* Data reference index */
add_u32b(h, 0); /* Reserved */
add_u32b(h, 0); /* Reserved 2 */
add_u16b(h, h->ktac->channels); /* Channel count */
add_u16b(h, 16); /* Sample size */
add_u32b(h, 0); /* Pre-defined */
add_u16b(h, h->ktac->sample_rate); /* Sample rate */
add_u16b(h, 0); /* ? */
add_esds(h); /* elementary stream descriptor */
}
static void add_stsd(m4a_header_t* h) {
add_atom(h, "stsd", 0x67);
add_u32b(h, 0); /* Version (1 byte) + Flags (3 byte) */
add_u32b(h, 1); /* Number of entries */
add_mp4a(h);
}
static void add_stbl(m4a_header_t* h) {
m4a_state_t s;
save_atom(h, &s);
add_atom(h, "stbl", 0x00);
add_stsd(h); /* Sample description */
add_stts(h); /* Time-to-sample */
add_stsc(h); /* Sample-to-chunk */
add_stsz(h); /* Sample size */
add_stco(h); /* Chunk offset */
load_atom(h, &s);
}
static void add_dinf(m4a_header_t* h) {
add_atom(h, "dinf", 0x24);
add_atom(h, "dref", 0x1c);
add_u32b(h, 0); /* Version (1 byte) + Flags (3 byte) */
add_u32b(h, 1); /* Number of entries */
add_atom(h, "url ", 0x0c);
add_u32b(h, 1); /* Version (1 byte) + Flags (3 byte) */
}
static void add_smhd(m4a_header_t* h) {
add_atom(h, "smhd", 0x10);
add_u32b(h, 0); /* Version (1 byte) + Flags (3 byte) */
add_u16b(h, 0); /* Balance */
add_u16b(h, 0); /* Reserved */
}
static void add_minf(m4a_header_t* h) {
m4a_state_t s;
save_atom(h, &s);
add_atom(h, "minf", 0x00);
add_smhd(h);
add_dinf(h);
add_stbl(h);
load_atom(h, &s);
}
static void add_hdlr(m4a_header_t* h) {
add_atom(h, "hdlr", 0x22);
add_u32b(h, 0); /* version (1 byte) + flags (3 byte) */
add_u32b(h, 0); /* Component type */
add_name(h, "soun"); /* Component subtype */
add_u32b(h, 0); /* Component manufacturer */
add_u32b(h, 0); /* Component flags */
add_u32b(h, 0); /* Component flags mask */
add_u16b(h, 0); /* Component name */
}
static void add_mdhd(m4a_header_t* h) {
add_atom(h, "mdhd", 0x20);
add_u32b(h, 0); /* Version (1 byte) + Flags (3 byte) */
add_u32b(h, 0); /* Creation time */
add_u32b(h, 0); /* Modification time */
add_u32b(h, h->ktac->sample_rate); /* Time scale */
add_u32b(h, h->ktac->num_samples); /* Duration */
add_u16b(h, 0); /* Language (0xC455=eng?) */
add_u16b(h, 0); /* Quality */
}
static void add_mdia(m4a_header_t* h) {
m4a_state_t s;
save_atom(h, &s);
add_atom(h, "mdia", 0x00);
add_mdhd(h);
add_hdlr(h);
add_minf(h);
load_atom(h, &s);
}
static void add_tkhd(m4a_header_t* h) {
add_atom(h, "tkhd", 0x5C);
add_u32b(h, 0x00000001); /* Version (1 byte) + Flags (3 byte), 1=track enabled */
add_u32b(h, 0); /* Creation time */
add_u32b(h, 0); /* Modification time */
add_u32b(h, 1); /* Track ID */
add_u32b(h, 0); /* Reserved 1 */
add_u32b(h, h->ktac->num_samples); /* Duration */
add_u32b(h, 0); /* Reserved 1 */
add_u32b(h, 0); /* Reserved 2 */
add_u16b(h, 0); /* Layer */
add_u16b(h, 0); /* Alternate group (1?) */
add_u16b(h, 0x0100); /* Volume */
add_u16b(h, 0); /* Reserved */
add_u32b(h, 0x00010000); /* matrix_A */
add_u32b(h, 0); /* matrix_B */
add_u32b(h, 0); /* matrix_U */
add_u32b(h, 0); /* matrix_C */
add_u32b(h, 0x00010000); /* matrix_D */
add_u32b(h, 0); /* matrix_V */
add_u32b(h, 0); /* matrix_X */
add_u32b(h, 0); /* matrix_Y */
add_u32b(h, 0x40000000); /* matrix_W */
add_u32b(h, 0); /* Width */
add_u32b(h, 0); /* Height */
}
static void add_trak(m4a_header_t* h) {
m4a_state_t s;
save_atom(h, &s);
add_atom(h, "trak", 0x00);
add_tkhd(h);
add_mdia(h);
load_atom(h, &s);
}
static void add_mvhd(m4a_header_t* h) {
add_atom(h, "mvhd", 0x6c);
add_u32b(h, 0); /* Version (1 byte) + Flags (3 byte) */
add_u32b(h, 0); /* Creation time */
add_u32b(h, 0); /* Modification time */
add_u32b(h, h->ktac->sample_rate); /* Time scale */
add_u32b(h, h->ktac->num_samples); /* Duration */
add_u32b(h, 0x00010000); /* Preferred rate */
add_u16b(h, 0x0100); /* Preferred volume */
add_u32b(h, 0); /* Reserved 1 */
add_u32b(h, 0); /* Reserved 2 */
add_u16b(h, 0); /* Reserved 3 */
add_u32b(h, 0x00010000); /* matrix_A */
add_u32b(h, 0); /* matrix_B */
add_u32b(h, 0); /* matrix_U */
add_u32b(h, 0); /* matrix_C */
add_u32b(h, 0x00010000); /* matrix_D */
add_u32b(h, 0); /* matrix_V */
add_u32b(h, 0); /* matrix_X */
add_u32b(h, 0); /* matrix_Y */
add_u32b(h, 0x40000000); /* matrix_W */
add_u32b(h, 0); /* Preview time */
add_u32b(h, 0); /* Preview duration */
add_u32b(h, 0); /* Poster time */
add_u32b(h, 0); /* Selection time */
add_u32b(h, 0); /* Selection duration */
add_u32b(h, 0); /* Current time */
add_u32b(h, 2); /* Next track ID */
}
static void add_moov(m4a_header_t* h) {
m4a_state_t s;
save_atom(h, &s);
add_atom(h, "moov", 0x00);
add_mvhd(h);
add_trak(h);
//add_udta(h);
load_atom(h, &s);
}
/* *** */
static int make_m4a_header(uint8_t* buf, int buf_len, ktac_header_t* ktac, STREAMFILE* sf) {
m4a_header_t h = {0};
if (buf_len < 0x300 + ktac->table_entries * 0x4) /* approx */
goto fail;
h.sf = sf;
h.ktac = ktac;
h.out = buf;
add_ftyp(&h);
add_free(&h);
add_moov(&h);
add_mdat(&h);
/* define absolute chunk offset after all calcs */
put_u32be(h.chunks.out, h.bytes);
return h.bytes;
fail:
return 0;
}

View File

@ -28,7 +28,7 @@ VGMSTREAM * init_vgmstream_halpst(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_nds_strm(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_ngc_adpdtk(STREAMFILE *streamFile);
VGMSTREAM* init_vgmstream_dtk(STREAMFILE* sf);
VGMSTREAM* init_vgmstream_ngc_dsp_std(STREAMFILE* sf);
VGMSTREAM* init_vgmstream_ngc_mdsp_std(STREAMFILE* sf);
@ -77,7 +77,7 @@ VGMSTREAM * init_vgmstream_nps(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_rs03(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_rsf(STREAMFILE *streamFile);
VGMSTREAM* init_vgmstream_rsf(STREAMFILE* sf);
VGMSTREAM * init_vgmstream_rwsd(STREAMFILE *streamFile);
@ -94,9 +94,9 @@ VGMSTREAM * init_vgmstream_svag_kcet(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_ps_headerless(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_mib_mih(STREAMFILE *streamFile);
VGMSTREAM* init_vgmstream_mib_mih(STREAMFILE* sf);
VGMSTREAM * init_vgmstream_ps2_mic(STREAMFILE *streamFile);
VGMSTREAM* init_vgmstream_mic_koei(STREAMFILE* sf);
VGMSTREAM * init_vgmstream_raw_pcm(STREAMFILE *streamFile);
@ -105,7 +105,7 @@ VGMSTREAM * init_vgmstream_vag_aaap(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_seb(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_ps2_ild(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_ild(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_ps2_pnb(STREAMFILE *streamFile);
@ -225,7 +225,7 @@ VGMSTREAM * init_vgmstream_filp(STREAMFILE * streamFile);
VGMSTREAM* init_vgmstream_ikm(STREAMFILE* sf);
VGMSTREAM * init_vgmstream_sfs(STREAMFILE * streamFile);
VGMSTREAM* init_vgmstream_ster(STREAMFILE* sf);
VGMSTREAM * init_vgmstream_sat_dvi(STREAMFILE * streamFile);
@ -451,8 +451,6 @@ VGMSTREAM * init_vgmstream_dmsg(STREAMFILE* streamFile);
VGMSTREAM * init_vgmstream_ngc_dsp_konami(STREAMFILE* streamFile);
VGMSTREAM * init_vgmstream_ps2_ster(STREAMFILE* streamFile);
VGMSTREAM * init_vgmstream_bnsf(STREAMFILE* streamFile);
VGMSTREAM * init_vgmstream_ps2_wb(STREAMFILE* streamFile);
@ -971,5 +969,6 @@ VGMSTREAM* init_vgmstream_lopu_fb(STREAMFILE* sf);
VGMSTREAM* init_vgmstream_lpcm_fb(STREAMFILE* sf);
VGMSTREAM* init_vgmstream_wbk(STREAMFILE* sf);
VGMSTREAM* init_vgmstream_wbk_nslb(STREAMFILE* sf);
#endif /*_META_H*/

View File

@ -14,8 +14,9 @@ VGMSTREAM* init_vgmstream_msf(STREAMFILE* sf) {
/* .msf: standard
* .msa: Sonic & Sega All-Stars Racing (PS3)
* .at3: Silent Hill HD Collection (PS3), Z/X Zekkai no Crusade (PS3)
* .mp3: Darkstalkers Resurrection (PS3) */
if (!check_extensions(sf,"msf,msa,at3,mp3"))
* .mp3: Darkstalkers Resurrection (PS3)
* .str: Pac-Man and the Ghostly Adventures (PS3) */
if (!check_extensions(sf,"msf,msa,at3,mp3,str"))
goto fail;
/* check header "MSF" + version-char, usually:

View File

@ -3,7 +3,7 @@
#include "../util.h"
/* DTK - headerless Nintendo GC DTK file [Harvest Moon: Another Wonderful Life (GC), XGRA (GC)] */
VGMSTREAM* init_vgmstream_ngc_adpdtk(STREAMFILE* sf) {
VGMSTREAM* init_vgmstream_dtk(STREAMFILE* sf) {
VGMSTREAM* vgmstream = NULL;
off_t start_offset;
int channels, loop_flag;
@ -22,12 +22,12 @@ VGMSTREAM* init_vgmstream_ngc_adpdtk(STREAMFILE* sf) {
for (i = 0; i < 10; i++) { /* try a bunch of frames */
/* header 0x00/01 are repeated in 0x02/03 (for error correction?),
* could also test header values (upper nibble should be 0..3, and lower nibble 0..C) */
if (read_8bit(0x00 + i*0x20,sf) != read_8bit(0x02 + i*0x20,sf) ||
read_8bit(0x01 + i*0x20,sf) != read_8bit(0x03 + i*0x20,sf))
if (read_u8(0x00 + i*0x20,sf) != read_u8(0x02 + i*0x20,sf) ||
read_u8(0x01 + i*0x20,sf) != read_u8(0x03 + i*0x20,sf))
goto fail;
/* frame headers for silent frames are 0x0C, never null */
if (read_8bit(0x00 + i*0x20,sf) == 0x00)
if (read_u8(0x00 + i*0x20,sf) == 0x00)
goto fail;
}
}
@ -48,13 +48,11 @@ VGMSTREAM* init_vgmstream_ngc_adpdtk(STREAMFILE* sf) {
vgmstream->sample_rate = 48000; /* due to a GC hardware defect this may be closer to 48043 */
vgmstream->coding_type = coding_NGC_DTK;
vgmstream->layout_type = layout_none;
vgmstream->meta_type = meta_NGC_ADPDTK;
vgmstream->meta_type = meta_DTK;
if ( !vgmstream_open_stream(vgmstream, sf, start_offset) )
if (!vgmstream_open_stream(vgmstream, sf, start_offset))
goto fail;
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;

View File

@ -1,69 +1,58 @@
#include "meta.h"
#include "../util.h"
#include "../coding/coding.h"
/* ILD */
VGMSTREAM * init_vgmstream_ps2_ild(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
char filename[PATH_LIMIT];
int loop_flag=0;
int channel_count;
/* ILD - from Tose(?) games [Battle of Sunrise (PS2), Nightmare Before Christmas: Oogie's Revenge (PS2)] */
VGMSTREAM* init_vgmstream_ild(STREAMFILE* sf) {
VGMSTREAM* vgmstream = NULL;
int channels, loop_flag;
uint32_t data_size;
off_t start_offset;
int i;
/* check extension, case insensitive */
streamFile->get_name(streamFile,filename,sizeof(filename));
if (strcasecmp("ild",filename_extension(filename))) goto fail;
/* check ILD Header */
if (read_32bitBE(0x00,streamFile) != 0x494C4400)
if (!is_id32be(0x00,sf, "ILD\0"))
goto fail;
/* check loop */
loop_flag = (read_32bitLE(0x2C,streamFile)!=0);
channel_count=read_32bitLE(0x04,streamFile);
if (!check_extensions(sf, "ild"))
goto fail;
channels = read_u32le(0x04,sf); /* tracks (seen 2 and 4) */
start_offset = read_u32le(0x08,sf);
data_size = read_u32le(0x0C,sf);
/* 0x10: headers size / 2? */
/* 0x14: header per channel */
/* - 0x00: null */
/* - 0x04: header size? (always 0x20) */
/* - 0x08: size (may vary a bit between channel pairs) */
/* - 0x0c: interleave? */
/* - 0x10: channels per track (1) */
/* - 0x14: sample rate */
/* - 0x18: loop start */
/* - 0x1c: loop end (also varies) */
loop_flag = (read_s32le(0x2C,sf) > 0);
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
vgmstream = allocate_vgmstream(channels, loop_flag);
if (!vgmstream) goto fail;
/* fill in the vital statistics */
vgmstream->channels = read_32bitLE(0x04,streamFile);
vgmstream->sample_rate = read_32bitLE(0x28,streamFile);
vgmstream->meta_type = meta_ILD;
vgmstream->num_samples = ps_bytes_to_samples(data_size, channels);
vgmstream->interleave_block_size = read_u32le(0x14 + 0x0c,sf);
vgmstream->sample_rate = read_u32le(0x14 + 0x14,sf);
vgmstream->loop_start_sample = ps_bytes_to_samples(read_u32le(0x14 + 0x18,sf), 1);
vgmstream->loop_end_sample = ps_bytes_to_samples(read_u32le(0x14 + 0x1c,sf), 1);
/* Check for Compression Scheme */
vgmstream->coding_type = coding_PSX;
vgmstream->num_samples = read_32bitLE(0x0C,streamFile)/16*28/vgmstream->channels;
/* Get loop point values */
if(vgmstream->loop_flag) {
vgmstream->loop_start_sample = read_32bitLE(0x2C,streamFile)/16*28;
vgmstream->loop_end_sample = read_32bitLE(0x30,streamFile)/16*28;
}
vgmstream->interleave_block_size = read_32bitLE(0x18,streamFile)/2;
vgmstream->layout_type = layout_interleave;
vgmstream->meta_type = meta_PS2_ILD;
start_offset = (off_t)read_32bitLE(0x08,streamFile);
/* open the file for reading by each channel */
{
for (i=0;i<channel_count;i++) {
vgmstream->ch[i].streamfile = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE);
if (!vgmstream->ch[i].streamfile) goto fail;
vgmstream->ch[i].channel_start_offset=
vgmstream->ch[i].offset=
(off_t)(start_offset+vgmstream->interleave_block_size*i);
}
}
if (!vgmstream_open_stream(vgmstream, sf, start_offset))
goto fail;
return vgmstream;
/* clean up anything we may have opened */
fail:
if (vgmstream) close_vgmstream(vgmstream);
close_vgmstream(vgmstream);
return NULL;
}

View File

@ -2,29 +2,37 @@
#include "../coding/coding.h"
/* .MIC - from KOEI games [Crimson Sea 2 (PS2), Dynasty Tactics 2 (PS2)] */
VGMSTREAM * init_vgmstream_ps2_mic(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
VGMSTREAM* init_vgmstream_mic_koei(STREAMFILE* sf) {
VGMSTREAM* vgmstream = NULL;
off_t start_offset;
int loop_flag, channel_count, loop_start, loop_end, sample_rate;
int loop_flag, channels, loop_start, loop_end, sample_rate;
size_t interleave, block_size;
/* checks */
if (!check_extensions(streamFile, "mic"))
if (!check_extensions(sf, "mic"))
goto fail;
start_offset = read_32bitLE(0x00,streamFile);
/* simple header so throws some extra in some checks */
start_offset = read_u32le(0x00,sf);
if (start_offset != 0x800) goto fail;
sample_rate = read_32bitLE(0x04,streamFile);
channel_count = read_32bitLE(0x08,streamFile);
interleave = read_32bitLE(0x0c,streamFile);
loop_end = read_32bitLE(0x10,streamFile);
loop_start = read_32bitLE(0x14,streamFile);
sample_rate = read_u32le(0x04,sf);
channels = read_u32le(0x08,sf);
if (channels > 2) goto fail;
interleave = read_u32le(0x0c,sf);
if (interleave != 0x10) goto fail;
loop_end = read_32bitLE(0x10,sf);
loop_start = read_32bitLE(0x14,sf);
if (read_u32le(0x18,sf) != 0) goto fail;
if (read_u32le(0x1c,sf) != 0) goto fail;
loop_flag = (loop_start != 1);
block_size = interleave * channel_count;
block_size = interleave * channels;
/* 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_MIC;
@ -34,11 +42,11 @@ VGMSTREAM * init_vgmstream_ps2_mic(STREAMFILE *streamFile) {
vgmstream->interleave_block_size = interleave;
vgmstream->layout_type = layout_interleave;
vgmstream->num_samples = ps_bytes_to_samples(loop_end * block_size, channel_count);
vgmstream->loop_start_sample = ps_bytes_to_samples(loop_start * block_size, channel_count);
vgmstream->num_samples = ps_bytes_to_samples(loop_end * block_size, channels);
vgmstream->loop_start_sample = ps_bytes_to_samples(loop_start * block_size, channels);
vgmstream->loop_end_sample = vgmstream->num_samples;
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
if (!vgmstream_open_stream(vgmstream, sf, start_offset))
goto fail;
return vgmstream;

View File

@ -1,66 +0,0 @@
#include "meta.h"
#include "../util.h"
/* STER (from Juuni Kokuki: Kakukaku Taru Ou Michi Beni Midori no Uka) */
VGMSTREAM * init_vgmstream_ps2_ster(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
char filename[PATH_LIMIT];
off_t start_offset;
int loop_flag;
int channel_count;
/* check extension, case insensitive */
streamFile->get_name(streamFile,filename,sizeof(filename));
if (strcasecmp("ster",filename_extension(filename))) goto fail;
/* check header */
if (read_32bitBE(0x00,streamFile) != 0x53544552) /* "STER" */
goto fail;
loop_flag = (read_16bitLE(0xB,streamFile)==0);
channel_count = 2;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail;
/* fill in the vital statistics */
start_offset = 0x30;
vgmstream->channels = channel_count;
vgmstream->sample_rate = read_32bitBE(0x10,streamFile);
vgmstream->coding_type = coding_PSX;
vgmstream->num_samples = read_32bitLE(0x4,streamFile)*56/32;
if (loop_flag) {
vgmstream->loop_start_sample = read_32bitLE(0x8,streamFile)*28/32;
vgmstream->loop_end_sample = vgmstream->num_samples;
}
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = 0x10;
vgmstream->meta_type = meta_PS2_STER;
/* open the file for reading */
{
int i;
STREAMFILE * file;
file = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE);
if (!file) goto fail;
for (i=0;i<channel_count;i++) {
vgmstream->ch[i].streamfile = file;
vgmstream->ch[i].channel_start_offset=
vgmstream->ch[i].offset=start_offset+
vgmstream->interleave_block_size*i;
}
}
return vgmstream;
/* clean up anything we may have opened */
fail:
if (vgmstream) close_vgmstream(vgmstream);
return NULL;
}

View File

@ -2,9 +2,9 @@
#include "../coding/coding.h"
/* headerless PS-ADPCM - from Katamary Damacy (PS2), Air (PS2), Aladdin: Nasira's Revenge (PS1)
* (guesses interleave and channels by testing data and using the file extension, and finds
* loops in PS-ADPCM flags; this is a crutch for convenience, consider using GENH/TXTH instead). */
/* Headerless PS-ADPCM
* Guesses interleave/channels/loops by testing data and using the file extension for sample rate.
* This is an ugly crutch for older sets, use TXTH to properly play headerless data instead. */
VGMSTREAM * init_vgmstream_ps_headerless(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
off_t start_offset = 0x00;
@ -34,18 +34,12 @@ VGMSTREAM * init_vgmstream_ps_headerless(STREAMFILE *streamFile) {
/* checks
* .mib: common, but many ext-less files are renamed to this.
* .mi4: fake .mib to force another sample rate
* .cvs: Aladdin - Nasira's Revenge (PS1)
* .snds: The Incredibles (PS2)
* .vb: Tantei Jinguuji Saburo - Mikan no Rupo (PS1)
* .xag: Hagane no Renkinjutsushi - Dream Carnival (PS2)
* */
streamFile->get_name(streamFile,filename,sizeof(filename));
if (strcasecmp("cvs",filename_extension(filename)) &&
strcasecmp("mib",filename_extension(filename)) &&
if (strcasecmp("mib",filename_extension(filename)) &&
strcasecmp("mi4",filename_extension(filename)) &&
strcasecmp("snds",filename_extension(filename))&&
strcasecmp("vb",filename_extension(filename)) &&
strcasecmp("xag",filename_extension(filename)))
strcasecmp("vb",filename_extension(filename)))
goto fail;
/* test if raw PS-ADPCM */
@ -136,9 +130,6 @@ VGMSTREAM * init_vgmstream_ps_headerless(STREAMFILE *streamFile) {
if(!strcasecmp("vb",filename_extension(filename)))
loopStart=0;
if(!strcasecmp("xag",filename_extension(filename)))
channel_count=2;
// Calc Loop Points & Interleave ...
if(loopStartPointsCount>=2) {
// can't get more then 0x10 loop point !
@ -200,8 +191,7 @@ VGMSTREAM * init_vgmstream_ps_headerless(STREAMFILE *streamFile) {
channel_count=newChannelCount;
}
if (!strcasecmp("cvs", filename_extension(filename)) ||
!strcasecmp("vb",filename_extension(filename)))
if (!strcasecmp("vb",filename_extension(filename)))
channel_count=1;
@ -220,14 +210,7 @@ VGMSTREAM * init_vgmstream_ps_headerless(STREAMFILE *streamFile) {
if(!strcasecmp("mi4",filename_extension(filename)))
vgmstream->sample_rate = 48000;
if(!strcasecmp("snds", filename_extension(filename)))
vgmstream->sample_rate = 48000;
if(!strcasecmp("xag",filename_extension(filename)))
vgmstream->sample_rate = 44100;
if (!strcasecmp("cvs", filename_extension(filename)) ||
!strcasecmp("vb",filename_extension(filename)))
if (!strcasecmp("vb",filename_extension(filename)))
vgmstream->sample_rate = 22050;
vgmstream->num_samples = (int32_t)(fileLength/16/channel_count*28);

View File

@ -155,7 +155,7 @@ VGMSTREAM* init_vgmstream_psb(STREAMFILE* sf) {
default: goto fail;
}
if (psb.duration_test && psb.loop_start + psb.loop_end < vgmstream->num_samples)
if (psb.duration_test && psb.loop_start + psb.loop_end <= vgmstream->num_samples)
vgmstream->loop_end_sample += psb.loop_start;
break;

View File

@ -518,10 +518,10 @@ VGMSTREAM* init_vgmstream_riff(STREAMFILE* sf) {
break;
case 0x66616374: /* "fact" */
if (chunk_size == 0x04) { /* standard (usually for ADPCM, MS recommends to set for non-PCM codecs) */
if (chunk_size == 0x04) { /* standard (usually for ADPCM, MS recommends setting for non-PCM codecs but optional) */
fact_sample_count = read_32bitLE(current_chunk+0x08, sf);
}
else if (chunk_size == 0x10 && read_32bitBE(current_chunk+0x08+0x04, sf) == 0x4C794E20) { /* "LyN " */
else if (chunk_size == 0x10 && is_id32be(current_chunk+0x08+0x04, sf, "LyN ")) {
goto fail; /* parsed elsewhere */
}
else if ((fmt.is_at3 || fmt.is_at3p) && chunk_size == 0x08) { /* early AT3 (mainly PSP games) */
@ -540,6 +540,9 @@ VGMSTREAM* init_vgmstream_riff(STREAMFILE* sf) {
}
break;
case 0x4C795345: /* "LySE" */
goto fail; /* parsed elsewhere */
case 0x70666c74: /* "pflt" (.mwv extension) */
if (!mwv) break; /* ignore if not in an mwv */
mwv_pflt_offset = current_chunk; /* predictor filters */

View File

@ -4,42 +4,47 @@
/* .rsf - from Metroid Prime */
VGMSTREAM * init_vgmstream_rsf(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
char filename[PATH_LIMIT];
VGMSTREAM* init_vgmstream_rsf(STREAMFILE* sf) {
VGMSTREAM* vgmstream = NULL;
int channels, loop_flag;
uint32_t interleave, file_size;
size_t file_size;
/* checks */
if (!check_extensions(sf,"rsf"))
goto fail;
/* check extension, case insensitive */
/* this is all we have to go on, rsf is completely headerless */
streamFile->get_name(streamFile,filename,sizeof(filename));
if (strcasecmp("rsf",filename_extension(filename))) goto fail;
file_size = get_streamfile_size(streamFile);
file_size = get_streamfile_size(sf);
interleave = (file_size + 1) / 2;
/* G.721 has no zero nibbles, so we look at the first few bytes
* (known files start with 0xFFFFFFFF, but probably an oddity of the codec) */
{
/* extra check: G.721 has no zero nibbles, so we look at
* the first few bytes*/
int8_t test_byte;
uint8_t test_byte;
off_t i;
/* 0x20 is arbitrary, all files are much larger */
for (i=0;i<0x20;i++) {
test_byte = read_8bit(i,streamFile);
for (i = 0; i < 0x20; i++) {
test_byte = read_u8(i,sf);
if (!(test_byte&0xf) || !(test_byte&0xf0)) goto fail;
}
/* and also check start of second channel */
for (i=(file_size+1)/2;i<(file_size+1)/2+0x20;i++) {
test_byte = read_8bit(i,streamFile);
for (i = interleave; i < interleave + 0x20; i++) {
test_byte = read_u8(i,sf);
if (!(test_byte&0xf) || !(test_byte&0xf0)) goto fail;
}
}
/* build the VGMSTREAM */
channels = 2;
loop_flag = 0;
vgmstream = allocate_vgmstream(2,0);
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channels, loop_flag);
if (!vgmstream) goto fail;
/* fill in the vital statistics */
vgmstream->num_samples = file_size;
vgmstream->sample_rate = 32000;
@ -47,27 +52,20 @@ VGMSTREAM * init_vgmstream_rsf(STREAMFILE *streamFile) {
vgmstream->layout_type = layout_none;
vgmstream->meta_type = meta_RSF;
if (!vgmstream_open_stream(vgmstream, sf, 0))
goto fail;
/* open the file for reading by each channel */
{
int i;
for (i=0;i<2;i++) {
vgmstream->ch[i].streamfile = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE);
if (!vgmstream->ch[i].streamfile) goto fail;
vgmstream->ch[i].channel_start_offset=
vgmstream->ch[i].offset=
(file_size+1)/2*i;
for (i = 0; i < channels; i++) {
vgmstream->ch[i].channel_start_offset= vgmstream->ch[i].offset = interleave * i;
g72x_init_state(&(vgmstream->ch[i].g72x_state));
}
}
return vgmstream;
/* clean up anything we may have opened */
fail:
if (vgmstream) close_vgmstream(vgmstream);
close_vgmstream(vgmstream);
return NULL;
}

View File

@ -1,8 +1,8 @@
#include "meta.h"
#include "../coding/coding.h"
/* SFS - from Sting games [Baroque (PS2)] */
VGMSTREAM* init_vgmstream_sfs(STREAMFILE* sf) {
/* STER - from Silicon Studios/Vicarious Visions's ALCHEMY middleware [Baroque (PS2), Star Soldier (PS2)] */
VGMSTREAM* init_vgmstream_ster(STREAMFILE* sf) {
VGMSTREAM* vgmstream = NULL;
off_t start_offset;
int loop_flag, channels, sample_rate;
@ -10,8 +10,9 @@ VGMSTREAM* init_vgmstream_sfs(STREAMFILE* sf) {
/* checks */
/* .sfs: bigfile extension (no apparent names) */
if (!check_extensions(sf, "sfs"))
/* .ster: header id (no apparent names/extensions)
* .sfs: generic bigfile extension (to be removed?)*/
if (!check_extensions(sf, "ster,sfs"))
goto fail;
if (!is_id32be(0x00,sf, "STER"))
@ -20,9 +21,10 @@ VGMSTREAM* init_vgmstream_sfs(STREAMFILE* sf) {
loop_start = read_u32le(0x08, sf); /* absolute (ex. offset 0x50 for full loops) */
/* 0x0c: data size BE */
sample_rate = read_s32be(0x10,sf);
/* 0x14~20: null */
loop_flag = loop_start != 0xFFFFFFFF;
channels = 2;
channels = 2; /* mono files are simply .VAG */
start_offset = 0x30;
@ -30,7 +32,7 @@ VGMSTREAM* init_vgmstream_sfs(STREAMFILE* sf) {
vgmstream = allocate_vgmstream(channels, loop_flag);
if (!vgmstream) goto fail;
vgmstream->meta_type = meta_SFS;
vgmstream->meta_type = meta_STER;
vgmstream->sample_rate = sample_rate;
vgmstream->num_samples = ps_bytes_to_samples(channel_size, 1);

View File

@ -32,6 +32,8 @@ typedef struct {
off_t audio_stream_type;
off_t audio_prefetch_size;
size_t audio_interleave;
off_t audio_cue_count;
off_t audio_cue_size;
int audio_fix_psx_samples;
int audio_external_and;
int audio_loop_and;
@ -79,21 +81,21 @@ typedef struct {
ubi_bao_config cfg;
/* header info */
off_t header_offset;
uint32_t header_offset;
uint8_t header_format;
uint32_t header_version;
uint32_t header_id;
uint32_t header_type;
size_t header_skip; /* common sub-header size */
size_t header_size; /* normal base size (not counting extra tables) */
size_t extra_size; /* extra tables size */
uint32_t header_skip; /* common sub-header size */
uint32_t header_size; /* normal base size (not counting extra tables) */
uint32_t extra_size; /* extra tables size */
uint32_t stream_id;
size_t stream_size;
off_t stream_offset;
uint32_t stream_size;
uint32_t stream_offset;
uint32_t prefetch_id;
size_t prefetch_size;
off_t prefetch_offset;
uint32_t prefetch_size;
uint32_t prefetch_offset;
size_t memory_skip;
size_t stream_skip;
@ -265,10 +267,10 @@ static VGMSTREAM* init_vgmstream_ubi_bao_base(ubi_bao_header* bao, STREAMFILE* s
vgmstream->coding_type = coding_NGC_DSP;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = bao->stream_size / bao->channels;
VGM_LOG("dsp=%x, %x, %x\n", bao->header_offset, bao->header_size, bao->extra_size);
/* mini DSP header (first 0x10 seem to contain DSP header fields like nibbles and format) */
dsp_read_coefs_be(vgmstream, streamHead, bao->header_offset + bao->header_size + 0x10, 0x40);
dsp_read_hist_be (vgmstream, streamHead, bao->header_offset + bao->header_size + 0x34, 0x40); /* after gain/initial ps */
dsp_read_coefs_be(vgmstream, streamHead, bao->header_offset + bao->header_size + bao->extra_size + 0x10, 0x40);
dsp_read_hist_be (vgmstream, streamHead, bao->header_offset + bao->header_size + bao->extra_size + 0x34, 0x40); /* after gain/initial ps */
break;
#ifdef VGM_USE_FFMPEG
@ -638,12 +640,12 @@ static VGMSTREAM* init_vgmstream_ubi_bao_header(ubi_bao_header* bao, STREAMFILE*
goto fail; /* not uncommon */
}
//;VGM_LOG("UBI BAO: target at %x, h_id=%08x, s_id=%08x, p_id=%08x\n",
// (uint32_t)bao->header_offset, bao->header_id, bao->stream_id, bao->prefetch_id);
//;VGM_LOG("UBI BAO: stream=%x, size=%x, res=%s\n",
// (uint32_t)bao->stream_offset, bao->stream_size, (bao->is_external ? bao->resource_name : "internal"));
//;VGM_LOG("UBI BAO: type=%i, header=%x, extra=%x, prefetch=%x, size=%x\n",
// bao->header_type, bao->header_size, bao->extra_size, (uint32_t)bao->prefetch_offset, bao->prefetch_size);
;VGM_LOG("UBI BAO: target at %x, h_id=%08x, s_id=%08x, p_id=%08x\n",
(uint32_t)bao->header_offset, bao->header_id, bao->stream_id, bao->prefetch_id);
;VGM_LOG("UBI BAO: stream=%x, size=%x, res=%s\n",
(uint32_t)bao->stream_offset, bao->stream_size, (bao->is_external ? bao->resource_name : "internal"));
;VGM_LOG("UBI BAO: type=%i, header=%x, extra=%x, prefetch=%x, size=%x\n",
bao->header_type, bao->header_size, bao->extra_size, (uint32_t)bao->prefetch_offset, bao->prefetch_size);
switch(bao->type) {
@ -830,6 +832,12 @@ static int parse_type_audio(ubi_bao_header* bao, off_t offset, STREAMFILE* sf) {
bao->channels = read_32bit(h_offset + bao->cfg.audio_channels, sf);
bao->sample_rate = read_32bit(h_offset + bao->cfg.audio_sample_rate, sf);
/* extra cue table, rare (found with DSP) [We Dare (Wii)] */
if (bao->cfg.audio_cue_size) {
//bao->cfg.audio_cue_count //not needed?
bao->extra_size = read_32bit(h_offset + bao->cfg.audio_cue_size, sf);
}
/* prefetch data is in another internal BAO right after the base header */
if (bao->cfg.audio_prefetch_size) {
bao->prefetch_size = read_32bit(h_offset + bao->cfg.audio_prefetch_size, sf);
@ -1570,6 +1578,12 @@ static void config_bao_audio_m(ubi_bao_header* bao, off_t channels, off_t sample
bao->cfg.audio_prefetch_size = prefetch_size;
}
static void config_bao_audio_c(ubi_bao_header* bao, off_t cue_count, off_t cue_size) {
/* audio header extra */
bao->cfg.audio_cue_count = cue_count;
bao->cfg.audio_cue_size = cue_size;
}
static void config_bao_sequence(ubi_bao_header* bao, off_t sequence_count, off_t sequence_single, off_t sequence_loop, off_t entry_size) {
/* sequence header and chain table */
bao->cfg.sequence_sequence_count = sequence_count;
@ -1768,6 +1782,9 @@ static int config_bao_version(ubi_bao_header* bao, STREAMFILE* sf) {
if (version == 0x0022000D) /* Just Dance (Wii) oddity */
bao->cfg.audio_ignore_resource_size = 1;
if (version == 0x0022000D) /* We Dare (Wii) */
config_bao_audio_c(bao, 0x68, 0x78);
return 1;
case 0x00220015: /* James Cameron's Avatar: The Game (PSP)-package */

View File

@ -1,106 +1,116 @@
#include "meta.h"
#include "../util/chunks.h"
#include "../coding/coding.h"
static int get_loop_points(STREAMFILE* sf, int* p_loop_start, int* p_loop_end);
static int get_loop_points(STREAMFILE* sf, uint32_t cue_offset, uint32_t cue_size, uint32_t list_offset, uint32_t list_size, int* p_loop_start, int* p_loop_end);
/* Jade RIFF - from Ubisoft Jade engine games [Beyond Good & Evil (multi), Rayman Raving Rabbids 1/2 (multi)] */
VGMSTREAM* init_vgmstream_ubi_jade(STREAMFILE* sf) {
VGMSTREAM* vgmstream = NULL;
off_t start_offset, first_offset = 0xc;
off_t fmt_offset, data_offset;
size_t fmt_size, data_size;
int loop_flag, channel_count, sample_rate, codec, block_size;
uint32_t fmt_offset = 0, fmt_size = 0, data_offset = 0, data_size = 0;
uint32_t cue_offset = 0, cue_size = 0, list_offset = 0, list_size = 0;
int loop_flag = 0, channels = 0, sample_rate = 0, codec = 0, block_size = 0;
int loop_start = 0, loop_end = 0;
int is_jade_v2 = 0;
/* checks */
/* .waa: ambiances, .wam: music, .wac: sfx, .wad: dialogs (usually)
* .wav: Beyond Good & Evil HD (PS3)
* .psw: fake/badly extracted names [ex. Rayman Raving Rabbids (PS2)] */
if (!check_extensions(sf,"waa,wac,wad,wam,wav,lwav,psw"))
if (!is_id32be(0x00,sf, "RIFF"))
goto fail;
if (read_u32le(0x04,sf) + 0x04 + 0x04 != get_streamfile_size(sf))
goto fail;
if (!is_id32be(0x08,sf, "WAVE"))
goto fail;
/* .waa: ambiances / .wam: music / .wac: sfx / .wad: dialogs (usually)
* .wav: Beyond Good & Evil HD (PS3) */
if (!check_extensions(sf,"waa,wac,wad,wam,wav,lwav"))
goto fail;
/* a slightly twisted RIFF with custom codecs */
if (!is_id32be(0x00,sf, "RIFF") ||
!is_id32be(0x08,sf, "WAVE"))
goto fail;
if (check_extensions(sf,"psw")) { /* .psw are incorrectly extracted missing 0x04 at the end */
if (read_32bitLE(0x04,sf)+0x04 != get_streamfile_size(sf))
goto fail;
}
else {
if (read_32bitLE(0x04,sf)+0x04+0x04 != get_streamfile_size(sf))
goto fail;
}
if (!find_chunk(sf, 0x666d7420,first_offset,0, &fmt_offset,&fmt_size, 0, 0)) /* "fmt " */
goto fail;
if (!find_chunk(sf, 0x64617461,first_offset,0, &data_offset,&data_size, 0, 0)) /* "data" */
goto fail;
/* ignore LyN RIFF (needed as codec 0xFFFE is reused) */
/* parse chunks (reads once linearly) */
{
off_t fact_offset;
size_t fact_size;
chunk_t rc = {0};
if (find_chunk(sf, 0x66616374,first_offset,0, &fact_offset,&fact_size, 0, 0)) { /* "fact" */
if (fact_size == 0x10 && read_32bitBE(fact_offset+0x04, sf) == 0x4C794E20) /* "LyN " */
goto fail; /* parsed elsewhere */
/* Jade doesn't use "fact", though */
}
}
rc.current = 0x0c;
while (next_chunk(&rc, sf)) {
switch(rc.type) {
case 0x666d7420: /* "fmt " */
fmt_offset = rc.offset;
fmt_size = rc.size;
/* parse format */
{
if (fmt_size < 0x10)
goto fail;
codec = (uint16_t)read_16bitLE(fmt_offset+0x00,sf);
channel_count = read_16bitLE(fmt_offset+0x02,sf);
sample_rate = read_32bitLE(fmt_offset+0x04,sf);
block_size = (uint16_t)read_16bitLE(fmt_offset+0x0c,sf);
/* 0x08: average bytes, 0x0e: bps, etc */
if (fmt_size < 0x10) /* min 0x10: MSF, 0x12: common, 0x32: MSADPCM */
goto fail;
codec = read_u16le(fmt_offset+0x00,sf);
channels = read_u16le(fmt_offset+0x02,sf);
sample_rate = read_s32le(fmt_offset+0x04,sf);
block_size = read_u16le(fmt_offset+0x0c,sf);
/* 0x08: average bytes, 0x0e: bps, etc */
break;
/* autodetect Jade "v2", uses a different interleave [Rayman Raving Rabbids (PS2/Wii)] */
switch(codec) {
case 0xFFFF: { /* PS2 */
int i;
case 0x64617461: /* "data" */
data_offset = rc.offset;
data_size = rc.size;
break;
/* half interleave check as there is no flag (ends with the PS-ADPCM stop frame) */
for (i = 0; i < channel_count; i++) {
off_t end_frame = data_offset + (data_size / channel_count) * (i+1) - 0x10;
if (read_32bitBE(end_frame+0x00,sf) != 0x07007777 ||
read_32bitBE(end_frame+0x04,sf) != 0x77777777 ||
read_32bitBE(end_frame+0x08,sf) != 0x77777777 ||
read_32bitBE(end_frame+0x0c,sf) != 0x77777777) {
is_jade_v2 = 1;
break;
}
}
break;
case 0x63756520: /* "cue ": catches PC Rabbids (hopefully) */
is_jade_v2 = 1;
cue_offset = rc.offset;
cue_size = rc.size;
break;
case 0x66616374: /* "fact" */
/* ignore LyN RIFF (needed as codec 0xFFFE is reused, and Jade doesn't set "fact") */
//if (rc.size == 0x10 && !is_id32be(rc.offset + 0x04, sf, "LyN "))
// goto fail; /* parsed elsewhere */
goto fail;
case 0x4C495354: /* "LIST": labels (rare) */
list_offset = rc.offset;
list_size = rc.size;
break;
default:
/* unknown chunk: must be another RIFF */
goto fail;
}
case 0xFFFE: /* GC/Wii */
is_jade_v2 = (read_16bitLE(fmt_offset+0x10,sf) == 0); /* extra data size (0x2e*channels) */
break;
default:
break;
}
/* hopefully catches PC Rabbids */
if (find_chunk(sf, 0x63756520,first_offset,0, NULL,NULL, 0, 0)) { /* "cue " */
is_jade_v2 = 1;
}
}
if (!fmt_offset || !fmt_size || !data_offset || !data_size)
goto fail;
/* autodetect Jade "v2", uses a different interleave [Rayman Raving Rabbids (PS2/Wii)] */
switch(codec) {
case 0xFFFF: { /* PS2 */
int i;
/* half interleave check as there is no flag (ends with the PS-ADPCM stop frame) */
for (i = 0; i < channels; i++) {
uint32_t end_frame = data_offset + (data_size / channels) * (i+1) - 0x10;
if (read_u32be(end_frame+0x00,sf) != 0x07007777 ||
read_u32be(end_frame+0x04,sf) != 0x77777777 ||
read_u32be(end_frame+0x08,sf) != 0x77777777 ||
read_u32be(end_frame+0x0c,sf) != 0x77777777) {
is_jade_v2 = 1;
break;
}
}
break;
}
case 0xFFFE: /* GC/Wii */
is_jade_v2 = (read_u16le(fmt_offset+0x10,sf) == 0); /* extra data size (0x2e*channels) */
break;
default:
break;
}
/* get loop points */
if (is_jade_v2) {
loop_flag = get_loop_points(sf, &loop_start, &loop_end); /* loops in "LIST" */
loop_flag = get_loop_points(sf, cue_offset, cue_size, list_offset, list_size, &loop_start, &loop_end); /* loops in "LIST" */
}
else {
/* BG&E files don't contain looping information, so the looping is done by extension.
@ -109,15 +119,13 @@ VGMSTREAM* init_vgmstream_ubi_jade(STREAMFILE* sf) {
loop_flag = check_extensions(sf,"waa,wam");
}
start_offset = data_offset;
/* 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;
vgmstream->meta_type = meta_UBI_JADE;
vgmstream->sample_rate = sample_rate;
if (is_jade_v2) {
vgmstream->loop_start_sample = loop_start;
vgmstream->loop_end_sample = loop_end;
@ -128,12 +136,12 @@ VGMSTREAM* init_vgmstream_ubi_jade(STREAMFILE* sf) {
case 0x0069: /* Xbox */
/* Peter Jackson's King Kong uses 0x14 (other versions don't) */
if (fmt_size != 0x12 && fmt_size != 0x14) goto fail;
if (block_size != 0x24*channel_count) goto fail;
if (block_size != 0x24*channels) goto fail;
vgmstream->coding_type = coding_XBOX_IMA;
vgmstream->layout_type = layout_none;
vgmstream->num_samples = xbox_ima_bytes_to_samples(data_size, channel_count);
vgmstream->num_samples = xbox_ima_bytes_to_samples(data_size, channels);
if (!is_jade_v2) {
vgmstream->loop_start_sample = 0;
vgmstream->loop_end_sample = vgmstream->num_samples;
@ -154,10 +162,10 @@ VGMSTREAM* init_vgmstream_ubi_jade(STREAMFILE* sf) {
vgmstream->interleave_last_block_size = (data_size % (vgmstream->interleave_block_size*vgmstream->channels)) / vgmstream->channels;
}
else {
vgmstream->interleave_block_size = data_size / channel_count;
vgmstream->interleave_block_size = data_size / channels;
}
vgmstream->num_samples = ps_bytes_to_samples(data_size, channel_count);
vgmstream->num_samples = ps_bytes_to_samples(data_size, channels);
if (!is_jade_v2) {
vgmstream->loop_start_sample = 0;
vgmstream->loop_end_sample = vgmstream->num_samples;
@ -172,7 +180,7 @@ VGMSTREAM* init_vgmstream_ubi_jade(STREAMFILE* sf) {
vgmstream->coding_type = coding_NGC_DSP;
vgmstream->layout_type = layout_interleave;
vgmstream->num_samples = dsp_bytes_to_samples(data_size, channel_count);
vgmstream->num_samples = dsp_bytes_to_samples(data_size, channels);
if (!is_jade_v2) {
vgmstream->loop_start_sample = 0;
vgmstream->loop_end_sample = vgmstream->num_samples;
@ -191,7 +199,7 @@ VGMSTREAM* init_vgmstream_ubi_jade(STREAMFILE* sf) {
};
int i, ch;
for (ch = 0; ch < channel_count; ch++) {
for (ch = 0; ch < channels; ch++) {
for (i = 0; i < 16; i++) {
vgmstream->ch[ch].adpcm_coef[i] = coef[i];
}
@ -200,23 +208,33 @@ VGMSTREAM* init_vgmstream_ubi_jade(STREAMFILE* sf) {
}
else {
/* has extra 0x2e coefs before each channel, not counted in data_size */
vgmstream->interleave_block_size = (data_size + 0x2e*channel_count) / channel_count;
vgmstream->interleave_block_size = (data_size + 0x2e*channels) / channels;
dsp_read_coefs_be(vgmstream, sf, start_offset+0x00, vgmstream->interleave_block_size);
dsp_read_hist_be (vgmstream, sf, start_offset+0x20, vgmstream->interleave_block_size);
start_offset += 0x2e;
dsp_read_coefs_be(vgmstream, sf, data_offset+0x00, vgmstream->interleave_block_size);
dsp_read_hist_be (vgmstream, sf, data_offset+0x20, vgmstream->interleave_block_size);
data_offset += 0x2e;
}
break;
case 0x0002: /* PC */
if (fmt_size != 0x12) goto fail;
if (block_size != 0x24*channel_count) goto fail;
if (fmt_size != 0x12 && fmt_size != 0x32) goto fail;
if (block_size != 0x24*channels) goto fail;
vgmstream->coding_type = coding_MSADPCM;
vgmstream->layout_type = layout_none;
vgmstream->frame_size = block_size;
vgmstream->num_samples = msadpcm_bytes_to_samples(data_size, vgmstream->frame_size, channel_count);
/* King Kong: Gamers Edition (PC) */
if (fmt_size == 0x32) {
/* standard WAVEFORMATEX must write extra size here, Jade sets 0 */
if (read_u16le(fmt_offset + 0x10, sf) != 0)
goto fail;
/* 0x12: block samples */
if (!msadpcm_check_coefs(sf, fmt_offset + 0x14))
goto fail;
}
vgmstream->num_samples = msadpcm_bytes_to_samples(data_size, vgmstream->frame_size, channels);
if (!is_jade_v2) {
vgmstream->loop_start_sample = 0;
vgmstream->loop_end_sample = vgmstream->num_samples;
@ -225,17 +243,17 @@ VGMSTREAM* init_vgmstream_ubi_jade(STREAMFILE* sf) {
break;
case 0x0001: { /* PS3 */
VGMSTREAM *temp_vgmstream = NULL;
STREAMFILE *temp_sf = NULL;
VGMSTREAM* temp_vgmstream = NULL;
STREAMFILE* temp_sf = NULL;
if (fmt_size != 0x10) goto fail;
if (block_size != 0x02*channel_count) goto fail;
if (block_size != 0x02 * channels) goto fail;
/* a MSF (usually ATRAC3) masquerading as PCM */
if (read_32bitBE(start_offset, sf) != 0x4D534643) /* "MSF\43" */
if (!is_id32be(data_offset, sf, "MSFC"))
goto fail;
temp_sf = setup_subfile_streamfile(sf, start_offset, data_size, "msf");
temp_sf = setup_subfile_streamfile(sf, data_offset, data_size, "msf");
if (!temp_sf) goto fail;
temp_vgmstream = init_vgmstream_msf(temp_sf);
@ -260,7 +278,7 @@ VGMSTREAM* init_vgmstream_ubi_jade(STREAMFILE* sf) {
}
if (!vgmstream_open_stream(vgmstream, sf,start_offset))
if (!vgmstream_open_stream(vgmstream, sf, data_offset))
goto fail;
return vgmstream;
@ -270,58 +288,54 @@ fail:
}
/* extract loops from "cue /LIST", returns if loops (info from Droolie) */
static int get_loop_points(STREAMFILE *sf, int *out_loop_start, int *out_loop_end) {
off_t cue_offset, list_offset;
size_t cue_size, list_size;
off_t offset, first_offset = 0x0c;
static int get_loop_points(STREAMFILE* sf, uint32_t cue_offset, uint32_t cue_size, uint32_t list_offset, uint32_t list_size, int* p_loop_start, int* p_loop_end) {
//off_t offset;
int i, cue_count, loop_id = 0, loop_start = 0, loop_end = 0;
chunk_t rc = {0};
/* unlooped files may contain LIST, but also may not */
if (!find_chunk(sf, 0x63756520,first_offset,0, &cue_offset,&cue_size, 0, 0)) /* "cue " */
goto fail;
if (!find_chunk(sf, 0x4C495354,first_offset,0, &list_offset,&list_size, 0, 0)) /* "LIST" */
if (!cue_offset || !cue_size || !list_offset || !list_size)
goto fail;
offset = list_offset + 0x04;
while (offset < list_offset + list_size) {
uint32_t chunk_id = read_32bitBE(offset+0x00, sf);
uint32_t chunk_size = read_32bitLE(offset+0x04, sf);
offset += 0x08;
switch(chunk_id) {
rc.current = list_offset + 0x04; /* skip "adtl" */
rc.max = list_offset + list_size;
while (next_chunk(&rc, sf)) {
switch(rc.type) {
case 0x6C61626C: /* "labl" */
if (read_32bitBE(offset+0x04, sf) == 0x6C6F6F70) /* "loop", actually an string tho */
loop_id = read_32bitLE(offset+0x00, sf);
chunk_size += (chunk_size % 2) ? 1 : 0; /* string is even-padded after size */
if (is_id32be(rc.offset + 0x04, sf, "loop")) /* actually a C-string tho */
loop_id = read_u32le(rc.offset + 0x00, sf);
if (rc.size % 2) { /* string is even-padded after size */
rc.size++;
rc.current++;
}
break;
case 0x6C747874: /* "ltxt" */
if (loop_id == read_32bitLE(offset+0x00, sf))
loop_end = read_32bitLE(offset+0x04, sf);
if (loop_id == read_u32le(rc.offset + 0x00, sf))
loop_end = read_u32le(rc.offset + 0x04, sf);
break;
default:
VGM_LOG("Jade: unknown LIST chunk\n");
VGM_LOG("UBI JADE: unknown LIST chunk\n");
goto fail;
}
offset += chunk_size;
}
if (!loop_end)
return 0;
cue_count = read_32bitLE(cue_offset+0x00, sf);
cue_count = read_u32le(cue_offset+0x00, sf);
for (i = 0; i < cue_count; i++) {
if (loop_id == read_32bitLE(cue_offset+0x04 + i*0x18 + 0x00, sf)) {
loop_start = read_32bitLE(cue_offset+0x04 + i*0x18 + 0x04, sf);
if (loop_id == read_u32le(cue_offset+0x04 + i*0x18 + 0x00, sf)) {
loop_start = read_u32le(cue_offset+0x04 + i*0x18 + 0x04, sf);
loop_end += loop_start;
break;
}
}
*out_loop_start = loop_start;
*out_loop_end = loop_end;
*p_loop_start = loop_start;
*p_loop_end = loop_end;
return 1;
fail:
@ -340,23 +354,19 @@ VGMSTREAM* init_vgmstream_ubi_jade_container(STREAMFILE* sf) {
* the RIFF + padding after. Most extractors don't remove the padding correctly, so here we add support. */
/* checks */
/* standard Jade exts + .xma for padded XMA used in Beyond Good & Evil HD (X360) */
if (!check_extensions(sf,"waa,wac,wad,wam,wav,lwav,xma"))
goto fail;
if (read_32bitBE(0x04,sf) == 0x52494646 &&
read_32bitLE(0x00,sf)+0x04 == get_streamfile_size(sf)) {
if (is_id32be(0x04,sf, "RIFF") &&
read_u32le(0x00,sf)+0x04 == get_streamfile_size(sf)) {
/* data size + RIFF + padding */
subfile_offset = 0x04;
}
else if (read_32bitBE(0x00,sf) == 0x52494646 &&
read_32bitLE(0x04,sf)+0x04+0x04 < get_streamfile_size(sf) &&
else if (is_id32be(0x00,sf, "RIFF") &&
read_u32le(0x04,sf) + 0x04 + 0x04 < get_streamfile_size(sf) &&
(get_streamfile_size(sf) + 0x04) % 0x800 == 0) {
/* RIFF + padding with data size removed (bad extraction) */
subfile_offset = 0x00;
}
else if (read_32bitBE(0x04,sf) == 0x52494646 &&
read_32bitLE(0x00,sf) == get_streamfile_size(sf)) {
else if (is_id32be(0x04,sf, "RIFF") &&
read_u32le(0x00,sf) == get_streamfile_size(sf)) {
/* data_size + RIFF + padding - 0x04 (bad extraction) */
subfile_offset = 0x04;
}
@ -364,14 +374,19 @@ VGMSTREAM* init_vgmstream_ubi_jade_container(STREAMFILE* sf) {
goto fail;
}
subfile_size = read_32bitLE(subfile_offset+0x04,sf) + 0x04+0x04;
/* standard Jade exts + .xma for padded XMA used in Beyond Good & Evil HD (X360) */
if (!check_extensions(sf,"waa,wac,wad,wam,wav,lwav,xma"))
goto fail;
temp_sf = setup_subfile_streamfile(sf, subfile_offset,subfile_size, NULL);
subfile_size = read_u32le(subfile_offset + 0x04,sf) + 0x04 + 0x04;
temp_sf = setup_subfile_streamfile(sf, subfile_offset, subfile_size, NULL);
if (!temp_sf) goto fail;
if (check_extensions(sf,"xma")) {
if (read_u16le(0x14, sf) == 0x166) {
vgmstream = init_vgmstream_xma(temp_sf);
} else {
}
else {
vgmstream = init_vgmstream_ubi_jade(temp_sf);
}

View File

@ -1,70 +1,98 @@
#include "meta.h"
#include "../layout/layout.h"
#include "../coding/coding.h"
#include "../util/chunks.h"
#include "ubi_lyn_streamfile.h"
/* LyN RIFF - from Ubisoft LyN engine games [Red Steel 2 (Wii), Adventures of Tintin (Multi), From Dust (Multi), Just Dance 3/4 (multi)] */
VGMSTREAM* init_vgmstream_ubi_lyn(STREAMFILE* sf) {
VGMSTREAM * vgmstream = NULL;
off_t start_offset, first_offset = 0xc;
off_t fmt_offset, data_offset, fact_offset;
size_t fmt_size, data_size, fact_size;
int loop_flag, channels, sample_rate, codec;
int num_samples;
VGMSTREAM* vgmstream = NULL;
off_t start_offset;
uint32_t fmt_offset = 0, fmt_size = 0, data_offset = 0, data_size = 0, fact_offset = 0, fact_size = 0;
int loop_flag = 0, channels = 0, sample_rate = 0, codec = 0;
int32_t num_samples = 0;
/* checks */
if (!is_id32be(0x00,sf, "RIFF"))
goto fail;
if (read_u32le(0x04,sf) + 0x04 + 0x04 != get_streamfile_size(sf))
goto fail;
if (!is_id32be(0x08,sf, "WAVE"))
goto fail;
/* .sns: Red Steel 2
* .wav: Tintin, Just Dance
* .son: From Dust */
* .son: From Dust, ZombieU */
if (!check_extensions(sf,"sns,wav,lwav,son"))
goto fail;
/* a slightly eccentric RIFF with custom codecs */
if (!is_id32be(0x00,sf, "RIFF") ||
!is_id32be(0x08,sf, "WAVE"))
goto fail;
if (read_32bitLE(0x04,sf) + 0x04 + 0x04 != get_streamfile_size(sf))
goto fail;
if (!find_chunk(sf, 0x666d7420,first_offset,0, &fmt_offset,&fmt_size, 0, 0)) /* "fmt " */
goto fail;
if (!find_chunk(sf, 0x64617461,first_offset,0, &data_offset,&data_size, 0, 0)) /* "data" */
goto fail;
/* always found, even with PCM (LyN subchunk seems to contain the engine version, ex. 0x0d/10) */
if (!find_chunk(sf, 0x66616374,first_offset,0, &fact_offset,&fact_size, 0, 0)) /* "fact" */
goto fail;
if (fact_size != 0x10 || read_32bitBE(fact_offset+0x04, sf) != 0x4C794E20) /* "LyN " */
goto fail;
num_samples = read_32bitLE(fact_offset+0x00, sf);
/* sometimes there is a LySE chunk */
/* parse format */
/* parse chunks (reads once linearly) */
{
if (fmt_size < 0x12)
goto fail;
codec = read_u16le(fmt_offset+0x00,sf);
channels = read_u16le(fmt_offset+0x02,sf);
sample_rate = read_s32le(fmt_offset+0x04,sf);
/* 0x08: average bytes, 0x0c: block align, 0x0e: bps, etc */
chunk_t rc = {0};
/* fake WAVEFORMATEX, used with > 2ch */
if (codec == 0xFFFE) {
if (fmt_size < 0x28)
goto fail;
/* fake GUID with first value doubling as codec */
codec = read_32bitLE(fmt_offset+0x18,sf);
if (read_32bitBE(fmt_offset+0x1c,sf) != 0x00001000 &&
read_32bitBE(fmt_offset+0x20,sf) != 0x800000AA &&
read_32bitBE(fmt_offset+0x24,sf) != 0x00389B71) {
goto fail;
rc.current = 0x0c;
while (next_chunk(&rc, sf)) {
switch(rc.type) {
case 0x666d7420: /* "fmt " */
fmt_offset = rc.offset;
fmt_size = rc.size;
if (fmt_size < 0x12)
goto fail;
codec = read_u16le(fmt_offset+0x00,sf);
channels = read_u16le(fmt_offset+0x02,sf);
sample_rate = read_s32le(fmt_offset+0x04,sf);
/* 0x08: average bytes, 0x0c: block align, 0x0e: bps, etc */
/* fake WAVEFORMATEX, used with > 2ch */
if (codec == 0xFFFE) {
if (fmt_size < 0x28)
goto fail;
/* fake GUID with first value doubling as codec */
codec = read_u32le(fmt_offset+0x18,sf);
if (read_u32be(fmt_offset+0x1c,sf) != 0x00001000 &&
read_u32be(fmt_offset+0x20,sf) != 0x800000AA &&
read_u32be(fmt_offset+0x24,sf) != 0x00389B71) {
goto fail;
}
}
break;
case 0x64617461: /* "data" */
data_offset = rc.offset;
data_size = rc.size;
break;
case 0x66616374: /* "fact" */
/* always found, even with PCM (LyN subchunk seems to contain the engine version, ex. 0x0d/10) */
fact_offset = rc.offset;
fact_size = rc.size;
if (fact_size != 0x10 || !is_id32be(fact_offset+0x04, sf, "LyN "))
goto fail;
num_samples = read_s32le(fact_offset+0x00, sf);
break;
case 0x4C795345: /* "LySE": optional, config? */
case 0x63756520: /* "cue ": total size cue? (rare) */
case 0x4C495354: /* "LIST": labels (rare) */
break;
default:
/* unknown chunk: must be another RIFF */
goto fail;
}
}
}
if (!fmt_offset || !fmt_size || !data_offset || !data_size || !fact_offset || !fact_size)
goto fail;
/* most songs simply repeat; loop if it looks long enough,
* but not too long (ex. Michael Jackson The Experience songs) */
loop_flag = (num_samples > 20*sample_rate && num_samples < 60*3*sample_rate); /* in seconds */
@ -111,15 +139,19 @@ VGMSTREAM* init_vgmstream_ubi_lyn(STREAMFILE* sf) {
break;
#ifdef VGM_USE_VORBIS
case 0x3157: { /* Ogg (PC), interleaved 1ch */
case 0x3156: /* Ogg (PC), interleaved 1ch (older version) [Rabbids Go Home (PC)] */
case 0x3157: { /* Ogg (PC), interleaved 1ch (newer version) [Adventures of Tintin (PC)] */
size_t interleave_size;
layered_layout_data* data = NULL;
int i;
if (read_32bitLE(start_offset+0x00,sf) != 1) /* id? */
goto fail;
if (codec == 0x3157) {
if (read_u32le(start_offset+0x00,sf) != 1) /* id? */
goto fail;
start_offset += 0x04;
}
interleave_size = read_32bitLE(start_offset+0x04,sf);
interleave_size = read_u32le(start_offset+0x00,sf);
/* interleave is adjusted so there is no smaller last block, it seems */
vgmstream->coding_type = coding_OGG_VORBIS;
@ -133,8 +165,8 @@ VGMSTREAM* init_vgmstream_ubi_lyn(STREAMFILE* sf) {
/* open each layer subfile */
for (i = 0; i < channels; i++) {
STREAMFILE* temp_sf = NULL;
size_t logical_size = read_32bitLE(start_offset+0x08 + 0x04*i,sf);
off_t layer_offset = start_offset + 0x08 + 0x04*channels; //+ interleave_size*i;
size_t logical_size = read_u32le(start_offset+0x04 + 0x04*i,sf);
off_t layer_offset = start_offset + 0x04 + 0x04*channels;
temp_sf = setup_ubi_lyn_streamfile(sf, layer_offset, interleave_size, i, channels, logical_size);
if (!temp_sf) goto fail;
@ -208,6 +240,38 @@ VGMSTREAM* init_vgmstream_ubi_lyn(STREAMFILE* sf) {
#endif
#ifdef VGM_USE_FFMPEG
case 0x5052: { /* MP4 AAC (WiiU), custom */
mp4_custom_t mp4 = {0};
int entries;
/* 0x00: null? */
/* 0x04: fact samples again */
entries = read_s32le(start_offset + 0x08, sf);
/* has a seek/frame table then raw (non-header) AAC data */
mp4.channels = channels;
mp4.sample_rate = sample_rate;
mp4.num_samples = num_samples;
mp4.stream_offset = data_offset + (0x0c + entries * 0x04);
mp4.stream_size = data_size - (0x0c + entries * 0x04);
mp4.table_offset = data_offset + 0x0c;
mp4.table_entries = entries;
/* assumed (not in fmt's block size, fact, LyN, etc) */
mp4.encoder_delay = 1024; /* observed, uses libaac */
mp4.end_padding = 0;
mp4.frame_samples = 1024;
vgmstream->num_samples -= mp4.encoder_delay;
vgmstream->loop_end_sample -= mp4.encoder_delay;
vgmstream->codec_data = init_ffmpeg_mp4_custom_lyn(sf, &mp4);
if (!vgmstream->codec_data) goto fail;
vgmstream->coding_type = coding_FFmpeg;
vgmstream->layout_type = layout_none;
break;
}
case 0x0166: { /* XMA (X360), standard */
uint8_t buf[0x100];
int bytes;
@ -217,8 +281,8 @@ VGMSTREAM* init_vgmstream_ubi_lyn(STREAMFILE* sf) {
/* skip standard XMA header + seek table */
/* 0x00: version? no apparent differences (0x1=Just Dance 4, 0x3=others) */
chunk_offset = start_offset + 0x04 + 0x04;
chunk_size = read_32bitLE(start_offset + 0x04, sf);
seek_size = read_32bitLE(chunk_offset+chunk_size, sf);
chunk_size = read_u32le(start_offset + 0x04, sf);
seek_size = read_u32le(chunk_offset+chunk_size, sf);
start_offset += (0x04 + 0x04 + chunk_size + 0x04 + seek_size);
data_size -= (0x04 + 0x04 + chunk_size + 0x04 + seek_size);
@ -248,9 +312,9 @@ fail:
/* LyN RIFF in containers */
VGMSTREAM * init_vgmstream_ubi_lyn_container(STREAMFILE *sf) {
VGMSTREAM *vgmstream = NULL;
STREAMFILE *temp_sf = NULL;
VGMSTREAM* init_vgmstream_ubi_lyn_container(STREAMFILE* sf) {
VGMSTREAM* vgmstream = NULL;
STREAMFILE* temp_sf = NULL;
off_t subfile_offset;
size_t subfile_size;
@ -262,29 +326,30 @@ VGMSTREAM * init_vgmstream_ubi_lyn_container(STREAMFILE *sf) {
goto fail;
/* find "RIFF" position */
if (read_32bitBE(0x00,sf) == 0x4C795345 && /* "LySE" */
read_32bitBE(0x14,sf) == 0x52494646) { /* "RIFF" */
if (is_id32be(0x00,sf, "LySE") &&
is_id32be(0x14,sf, "RIFF")) {
subfile_offset = 0x14; /* Adventures of Tintin */
}
else if (read_32bitBE(0x20,sf) == 0x4C795345 && /* "LySE" */
read_32bitBE(0x34,sf) == 0x52494646) { /* "RIFF" */
else if (read_u32le(0x00,sf) + 0x22 == get_streamfile_size(sf) &&
is_id32be(0x20,sf, "LySE") &&
is_id32be(0x34,sf, "RIFF")) {
subfile_offset = 0x34; /* Michael Jackson The Experience (Wii) */
}
else if (read_32bitLE(0x00,sf)+0x20 == get_streamfile_size(sf) &&
read_32bitBE(0x20,sf) == 0x52494646) { /* "RIFF" */
subfile_offset = 0x20; /* Red Steel 2, From Dust */
else if (read_u32le(0x00,sf)+0x20 == get_streamfile_size(sf) &&
is_id32be(0x20,sf, "RIFF")) {
subfile_offset = 0x20; /* Red Steel 2, From Dust, ZombieU (also has "SON\0" at 0x18) */
}
else {
goto fail;
}
subfile_size = read_32bitLE(subfile_offset+0x04,sf) + 0x04+0x04;
subfile_size = read_u32le(subfile_offset+0x04,sf) + 0x04 + 0x04;
temp_sf = setup_subfile_streamfile(sf, subfile_offset, subfile_size, NULL);
if (!temp_sf) goto fail;
vgmstream = init_vgmstream_ubi_lyn(temp_sf);
close_streamfile(temp_sf);
return vgmstream;

View File

@ -4,7 +4,7 @@
/* .WBK - seen in some Treyarch games [Spider-Man 2, Ultimate Spider-Man, Call of Duty 2: Big Red One] */
VGMSTREAM* init_vgmstream_wbk(STREAMFILE* sf) {
VGMSTREAM* vgmstream = NULL;
uint32_t table_offset, entry_offset, data_offset, streams_offset, strings_offset, strings_size, coefsec_offset,
uint32_t table_offset, entry_offset, data_offset, strings_offset, coefsec_offset,
name_offset, codec, flags, channels, sound_offset, sound_size, num_samples, sample_rate;
int target_subsong = sf->stream_index, total_subsongs, loop_flag, has_names, i;
@ -13,14 +13,13 @@ VGMSTREAM* init_vgmstream_wbk(STREAMFILE* sf) {
!is_id32be(0x04, sf, "BK11"))
goto fail;
/* checks */
if (!check_extensions(sf, "wbk"))
goto fail;
/* always little endian, even on GC */
data_offset = read_u32le(0x10, sf);
//data_size = read_u32le(0x14, sf);
streams_offset = read_u32le(0x18, sf);
//streams_offset = read_u32le(0x18, sf);
//streams_size = read_u32le(0x1c, sf);
total_subsongs = read_u32le(0x40, sf);
@ -34,7 +33,7 @@ VGMSTREAM* init_vgmstream_wbk(STREAMFILE* sf) {
//paramsec_offset = read_u32le(0x54, sf);
//coefsec_size = read_u32le(0x58, sf);
coefsec_offset = read_u32le(0x5c, sf);
strings_size = read_u32le(0x60, sf);
//strings_size = read_u32le(0x60, sf);
strings_offset = read_u32le(0x64, sf);
/* Ultimate Spider-Man has no names, only name hashes */
@ -60,7 +59,7 @@ VGMSTREAM* init_vgmstream_wbk(STREAMFILE* sf) {
* 0x1c: sound offset
* 0x20: sample rate
* 0x24: always 0?
*
*
* struct slightly changed in Call of Duty 2 but still compatible
*/
entry_offset = table_offset + (target_subsong - 1) * 0x28;
@ -94,7 +93,7 @@ VGMSTREAM* init_vgmstream_wbk(STREAMFILE* sf) {
switch (codec) {
case 0x03: { /* DSP */
uint32_t coef_offset;
uint16_t coef_table[16] = {
static const int16_t coef_table[16] = {
0x0216,0xfc9f,0x026c,0x04b4,0x065e,0xfdec,0x0a11,0xfd1e,
0x0588,0xfc38,0x05ad,0x01da,0x083b,0xfdbc,0x08c3,0xff18
};
@ -116,6 +115,7 @@ VGMSTREAM* init_vgmstream_wbk(STREAMFILE* sf) {
}
break;
}
case 0x04: /* PSX */
vgmstream->coding_type = coding_PSX;
vgmstream->layout_type = layout_interleave;
@ -152,4 +152,182 @@ VGMSTREAM* init_vgmstream_wbk(STREAMFILE* sf) {
fail:
close_vgmstream(vgmstream);
return NULL;
}
}
/* .WBK - evolution of the above Treyarch bank format [Call of Duty 3] */
VGMSTREAM* init_vgmstream_wbk_nslb(STREAMFILE* sf) {
VGMSTREAM* vgmstream = NULL;
uint32_t table_offset, name_table_offset, strings_offset, info_offset, data_offset, streams_offset,
name_offset, entry_offset, info_entry_offset,
codec, flags, channels, sound_offset, sound_size, num_samples, sample_rate;
int target_subsong = sf->stream_index, total_subsongs, loop_flag;
/* checks */
if (!is_id32be(0x00, sf, "NSLB"))
goto fail;
if (!check_extensions(sf, "wbk"))
goto fail;
/* always little endian, even on PS3/X360 */
if (read_u32le(0x04, sf) != 0x01)
goto fail;
total_subsongs = read_u32le(0x10, sf);
if (target_subsong == 0) target_subsong = 1;
if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1)
goto fail;
name_table_offset = read_u32le(0x18, sf);
table_offset = read_u32le(0x1c, sf);
//info_size = read_u32le(0x20, sf);
info_offset = read_u32le(0x24, sf);
//strings_size = read_u32le(0x28, sf);
strings_offset = read_u32le(0x2c, sf);
//data_size = read_u32le(0x30, sf);
data_offset = read_u32le(0x34, sf);
//streams_size = read_u32le(0x38, sf);
streams_offset = read_u32le(0x3c, sf);
name_offset = read_u32le(name_table_offset + 0x04 * (target_subsong - 1), sf);
entry_offset = table_offset + 0x10 * (target_subsong - 1);
info_entry_offset = read_u32le(entry_offset + 0x00, sf) + info_offset;
sound_offset = read_u32le(entry_offset + 0x04, sf);
sound_size = read_u32le(entry_offset + 0x08, sf);
num_samples = read_u32le(entry_offset + 0x0c, sf);
sample_rate = read_u16le(info_entry_offset + 0x00, sf);
codec = read_u8(info_entry_offset + 0x04, sf);
flags = read_u8(info_entry_offset + 0x05, sf);
channels = read_u8(info_entry_offset + 0x06, sf) == 0x03 ? 2 : 1;
if (flags & 0x01)
sound_offset += streams_offset;
else
sound_offset += data_offset;
loop_flag = (flags & 0x02);
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channels, loop_flag);
if (!vgmstream) goto fail;
/* fill in the vital statistics */
vgmstream->meta_type = meta_WBK_NSLB;
vgmstream->sample_rate = sample_rate;
vgmstream->stream_size = sound_size;
vgmstream->num_samples = num_samples;
vgmstream->loop_start_sample = 0;
vgmstream->loop_end_sample = num_samples; /* full loops only */
vgmstream->num_streams = total_subsongs;
switch (codec) {
case 0x20: /* XBOX */
vgmstream->coding_type = coding_XBOX_IMA;
vgmstream->layout_type = layout_none;
break;
case 0x21: /* PSX */
vgmstream->coding_type = coding_PSX;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = (flags & 0x01) ? 0x800 : sound_size / channels;
break;
case 0x22: { /* DSP */
int i, j;
vgmstream->coding_type = coding_NGC_DSP;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = (flags & 0x01) ? 0x8000 : sound_size / channels;
/* It looks like the table is re-interpreted as 8 32-bit integers and stored with little endian byte order
* like the rest of the data. Fun times. */
for (i = 0; i < vgmstream->channels; i++) {
off_t coef_offset = info_entry_offset + 0x4c + 0x30*i;
for (j = 0; j < 8; j++) {
vgmstream->ch[i].adpcm_coef[j*2] = read_s16le(coef_offset + j*0x04 + 0x02, sf);
vgmstream->ch[i].adpcm_coef[j*2+1] = read_s16le(coef_offset + j*0x04 + 0x00, sf);
}
}
break;
}
case 0x25: { /* FSB IMA */
VGMSTREAM* fsb_vgmstream;
STREAMFILE* temp_sf;
/* skip "fsb3adpc" */
sound_offset += 0x08;
sound_size -= 0x08;
temp_sf = setup_subfile_streamfile(sf, sound_offset, sound_size, "fsb");
if (!temp_sf) goto fail;
temp_sf->stream_index = 0;
fsb_vgmstream = init_vgmstream_fsb(temp_sf);
close_streamfile(temp_sf);
if (!fsb_vgmstream) goto fail;
fsb_vgmstream->meta_type = vgmstream->meta_type;
fsb_vgmstream->num_streams = vgmstream->num_streams;
vgmstream_force_loop(fsb_vgmstream, loop_flag, 0, fsb_vgmstream->num_samples);
read_string(fsb_vgmstream->stream_name, STREAM_NAME_SIZE, strings_offset + name_offset, sf);
close_vgmstream(vgmstream);
return fsb_vgmstream;
}
#ifdef VGM_USE_FFMPEG
case 0x30: { /* RIFF XMA */
uint8_t buf[0x100];
off_t riff_fmt_offset, riff_data_offset;
size_t bytes, riff_fmt_size, riff_data_size;
/* find "fmt" chunk */
if (!find_chunk_riff_le(sf, 0x666d7420, sound_offset + 0x0c, sound_size - 0x0c, &riff_fmt_offset, &riff_fmt_size))
goto fail;
/* find "data" chunk */
if (!find_chunk_riff_le(sf, 0x64617461, sound_offset + 0x0c, sound_size - 0x0c, &riff_data_offset, &riff_data_size))
goto fail;
bytes = ffmpeg_make_riff_xma_from_fmt_chunk(buf, 0x100, riff_fmt_offset, riff_fmt_size, riff_data_size, sf, 0);
vgmstream->codec_data = init_ffmpeg_header_offset(sf, buf, bytes, riff_data_offset, riff_data_size);
if (!vgmstream->codec_data) goto fail;
vgmstream->coding_type = coding_FFmpeg;
vgmstream->layout_type = layout_none;
xma_fix_raw_samples(vgmstream, sf, riff_data_offset, riff_data_size, riff_fmt_offset, 0, 0);
break;
}
#endif
#ifdef VGM_USE_MPEG
case 0x32: { /* MP3 */
coding_t mpeg_coding;
vgmstream->codec_data = init_mpeg(sf, sound_offset, &mpeg_coding, channels);
if (!vgmstream->codec_data) goto fail;
vgmstream->coding_type = mpeg_coding;
vgmstream->layout_type = layout_none;
break;
}
#endif
default:
goto fail;
}
if (codec != 0x21) /* name table is filled with garbage on PS2 for some reason */
read_string(vgmstream->stream_name, STREAM_NAME_SIZE, strings_offset + name_offset, sf);
if (!vgmstream_open_stream(vgmstream, sf, sound_offset))
goto fail;
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;
}

View File

@ -56,7 +56,7 @@ typedef struct {
} wwise_header;
static int parse_wwise(STREAMFILE* sf, wwise_header* ww);
static int is_dsp_full_interleave(STREAMFILE* sf, wwise_header* ww, off_t coef_offset);
static int is_dsp_full_interleave(STREAMFILE* sf, wwise_header* ww, off_t coef_offset, int full_detection);
/* Wwise - Audiokinetic Wwise (WaveWorks Interactive Sound Engine) middleware */
VGMSTREAM* init_vgmstream_wwise(STREAMFILE* sf) {
@ -329,13 +329,16 @@ VGMSTREAM* init_vgmstream_wwise_bnk(STREAMFILE* sf, int* p_prefetch) {
vgmstream->num_samples = dsp_bytes_to_samples(ww.data_size, ww.channels);
if (ww.wiih_size != 0x2e * ww.channels) goto fail;
if (is_dsp_full_interleave(sf, &ww, ww.wiih_offset))
if (is_dsp_full_interleave(sf, &ww, ww.wiih_offset, 1))
vgmstream->interleave_block_size = ww.data_size / 2;
}
else if (ww.extra_size == 0x0c + ww.channels * 0x2e) { /* newer */
vgmstream->num_samples = read_s32(ww.fmt_offset + 0x18, sf);
ww.wiih_offset = ww.fmt_offset + 0x1c;
ww.wiih_size = 0x2e * ww.channels;
if (is_dsp_full_interleave(sf, &ww, ww.wiih_offset, 0)) /* less common */
vgmstream->interleave_block_size = ww.data_size / 2;
}
else {
goto fail;
@ -670,9 +673,9 @@ fail:
return NULL;
}
static int is_dsp_full_interleave(STREAMFILE* sf, wwise_header* ww, off_t coef_offset) {
/* older (only?) Wwise use full interleave for memory (in .bnk) files, but
* detection from the .wem side is problematic [Punch Out!! (Wii)]
static int is_dsp_full_interleave(STREAMFILE* sf, wwise_header* ww, off_t coef_offset, int full_detection) {
/* older (bank ~v48) Wwise use full interleave for memory (in .bnk) files, but
* detection from the .wem side is problematic [Punch Out!! (Wii)-old, Luigi's Mansion 2 (3DS)-new]
* - prefetch point to streams = normal
* - .bnk would be memory banks = full
* - otherwise small-ish sizes, stereo, with initial predictors for the
@ -692,7 +695,8 @@ static int is_dsp_full_interleave(STREAMFILE* sf, wwise_header* ww, off_t coef_o
if (ww->data_size > 0x30000)
return 0;
{
/* skip reading data if possible */
if (full_detection) {
uint16_t head_ps2 = read_u16be(coef_offset + 1 * 0x2e + 0x22, sf); /* ch2's initial predictor */
uint16_t init_ps2 = read_u8(ww->data_offset + 0x08, sf); /* at normal interleave */
uint16_t half_ps2 = read_u8(ww->data_offset + ww->data_size / 2, sf); /* at full interleave */

View File

@ -23,9 +23,6 @@ VGMSTREAM* (*init_vgmstream_functions[])(STREAMFILE* sf) = {
init_vgmstream_brstm,
init_vgmstream_bfwav,
init_vgmstream_nds_strm,
init_vgmstream_agsc,
init_vgmstream_ngc_adpdtk,
init_vgmstream_rsf,
init_vgmstream_afc,
init_vgmstream_ast,
init_vgmstream_halpst,
@ -45,15 +42,11 @@ VGMSTREAM* (*init_vgmstream_functions[])(STREAMFILE* sf) = {
init_vgmstream_ngc_dsp_stm,
init_vgmstream_exst,
init_vgmstream_svag_kcet,
init_vgmstream_mib_mih,
init_vgmstream_ngc_mpdsp,
init_vgmstream_ps2_mic,
init_vgmstream_ngc_dsp_std_int,
init_vgmstream_vag,
init_vgmstream_vag_aaap,
init_vgmstream_seb,
init_vgmstream_ps2_ild,
init_vgmstream_ps2_pnb,
init_vgmstream_ild,
init_vgmstream_ngc_str,
init_vgmstream_ea_schl,
init_vgmstream_caf,
@ -93,7 +86,7 @@ VGMSTREAM* (*init_vgmstream_functions[])(STREAMFILE* sf) = {
init_vgmstream_leg,
init_vgmstream_filp,
init_vgmstream_ikm,
init_vgmstream_sfs,
init_vgmstream_ster,
init_vgmstream_bg00,
init_vgmstream_sat_dvi,
init_vgmstream_dc_kcey,
@ -216,7 +209,6 @@ VGMSTREAM* (*init_vgmstream_functions[])(STREAMFILE* sf) = {
init_vgmstream_dmsg,
init_vgmstream_ngc_dsp_aaap,
init_vgmstream_ngc_dsp_konami,
init_vgmstream_ps2_ster,
init_vgmstream_ps2_wb,
init_vgmstream_bnsf,
init_vgmstream_ps2_gcm,
@ -258,7 +250,6 @@ VGMSTREAM* (*init_vgmstream_functions[])(STREAMFILE* sf) = {
init_vgmstream_lsf_n1nj4n,
init_vgmstream_xwav_new,
init_vgmstream_xwav_old,
init_vgmstream_ps2_wmus,
init_vgmstream_hyperscan_kvag,
init_vgmstream_ios_psnd,
init_vgmstream_adp_bos,
@ -526,15 +517,26 @@ VGMSTREAM* (*init_vgmstream_functions[])(STREAMFILE* sf) = {
init_vgmstream_lopu_fb,
init_vgmstream_lpcm_fb,
init_vgmstream_wbk,
init_vgmstream_wbk_nslb,
init_vgmstream_dsp_apex,
/* lower priority metas (no clean header identity, somewhat ambiguous, or need extension/companion file to identify) */
init_vgmstream_agsc,
init_vgmstream_dtk,
init_vgmstream_rsf,
init_vgmstream_ps2_wmus,
init_vgmstream_mib_mih,
init_vgmstream_mic_koei,
init_vgmstream_seb,
init_vgmstream_ps2_pnb,
/* 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 */
init_vgmstream_encrypted, /* encrypted stuff */
init_vgmstream_btsnd, /* semi-headerless */
init_vgmstream_raw_int, /* .int raw PCM */
init_vgmstream_ps_headerless, /* tries to detect a bunch of PS-ADPCM formats */
init_vgmstream_raw_snds, /* .snds raw SNDS IMA (*after* ps_headerless) */
init_vgmstream_raw_snds, /* .snds raw SNDS IMA */
init_vgmstream_raw_wavm, /* .wavm raw xbox */
init_vgmstream_raw_pcm, /* .raw raw PCM */
init_vgmstream_s14_sss, /* .s14/sss raw siren14 */

View File

@ -342,8 +342,8 @@ typedef enum {
meta_AAX, /* CRI AAX */
meta_UTF_DSP, /* CRI ADPCM_WII, like AAX with DSP */
meta_NGC_ADPDTK, /* NGC DTK/ADP (.adp/dkt DTK) [no header_id] */
meta_RSF, /* Retro Studios RSF (Metroid Prime .rsf) [no header_id] */
meta_DTK,
meta_RSF,
meta_HALPST, /* HAL Labs HALPST */
meta_GCSW, /* GCSW (PCM) */
meta_CAF, /* tri-Crescendo CAF */
@ -367,7 +367,7 @@ typedef enum {
meta_PS2_VAGp_AAAP, /* Acclaim Austin Audio VAG header */
meta_SEB,
meta_STR_WAV, /* Blitz Games STR+WAV files */
meta_PS2_ILD, /* ILD File */
meta_ILD,
meta_PS2_PNB, /* PsychoNauts Bgm File */
meta_VPK, /* VPK Audio File */
meta_PS2_BMDX, /* Beatmania thing */
@ -392,7 +392,7 @@ typedef enum {
meta_LEG, /* Legaia 2 [no header_id] */
meta_FILP, /* Resident Evil - Dead Aim */
meta_IKM,
meta_SFS, /* Baroque */
meta_STER,
meta_BG00, /* Ibara, Mushihimesama */
meta_PS2_RSTM, /* Midnight Club 3 */
meta_PS2_KCES, /* Dance Dance Revolution */
@ -522,7 +522,6 @@ typedef enum {
meta_AST_MMV,
meta_DMSG, /* Nightcaster II - Equinox (XBOX) */
meta_NGC_DSP_AAAP, /* Turok: Evolution (NGC), Vexx (NGC) */
meta_PS2_STER, /* Juuni Kokuki: Kakukaku Taru Ou Michi Beni Midori no Uka */
meta_PS2_WB, /* Shooting Love. ~TRIZEAL~ */
meta_S14, /* raw Siren 14, 24kbit mono */
meta_SSS, /* raw Siren 14, 48kbit stereo */
@ -762,6 +761,7 @@ typedef enum {
meta_LOPU_FB,
meta_LPCM_FB,
meta_WBK,
meta_WBK_NSLB,
meta_DSP_APEX,
} meta_t;