Updated VGMStream to r1690-35-gc38c09fb
parent
16b1bfea03
commit
78fa5accc0
|
@ -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 */,
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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"},
|
||||
};
|
||||
|
||||
|
|
|
@ -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++) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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_*/
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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*/
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
|
@ -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 */
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue