mirror of
https://github.com/vgmstream/vgmstream.git
synced 2024-11-24 23:10:10 +01:00
commit
9346ae50ed
@ -226,8 +226,9 @@ single file). Contains dynamic text commands to read data from the original
|
||||
file, or static values.
|
||||
|
||||
**TXTP**: a text playlist that works as a single song. Can contain a list of
|
||||
filenames to play as one (ex. "intro.vag" "loop.vag"), name with subsong index
|
||||
(ex. bgm.sxd#10), or mask channels to only play some (ex. "song.adx#c1,2").
|
||||
filenames to play as one (ex. "intro.vag" "loop.vag"), separate channel files
|
||||
to join as a single multichannel file, subsong index (ex. bgm.sxd#10), or
|
||||
channel mask to allow only certain channels (ex. "song.adx#c1,2").
|
||||
|
||||
Creation of those files is meant for advanced users, docs can be found in
|
||||
vgmstream source.
|
||||
@ -297,6 +298,7 @@ are used in few games.
|
||||
- Konami XMD 4-bit ADPCM
|
||||
- Argonaut ASF 4-bit ADPCM
|
||||
- Circus XPCM ADPCM
|
||||
- PC-FX ADPCM
|
||||
- SDX2 2:1 Squareroot-Delta-Exact compression DPCM
|
||||
- CBD2 2:1 Cuberoot-Delta-Exact compression DPCM
|
||||
- Activision EXAKT SASSC DPCM
|
||||
|
@ -30,7 +30,7 @@ static const int EA_XA_TABLE[20] = {
|
||||
};
|
||||
|
||||
/* EA XA v2 (always mono); like ea_xa_int but with "PCM samples" flag and doesn't add 128 on expand or clamp (pre-adjusted by the encoder?) */
|
||||
void decode_ea_xa_v2(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do,int channel) {
|
||||
void decode_ea_xa_v2(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) {
|
||||
uint8_t frame_info;
|
||||
int32_t coef1, coef2;
|
||||
int i, sample_count, shift;
|
||||
@ -83,6 +83,106 @@ void decode_ea_xa_v2(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspac
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
/* later PC games use float math, though in the end sounds basically the same (decompiled from various exes) */
|
||||
static const double XA_K0[16] = { 0.0, 0.9375, 1.796875, 1.53125 };
|
||||
static const double XA_K1[16] = { 0.0, 0.0, -0.8125, -0.859375 };
|
||||
/* code uses look-up table but it's be equivalent to:
|
||||
* (double)((nibble << 28) >> (shift + 8) >> 8) or (double)(signed_nibble << (12 - shift)) */
|
||||
static const uint32_t FLOAT_TABLE_INT[256] = {
|
||||
0x00000000,0x45800000,0x46000000,0x46400000,0x46800000,0x46A00000,0x46C00000,0x46E00000,
|
||||
0xC7000000,0xC6E00000,0xC6C00000,0xC6A00000,0xC6800000,0xC6400000,0xC6000000,0xC5800000,
|
||||
0x00000000,0x45000000,0x45800000,0x45C00000,0x46000000,0x46200000,0x46400000,0x46600000,
|
||||
0xC6800000,0xC6600000,0xC6400000,0xC6200000,0xC6000000,0xC5C00000,0xC5800000,0xC5000000,
|
||||
0x00000000,0x44800000,0x45000000,0x45400000,0x45800000,0x45A00000,0x45C00000,0x45E00000,
|
||||
0xC6000000,0xC5E00000,0xC5C00000,0xC5A00000,0xC5800000,0xC5400000,0xC5000000,0xC4800000,
|
||||
0x00000000,0x44000000,0x44800000,0x44C00000,0x45000000,0x45200000,0x45400000,0x45600000,
|
||||
0xC5800000,0xC5600000,0xC5400000,0xC5200000,0xC5000000,0xC4C00000,0xC4800000,0xC4000000,
|
||||
0x00000000,0x43800000,0x44000000,0x44400000,0x44800000,0x44A00000,0x44C00000,0x44E00000,
|
||||
0xC5000000,0xC4E00000,0xC4C00000,0xC4A00000,0xC4800000,0xC4400000,0xC4000000,0xC3800000,
|
||||
0x00000000,0x43000000,0x43800000,0x43C00000,0x44000000,0x44200000,0x44400000,0x44600000,
|
||||
0xC4800000,0xC4600000,0xC4400000,0xC4200000,0xC4000000,0xC3C00000,0xC3800000,0xC3000000,
|
||||
0x00000000,0x42800000,0x43000000,0x43400000,0x43800000,0x43A00000,0x43C00000,0x43E00000,
|
||||
0xC4000000,0xC3E00000,0xC3C00000,0xC3A00000,0xC3800000,0xC3400000,0xC3000000,0xC2800000,
|
||||
0x00000000,0x42000000,0x42800000,0x42C00000,0x43000000,0x43200000,0x43400000,0x43600000,
|
||||
0xC3800000,0xC3600000,0xC3400000,0xC3200000,0xC3000000,0xC2C00000,0xC2800000,0xC2000000,
|
||||
0x00000000,0x41800000,0x42000000,0x42400000,0x42800000,0x42A00000,0x42C00000,0x42E00000,
|
||||
0xC3000000,0xC2E00000,0xC2C00000,0xC2A00000,0xC2800000,0xC2400000,0xC2000000,0xC1800000,
|
||||
0x00000000,0x41000000,0x41800000,0x41C00000,0x42000000,0x42200000,0x42400000,0x42600000,
|
||||
0xC2800000,0xC2600000,0xC2400000,0xC2200000,0xC2000000,0xC1C00000,0xC1800000,0xC1000000,
|
||||
0x00000000,0x40800000,0x41000000,0x41400000,0x41800000,0x41A00000,0x41C00000,0x41E00000,
|
||||
0xC2000000,0xC1E00000,0xC1C00000,0xC1A00000,0xC1800000,0xC1400000,0xC1000000,0xC0800000,
|
||||
0x00000000,0x40000000,0x40800000,0x40C00000,0x41000000,0x41200000,0x41400000,0x41600000,
|
||||
0xC1800000,0xC1600000,0xC1400000,0xC1200000,0xC1000000,0xC0C00000,0xC0800000,0xC0000000,
|
||||
0x00000000,0x3F800000,0x40000000,0x40400000,0x40800000,0x40A00000,0x40C00000,0x40E00000,
|
||||
0xC1000000,0xC0E00000,0xC0C00000,0xC0A00000,0xC0800000,0xC0400000,0xC0000000,0xBF800000,
|
||||
0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,
|
||||
0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,
|
||||
0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,
|
||||
0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,
|
||||
0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,
|
||||
0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,
|
||||
};
|
||||
static const float *FLOAT_TABLE = (const float *)FLOAT_TABLE_INT;
|
||||
|
||||
void decode_ea_xa_v2(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) {
|
||||
uint8_t frame_info;
|
||||
int i, sample_count, shift;
|
||||
|
||||
int pcm_frame_size = 0x01 + 2*0x02 + 28*0x02;
|
||||
int xa_frame_size = 0x0f;
|
||||
int frame_samples = 28;
|
||||
first_sample = first_sample % frame_samples;
|
||||
|
||||
/* header */
|
||||
frame_info = read_8bit(stream->offset,stream->streamfile);
|
||||
|
||||
if (frame_info == 0xEE) { /* PCM frame (used in later revisions), samples always BE */
|
||||
stream->adpcm_history1_double = read_16bitBE(stream->offset + 0x01 + 0x00,stream->streamfile);
|
||||
stream->adpcm_history2_double = read_16bitBE(stream->offset + 0x01 + 0x02,stream->streamfile);
|
||||
|
||||
for (i=first_sample,sample_count=0; i<first_sample+samples_to_do; i++,sample_count+=channelspacing) {
|
||||
outbuf[sample_count] = read_16bitBE(stream->offset + 0x01 + 2*0x02 + i*0x02,stream->streamfile);
|
||||
}
|
||||
|
||||
/* only increment offset on complete frame */
|
||||
if (i == frame_samples)
|
||||
stream->offset += pcm_frame_size;
|
||||
}
|
||||
else { /* ADPCM frame */
|
||||
double coef1, coef2, hist1, hist2, new_sample;
|
||||
|
||||
coef1 = XA_K0[(frame_info >> 4)];
|
||||
coef2 = XA_K1[(frame_info >> 4)];
|
||||
shift = (frame_info & 0x0F) + 8;// << 4;
|
||||
hist1 = stream->adpcm_history1_double;
|
||||
hist2 = stream->adpcm_history2_double;
|
||||
|
||||
for (i=first_sample,sample_count=0; i<first_sample+samples_to_do; i++,sample_count+=channelspacing) {
|
||||
uint8_t sample_byte, sample_nibble;
|
||||
off_t byte_offset = (stream->offset + 0x01 + i/2);
|
||||
int nibble_shift = (!(i&1)) ? 4 : 0; /* high nibble first */
|
||||
|
||||
sample_byte = (uint8_t)read_8bit(byte_offset,stream->streamfile);
|
||||
sample_nibble = (sample_byte >> nibble_shift) & 0x0F;
|
||||
new_sample = (double)FLOAT_TABLE[sample_nibble + shift];
|
||||
new_sample = new_sample + coef1 * hist1 + coef2 * hist2;
|
||||
|
||||
outbuf[sample_count] = clamp16((int)new_sample);
|
||||
hist2 = hist1;
|
||||
hist1 = new_sample;
|
||||
}
|
||||
|
||||
stream->adpcm_history1_double = hist1;
|
||||
stream->adpcm_history2_double = hist2;
|
||||
|
||||
/* only increment offset on complete frame */
|
||||
if (i == frame_samples)
|
||||
stream->offset += xa_frame_size;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* EA XA v1 stereo */
|
||||
void decode_ea_xa(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do,int channel) {
|
||||
uint8_t frame_info;
|
||||
|
@ -129,6 +129,7 @@ static const char* extension_list[] = {
|
||||
|
||||
"e4x",
|
||||
"eam",
|
||||
"eas",
|
||||
"eda", //txth/reserved [Project Eden (PS2)]
|
||||
"emff", //fake extension for .mul (to be removed)
|
||||
"enm",
|
||||
@ -156,10 +157,12 @@ static const char* extension_list[] = {
|
||||
"genh",
|
||||
"gms",
|
||||
"gsb",
|
||||
//"gsf", //conflicts with GBA gsf plugins?
|
||||
"gtd",
|
||||
"gwm",
|
||||
|
||||
"h4m",
|
||||
"hab",
|
||||
"hca",
|
||||
"hdr",
|
||||
"hgc1",
|
||||
@ -417,8 +420,6 @@ static const char* extension_list[] = {
|
||||
"thp",
|
||||
"tk5",
|
||||
"tra",
|
||||
"trj",
|
||||
"trm",
|
||||
"tun",
|
||||
"txth",
|
||||
"txtp",
|
||||
@ -466,6 +467,7 @@ static const char* extension_list[] = {
|
||||
"wavebatch",
|
||||
"wavm",
|
||||
"wb",
|
||||
"wd",
|
||||
"wem",
|
||||
"wii",
|
||||
"wip", //txth/reserved [Colin McRae DiRT (PC)]
|
||||
@ -751,7 +753,7 @@ static const meta_info meta_info_list[] = {
|
||||
{meta_RWAV, "Nintendo RWAV header"},
|
||||
{meta_CWAV, "Nintendo CWAV header"},
|
||||
{meta_FWAV, "Nintendo FWAV header"},
|
||||
{meta_XA, "Sony XA RIFF header"},
|
||||
{meta_XA, "Sony XA header"},
|
||||
{meta_PS2_RXWS, "Sony RXWS header"},
|
||||
{meta_PS2_RAW, ".int PCM raw header"},
|
||||
{meta_PS2_OMU, "Alter Echo OMU Header"},
|
||||
@ -802,7 +804,8 @@ static const meta_info meta_info_list[] = {
|
||||
{meta_HGC1, "Knights of the Temple 2 hgC1 Header"},
|
||||
{meta_AUS, "Capcom AUS Header"},
|
||||
{meta_RWS, "RenderWare RWS header"},
|
||||
{meta_EA_1SNH, "Electronic Arts 1SNh/EACS header"},
|
||||
{meta_EA_1SNH, "Electronic Arts 1SNh header"},
|
||||
{meta_EA_EACS, "Electronic Arts EACS header"},
|
||||
{meta_SL3, "Atari Melbourne House SL3 header"},
|
||||
{meta_FSB1, "FMOD Sample Bank (FSB1) Header"},
|
||||
{meta_FSB2, "FMOD Sample Bank (FSB2) Header"},
|
||||
@ -842,6 +845,9 @@ static const meta_info meta_info_list[] = {
|
||||
{meta_PS2_RKV, "Legacy of Kain - Blood Omen 2 RKV PS2 header"},
|
||||
{meta_PS2_VAS, "Pro Baseball Spirits 5 VAS Header"},
|
||||
{meta_PS2_TEC, "assumed TECMO badflagged stream by .tec extension"},
|
||||
{meta_PS2_ENTH, ".enth Header"},
|
||||
{meta_SDT, "High Voltage .sdt header"},
|
||||
{meta_NGC_TYDSP, ".tydsp Header"},
|
||||
{meta_XBOX_WVS, "Metal Arms WVS Header (XBOX)"},
|
||||
{meta_NGC_WVS, "Metal Arms WVS Header (GameCube)"},
|
||||
{meta_XBOX_MATX, "assumed Matrix file by .matx extension"},
|
||||
|
@ -1,7 +1,7 @@
|
||||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
|
||||
typedef enum { ATRAC3, ATRAC9, KOVS, KTSS } atsl_codec;
|
||||
typedef enum { ATRAC3, ATRAC9, KOVS, KTSS, KTAC } atsl_codec;
|
||||
|
||||
/* .ATSL - Koei Tecmo audio container [One Piece Pirate Warriors (PS3), Warriors All-Stars (PC)] */
|
||||
VGMSTREAM * init_vgmstream_atsl(STREAMFILE *streamFile) {
|
||||
@ -12,7 +12,7 @@ VGMSTREAM * init_vgmstream_atsl(STREAMFILE *streamFile) {
|
||||
atsl_codec codec;
|
||||
const char* fake_ext;
|
||||
off_t subfile_offset = 0;
|
||||
size_t subfile_size = 0, header_size;
|
||||
size_t subfile_size = 0, header_size, entry_size;
|
||||
int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL;
|
||||
|
||||
|
||||
@ -39,24 +39,31 @@ VGMSTREAM * init_vgmstream_atsl(STREAMFILE *streamFile) {
|
||||
* - 00060301 00010301 atsl in G1L from One Piece Pirate Warriors 3 (PC)[KOVS]
|
||||
* - 000A0301 00010501 atsl in G1L from Warriors All-Stars (PC)[KOVS]
|
||||
* - 000B0301 00080601 atsl in G1l from Sengoku Musou Sanada Maru (Switch)[KTSS]
|
||||
* - 010C0301 01060601 .atsl from Dynasty Warriors 9 (PS4)[KTAC]
|
||||
*/
|
||||
type = read_8bit(0x0d, streamFile);
|
||||
entry_size = 0x28;
|
||||
type = read_16bitLE(0x0c, streamFile);
|
||||
switch(type) {
|
||||
case 0x01:
|
||||
case 0x0100:
|
||||
codec = KOVS;
|
||||
fake_ext = "kvs";
|
||||
break;
|
||||
case 0x02:
|
||||
case 0x0200:
|
||||
codec = ATRAC3;
|
||||
fake_ext = "at3";
|
||||
big_endian = 1;
|
||||
break;
|
||||
case 0x04:
|
||||
case 0x06:
|
||||
case 0x0400:
|
||||
case 0x0600:
|
||||
codec = ATRAC9;
|
||||
fake_ext = "at9";
|
||||
break;
|
||||
case 0x08:
|
||||
case 0x0601:
|
||||
codec = KTAC;
|
||||
fake_ext = "ktac";
|
||||
entry_size = 0x3c;
|
||||
break;
|
||||
case 0x0800:
|
||||
codec = KTSS;
|
||||
fake_ext = "ktss";
|
||||
break;
|
||||
@ -78,14 +85,15 @@ VGMSTREAM * init_vgmstream_atsl(STREAMFILE *streamFile) {
|
||||
for (i = 0; i < entries; i++) {
|
||||
int is_unique = 1;
|
||||
|
||||
off_t entry_offset = read_32bit(header_size + i*0x28 + 0x04,streamFile);
|
||||
size_t entry_size = read_32bit(header_size + i*0x28 + 0x08,streamFile);
|
||||
/* 0x00: id?, 0x08+: sample rate/num_samples/loop_start/etc, matching subfile header */
|
||||
/* 0x00: id */
|
||||
off_t entry_subfile_offset = read_32bit(header_size + i*entry_size + 0x04,streamFile);
|
||||
size_t entry_subfile_size = read_32bit(header_size + i*entry_size + 0x08,streamFile);
|
||||
/* 0x08+: channels/sample rate/num_samples/loop_start/etc (match subfile header) */
|
||||
|
||||
/* check if current entry was repeated in a prev entry */
|
||||
for (j = 0; j < i; j++) {
|
||||
off_t prev_offset = read_32bit(header_size + j*0x28 + 0x04,streamFile);
|
||||
if (prev_offset == entry_offset) {
|
||||
off_t prev_offset = read_32bit(header_size + j*entry_size + 0x04,streamFile);
|
||||
if (prev_offset == entry_subfile_offset) {
|
||||
is_unique = 0;
|
||||
break;
|
||||
}
|
||||
@ -97,8 +105,8 @@ VGMSTREAM * init_vgmstream_atsl(STREAMFILE *streamFile) {
|
||||
|
||||
/* target GET, but keep going to count subsongs */
|
||||
if (!subfile_offset && target_subsong == total_subsongs) {
|
||||
subfile_offset = entry_offset;
|
||||
subfile_size = entry_size;
|
||||
subfile_offset = entry_subfile_offset;
|
||||
subfile_size = entry_subfile_size;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -129,6 +137,10 @@ VGMSTREAM * init_vgmstream_atsl(STREAMFILE *streamFile) {
|
||||
vgmstream = init_vgmstream_ktss(temp_streamFile);
|
||||
if (!vgmstream) goto fail;
|
||||
break;
|
||||
case KTAC:
|
||||
//vgmstream = init_vgmstream_ktac(temp_streamFile); //Koei Tecto VBR-like ATRAC9
|
||||
//if (!vgmstream) goto fail;
|
||||
//break;
|
||||
default:
|
||||
goto fail;
|
||||
}
|
||||
|
@ -17,27 +17,33 @@ typedef struct {
|
||||
int32_t loop_start;
|
||||
int32_t loop_end;
|
||||
int32_t loop_start_offset;
|
||||
int32_t data_offset;
|
||||
|
||||
int big_endian;
|
||||
int loop_flag;
|
||||
int is_sead;
|
||||
int codec_config;
|
||||
int is_bank;
|
||||
int total_subsongs;
|
||||
} ea_header;
|
||||
|
||||
static int parse_header(STREAMFILE* streamFile, ea_header* ea, off_t begin_offset);
|
||||
static VGMSTREAM * init_vgmstream_main(STREAMFILE *streamFile, ea_header* ea);
|
||||
|
||||
static void set_ea_1snh_num_samples(STREAMFILE* streamFile, off_t start_offset, ea_header* ea);
|
||||
static int get_ea_1snh_ima_version(STREAMFILE* streamFile, off_t start_offset, const ea_header* ea);
|
||||
|
||||
/* EA 1SNh - from early EA games (~1996, ex. Need for Speed) */
|
||||
/* EA 1SNh - from early EA games, stream (~1996, ex. Need for Speed) */
|
||||
VGMSTREAM * init_vgmstream_ea_1snh(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
off_t start_offset;
|
||||
ea_header ea = {0};
|
||||
off_t eacs_offset;
|
||||
|
||||
|
||||
/* checks */
|
||||
/* .asf/as4: common, cnk: some PS games, .sng: fake for plugins (to mimic EA SCHl's common extension) */
|
||||
/* .uv, .tgq: some SAT games */
|
||||
/* .asf/as4: common
|
||||
* .cnk: some PS games
|
||||
* .sng: fake for plugins (to mimic EA SCHl's common extension)
|
||||
* .uv/tgq: some SAT games (video only?) */
|
||||
if (!check_extensions(streamFile,"asf,as4,cnk,sng,uv,tgq"))
|
||||
goto fail;
|
||||
|
||||
@ -47,45 +53,117 @@ VGMSTREAM * init_vgmstream_ea_1snh(STREAMFILE *streamFile) {
|
||||
|
||||
/* stream is divided into blocks/chunks: 1SNh=audio header, 1SNd=data xN, 1SNl=loop end, 1SNe=end.
|
||||
* Video uses various blocks (kVGT/fVGT/etc) and sometimes alt audio blocks (SEAD/SNDC/SEND). */
|
||||
|
||||
ea.is_sead = read_32bitBE(0x00,streamFile) == 0x53454144;
|
||||
|
||||
/* use block size as endian marker (Saturn = BE) */
|
||||
ea.big_endian = !(read_32bitLE(0x04,streamFile) < 0x0000FFFF);
|
||||
ea.big_endian = guess_endianness32bit(0x04,streamFile);
|
||||
|
||||
if (!parse_header(streamFile,&ea, 0x08))
|
||||
eacs_offset = 0x08; /* after 1SNh block id+size */
|
||||
|
||||
if (!parse_header(streamFile,&ea, eacs_offset))
|
||||
goto fail;
|
||||
return init_vgmstream_main(streamFile, &ea);
|
||||
|
||||
fail:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* EA EACS - from early EA games, bank (~1996, ex. Need for Speed) */
|
||||
VGMSTREAM * init_vgmstream_ea_eacs(STREAMFILE *streamFile) {
|
||||
ea_header ea = {0};
|
||||
off_t eacs_offset;
|
||||
|
||||
|
||||
/* checks */
|
||||
/* .eas: single bank [Need for Speed (PC)]
|
||||
* .bnk: multi bank [Need for Speed (PC)] */
|
||||
if (!check_extensions(streamFile,"eas,bnk"))
|
||||
goto fail;
|
||||
|
||||
start_offset = 0x00;
|
||||
/* plain data without blocks, can contain N*(EACS header) + N*(data), or N (EACS header + data) */
|
||||
ea.is_bank = 1;
|
||||
|
||||
/* use ??? as endian marker (Saturn = BE) */
|
||||
//ea.big_endian = guess_endianness32bit(0x04,streamFile);
|
||||
|
||||
if (read_32bitBE(0x00,streamFile) == 0x45414353) { /* "EACS" */
|
||||
/* single bank variant */
|
||||
eacs_offset = 0x00;
|
||||
}
|
||||
else if (read_32bitBE(0x00,streamFile) == 0x00) {
|
||||
/* multi bank variant */
|
||||
int i;
|
||||
int target_subsong = streamFile->stream_index;
|
||||
|
||||
if (target_subsong == 0) target_subsong = 1;
|
||||
if (target_subsong < 0) goto fail;
|
||||
|
||||
/* offsets to EACSs are scattered in the first 0x200
|
||||
* this looks dumb but seems like the only way */
|
||||
eacs_offset = 0;
|
||||
for (i = 0x00; i < 0x200; i += 0x04) {
|
||||
off_t bank_offset = read_32bitLE(i, streamFile);
|
||||
if (bank_offset == 0)
|
||||
continue;
|
||||
|
||||
ea.total_subsongs++;
|
||||
|
||||
/* parse mini bank header */
|
||||
if (ea.total_subsongs == target_subsong) {
|
||||
/* 0x00: some id or flags? */
|
||||
eacs_offset = read_32bitLE(bank_offset + 0x04, streamFile);
|
||||
/* rest: not sure if part of this header */
|
||||
}
|
||||
}
|
||||
|
||||
if (eacs_offset == 0)
|
||||
goto fail;
|
||||
}
|
||||
else {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!parse_header(streamFile,&ea, eacs_offset))
|
||||
goto fail;
|
||||
return init_vgmstream_main(streamFile, &ea);
|
||||
|
||||
fail:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static VGMSTREAM * init_vgmstream_main(STREAMFILE *streamFile, ea_header* ea) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(ea.channels, ea.loop_flag);
|
||||
vgmstream = allocate_vgmstream(ea->channels, ea->loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->sample_rate = ea.sample_rate;
|
||||
vgmstream->num_samples = ea.num_samples;
|
||||
vgmstream->loop_start_sample = ea.loop_start;
|
||||
vgmstream->loop_end_sample = ea.loop_end;
|
||||
vgmstream->sample_rate = ea->sample_rate;
|
||||
vgmstream->num_samples = ea->num_samples;
|
||||
vgmstream->loop_start_sample = ea->loop_start;
|
||||
vgmstream->loop_end_sample = ea->loop_end;
|
||||
|
||||
vgmstream->codec_endian = ea.big_endian;
|
||||
vgmstream->layout_type = layout_blocked_ea_1snh;
|
||||
vgmstream->meta_type = meta_EA_1SNH;
|
||||
vgmstream->codec_endian = ea->big_endian;
|
||||
vgmstream->layout_type = ea->is_bank ? layout_none : layout_blocked_ea_1snh;
|
||||
vgmstream->meta_type = ea->is_bank ? meta_EA_EACS : meta_EA_1SNH;
|
||||
vgmstream->num_streams = ea->total_subsongs;
|
||||
|
||||
switch (ea.codec) {
|
||||
switch (ea->codec) {
|
||||
case EA_CODEC_PCM: /* Need for Speed (PC) */
|
||||
vgmstream->coding_type = ea.bits==1 ? coding_PCM8_int : coding_PCM16_int;
|
||||
vgmstream->coding_type = ea->bits==1 ? coding_PCM8_int : coding_PCM16_int;
|
||||
break;
|
||||
|
||||
case EA_CODEC_ULAW: /* Crusader: No Remorse movies (SAT), FIFA 96 movies (SAT) */
|
||||
if (ea.bits && ea.bits!=2) goto fail; /* only set in EACS */
|
||||
if (ea->bits && ea->bits != 2) goto fail; /* only set in EACS */
|
||||
vgmstream->coding_type = coding_ULAW_int;
|
||||
break;
|
||||
|
||||
case EA_CODEC_IMA: /* Need for Speed II (PC) */
|
||||
if (ea.bits && ea.bits!=2) goto fail; /* only in EACS */
|
||||
if (ea->bits && ea->bits != 2) goto fail; /* only in EACS */
|
||||
vgmstream->coding_type = coding_DVI_IMA; /* stereo/mono, high nibble first */
|
||||
vgmstream->codec_config = ea.codec_config;
|
||||
vgmstream->codec_config = ea->codec_config;
|
||||
break;
|
||||
|
||||
case EA_CODEC_PSX: /* Need for Speed (PS) */
|
||||
@ -93,12 +171,12 @@ VGMSTREAM * init_vgmstream_ea_1snh(STREAMFILE *streamFile) {
|
||||
break;
|
||||
|
||||
default:
|
||||
VGM_LOG("EA: unknown codec 0x%02x\n", ea.codec);
|
||||
VGM_LOG("EA EACS: unknown codec 0x%02x\n", ea->codec);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
|
||||
if (!vgmstream_open_stream(vgmstream,streamFile,ea->data_offset))
|
||||
goto fail;
|
||||
return vgmstream;
|
||||
|
||||
@ -116,15 +194,18 @@ static int parse_header(STREAMFILE* streamFile, ea_header* ea, off_t offset) {
|
||||
ea->bits = read_8bit(offset+0x08, streamFile);
|
||||
ea->channels = read_8bit(offset+0x09, streamFile);
|
||||
ea->codec = read_8bit(offset+0x0a, streamFile);
|
||||
ea->type = read_8bit(offset+0x0b, streamFile);
|
||||
ea->type = read_8bit(offset+0x0b, streamFile); /* block type? 0=1SNh, -1=bank */
|
||||
ea->num_samples = read_32bit(offset+0x0c, streamFile);
|
||||
ea->loop_start = read_32bit(offset+0x10, streamFile);
|
||||
ea->loop_end = read_32bit(offset+0x14, streamFile) + ea->loop_start; /* loop length */
|
||||
/* 0x18: data start? (0x00), 0x1c: pan/volume/etc? (0x7F), rest can be padding/garbage */
|
||||
VGM_ASSERT(ea->type != 0, "EA EACS: unknown type\n"); /* block type? */
|
||||
ea->data_offset = read_32bit(offset+0x18, streamFile); /* 0 when blocked */
|
||||
/* 0x1c: pan/volume/etc? (0x7F)
|
||||
* rest may be padding/garbage */
|
||||
//VGM_ASSERT(ea->type != 0, "EA EACS: unknown type %i\n", ea->type);
|
||||
|
||||
if (ea->codec == EA_CODEC_IMA)
|
||||
ea->codec_config = get_ea_1snh_ima_version(streamFile, 0x00, ea);
|
||||
/* EACS banks with empty values exist but will be rejected later */
|
||||
}
|
||||
else if (ea->is_sead) {
|
||||
/* alt subheader (found in some PC videos) */
|
||||
|
@ -5,24 +5,24 @@
|
||||
|
||||
/* 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 // ~PS era
|
||||
#define EA_VERSION_V3 0x03 // ~PS2 era
|
||||
#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 (unasigned values seem internal only) */
|
||||
#define EA_PLATFORM_GENERIC -1 // typically Wii/X360/PS3/videos
|
||||
/* platform constants (unassigned values seem internal only) */
|
||||
#define EA_PLATFORM_GENERIC -1 /* typically Wii/X360/PS3/videos */
|
||||
#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_WII 0x06 // reused later for Wii
|
||||
#define EA_PLATFORM_GC_WII 0x06
|
||||
#define EA_PLATFORM_XBOX 0x07
|
||||
#define EA_PLATFORM_X360 0x09 // also "Xenon"
|
||||
#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_PS3 0x0E /* very rare [Need for Speed: Carbon (PS3)] */
|
||||
#define EA_PLATFORM_3DS 0x14
|
||||
|
||||
/* codec constants (undefined are probably reserved, ie.- sx.exe encodes PCM24/DVI but no platform decodes them) */
|
||||
@ -112,9 +112,24 @@ static void update_ea_stream_size_and_samples(STREAMFILE* streamFile, off_t star
|
||||
|
||||
/* EA SCHl with variable header - from EA games (roughly 1997~2010); generated by EA Canada's sx.exe/Sound eXchange */
|
||||
VGMSTREAM * init_vgmstream_ea_schl(STREAMFILE *streamFile) {
|
||||
/* check extension; exts don't seem enforced by EA's tools, but usually:
|
||||
* STR/ASF/MUS ~early, EAM ~mid, SNG/AUD ~late, rest uncommon/one game (ex. STRM: MySims Kingdom Wii) */
|
||||
if (!check_extensions(streamFile,"str,asf,mus,eam,sng,aud,sx,strm,xa,xsf,exa,stm,trj,trm"))
|
||||
|
||||
/* check extension */
|
||||
/* they don't seem enforced by EA's tools but usually:
|
||||
* .asf: ~early [ex. Need for Speed (PC)]
|
||||
* .str: ~early [ex. FIFA 2002 (PS1)]
|
||||
* .eam: ~mid (fake?)
|
||||
* .exa: ~mid [ex. 007 - From Russia with Love]
|
||||
* .sng: ~late (fake?)
|
||||
* .aud: ~late [ex. FIFA 14 (3DS)]
|
||||
* .strm: MySims Kingdom (Wii)
|
||||
* .stm: FIFA 12 (3DS)
|
||||
* .sx/xa: fake?
|
||||
* .hab: GoldenEye - Rogue Agent (inside .big)
|
||||
* .xsf: 007 - Agent Under Fire (Xbox)
|
||||
* .gsf: 007 - Everything or Nothing (GC)
|
||||
* .mus: map/mpf+mus only?
|
||||
* (extensionless): SSX (PS2) (inside .big) */
|
||||
if (!check_extensions(streamFile,"asf,str,eam,exa,sng,aud,sx,xa,strm,stm,hab,xsf,gsf,mus,"))
|
||||
goto fail;
|
||||
|
||||
/* check header */
|
||||
@ -147,8 +162,12 @@ VGMSTREAM * init_vgmstream_ea_bnk(STREAMFILE *streamFile) {
|
||||
off_t offset;
|
||||
|
||||
/* check extension */
|
||||
/* .bnk: sfx, .sdt: speech, .mus: streams/jingles (rare) */
|
||||
if (!check_extensions(streamFile,"bnk,sdt,mus"))
|
||||
/* .bnk: common
|
||||
* .sdt: Harry Potter games
|
||||
* .mus: streams/jingles (rare)
|
||||
* .abk: GoldenEye - Rogue Agent
|
||||
* .ast: FIFA 2004 (inside .big) */
|
||||
if (!check_extensions(streamFile,"bnk,sdt,mus,abk,ast"))
|
||||
goto fail;
|
||||
|
||||
/* check header (doesn't use EA blocks, otherwise very similar to SCHl) */
|
||||
@ -363,7 +382,59 @@ fail:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* EA MAP/MUS combo - used in some old games for interactive music info */
|
||||
|
||||
/* open map/mpf+mus pairs that aren't exact pairs, since EA's games can load any combo */
|
||||
static STREAMFILE * open_mapfile_pair(STREAMFILE *streamFile) {
|
||||
static const char *const mapfile_pairs[][2] = {
|
||||
/* standard cases, replace map part with mus part (from the end to preserve prefixes) */
|
||||
{"MUS_CTRL.MPF","MUS_STR.MUS"}, /* GoldenEye - Rogue Agent (PS2) */
|
||||
{"mus_ctrl.mpf","mus_str.mus"}, /* GoldenEye - Rogue Agent (others) */
|
||||
{".mpf","_main.mus"}, /* 007 - Everything or Nothing (GC) */
|
||||
/* hack when when multiple maps point to the same mus, uses name before "+"
|
||||
* ex. ZZZTR00A.TRJ+ZTR00PGR.MAP or ZZZTR00A.TRJ+ZTR00R0A.MAP both point to ZZZTR00A.TRJ */
|
||||
{"+",""}, /* Need for Speed III (PS1) */
|
||||
};
|
||||
STREAMFILE *musFile = NULL;
|
||||
char file_name[PATH_LIMIT];
|
||||
int pair_count = (sizeof(mapfile_pairs)/sizeof(mapfile_pairs[0]));
|
||||
int i;
|
||||
size_t file_len, map_len;
|
||||
|
||||
get_streamfile_filename(streamFile, file_name, PATH_LIMIT);
|
||||
file_len = strlen(file_name);
|
||||
|
||||
for (i = 0; i < pair_count; i++) {
|
||||
const char *map_name = mapfile_pairs[i][0];
|
||||
const char *mus_name = mapfile_pairs[i][1];
|
||||
map_len = strlen(map_name);
|
||||
|
||||
if (map_name[0] == '+') {
|
||||
/* use name before "+" */
|
||||
char *mod_name = strchr(file_name, '+');
|
||||
if (mod_name == NULL)
|
||||
continue;
|
||||
mod_name[0] = '\0';
|
||||
}
|
||||
else {
|
||||
/* replace map_name with expected mus_name */
|
||||
if (file_len < map_len)
|
||||
continue;
|
||||
if (strncmp(file_name+(file_len - map_len), map_name, map_len) != 0)
|
||||
continue;
|
||||
file_name[file_len - map_len] = '\0';
|
||||
strcat(file_name, mus_name);
|
||||
}
|
||||
|
||||
musFile = open_streamfile_by_filename(streamFile, file_name);
|
||||
if (musFile) return musFile;
|
||||
|
||||
get_streamfile_filename(streamFile, file_name, PATH_LIMIT); /* reset for next loop */
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* EA MAP/MUS combo - used in older games for interactive music (for EA's PathFinder tool) */
|
||||
VGMSTREAM * init_vgmstream_ea_map_mus(STREAMFILE *streamFile) {
|
||||
uint8_t version, num_sounds, num_userdata, userdata_size;
|
||||
off_t section_offset, schl_offset;
|
||||
@ -383,7 +454,10 @@ VGMSTREAM * init_vgmstream_ea_map_mus(STREAMFILE *streamFile) {
|
||||
if (version > 1) goto fail;
|
||||
|
||||
musFile = open_streamfile_by_ext(streamFile, "mus");
|
||||
if (!musFile) goto fail;
|
||||
if (!musFile) {
|
||||
musFile = open_mapfile_pair(streamFile);
|
||||
if (!musFile) goto fail;
|
||||
}
|
||||
|
||||
/*
|
||||
* 0x04: ???
|
||||
@ -427,7 +501,7 @@ fail:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* EA MPF/MUS combo - used in newer 6th gen games for storing music */
|
||||
/* EA MPF/MUS combo - used in newer 6th gen games for interactive music (for EA's PathFinder tool) */
|
||||
VGMSTREAM * init_vgmstream_ea_mpf_mus(STREAMFILE *streamFile) {
|
||||
off_t section_offset, entry_offset, subentry_num, eof_offset, off_mult, schl_offset;
|
||||
size_t sec2_size;
|
||||
@ -463,12 +537,15 @@ VGMSTREAM * init_vgmstream_ea_mpf_mus(STREAMFILE *streamFile) {
|
||||
if (version == 5 && sub_version > 2) goto fail; /* newer version using SNR/SNS */
|
||||
|
||||
musFile = open_streamfile_by_ext(streamFile, "mus");
|
||||
if (!musFile) goto fail;
|
||||
if (!musFile) {
|
||||
musFile = open_mapfile_pair(streamFile);
|
||||
if (!musFile) goto fail;
|
||||
}
|
||||
|
||||
/* HACK: number of sub-entries is stored in bitstreams that are different in LE and BE */
|
||||
/* I can't figure it out, so let's just use a workaround for now */
|
||||
|
||||
if (version == 3 && sub_version == 1) { /* SSX Tricky /*
|
||||
if (version == 3 && sub_version == 1) { /* SSX Tricky */
|
||||
/* we need to go through the first two sections to find sound table */
|
||||
sec1_num = read_16bit(0x12, streamFile);
|
||||
sec2_size = read_8bit(0x0e, streamFile);
|
||||
@ -517,7 +594,7 @@ VGMSTREAM * init_vgmstream_ea_mpf_mus(STREAMFILE *streamFile) {
|
||||
eof_offset = read_32bit(entry_offset + 0x04, streamFile) * 0x04;
|
||||
total_streams = (eof_offset - section_offset) / 0x08;
|
||||
off_mult = 0x04;
|
||||
} else if (version == 4) { /* SSX 3, Need for Speed: Underground 2 /*
|
||||
} else if (version == 4) { /* SSX 3, Need for Speed: Underground 2 */
|
||||
/* we need to go through the first two sections to find sound table */
|
||||
sec1_num = read_16bit(0x12, streamFile);
|
||||
sec2_num = read_8bit(0x0f, streamFile);
|
||||
|
@ -174,6 +174,7 @@ VGMSTREAM * init_vgmstream_pos(STREAMFILE * streamFile);
|
||||
VGMSTREAM * init_vgmstream_nwa(STREAMFILE * streamFile);
|
||||
|
||||
VGMSTREAM * init_vgmstream_ea_1snh(STREAMFILE * streamFile);
|
||||
VGMSTREAM * init_vgmstream_ea_eacs(STREAMFILE * streamFile);
|
||||
|
||||
VGMSTREAM * init_vgmstream_xss(STREAMFILE * streamFile);
|
||||
|
||||
|
@ -274,8 +274,9 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) {
|
||||
* .xss: Spider-Man The Movie (Xbox)
|
||||
* .xsew: Mega Man X Legacy Collections (PC)
|
||||
* .adpcm: Angry Birds Transformers (Android)
|
||||
* .adw: Dead Rising 2 (PC) */
|
||||
if ( check_extensions(streamFile, "wav,lwav,xwav,da,dax,cd,med,snd,adx,adp,xss,xsew,adpcm,adw") ) {
|
||||
* .adw: Dead Rising 2 (PC)
|
||||
* .wd: Genma Onimusha (Xbox) voices */
|
||||
if ( check_extensions(streamFile, "wav,lwav,xwav,da,dax,cd,med,snd,adx,adp,xss,xsew,adpcm,adw,wd") ) {
|
||||
;
|
||||
}
|
||||
else if ( check_extensions(streamFile, "mwv") ) {
|
||||
@ -312,13 +313,13 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) {
|
||||
/* some Dreamcast/Naomi games do this [Headhunter (DC), Bomber hehhe (DC)] */
|
||||
if (riff_size + 0x04 == file_size && read_16bitLE(0x14,streamFile)==0x0000)
|
||||
riff_size -= 0x04;
|
||||
/* some PC games do this [Halo 2 (PC)] */
|
||||
/* some PC games do this [Halo 2 (PC)] (possibly bad extractor? 'Gravemind Tool') */
|
||||
if (riff_size + 0x04 == file_size && read_16bitLE(0x14,streamFile)==0x0069)
|
||||
riff_size -= 0x04;
|
||||
|
||||
|
||||
/* check for truncated RIFF */
|
||||
if (file_size < riff_size+0x08) goto fail;
|
||||
if (file_size < riff_size+0x08)
|
||||
goto fail;
|
||||
|
||||
/* read through chunks to verify format and find metadata */
|
||||
{
|
||||
|
@ -1,80 +1,58 @@
|
||||
#include "meta.h"
|
||||
#include "../util.h"
|
||||
#include "../coding/coding.h"
|
||||
|
||||
/* SDT (Baldur's Gate - Dark Alliance) */
|
||||
/* .sdt - from High Voltage games? [Baldur's Gate - Dark Alliance (GC)] */
|
||||
VGMSTREAM * init_vgmstream_sdt(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
char filename[PATH_LIMIT];
|
||||
off_t start_offset;
|
||||
size_t data_size;
|
||||
int loop_flag, channel_count;
|
||||
|
||||
int loop_flag;
|
||||
int channel_count;
|
||||
|
||||
/* check extension, case insensitive */
|
||||
streamFile->get_name(streamFile,filename,sizeof(filename));
|
||||
if (strcasecmp("sdt",filename_extension(filename))) goto fail;
|
||||
|
||||
#if 0
|
||||
/* check header */
|
||||
if (read_32bitBE(0x00,streamFile) != 0x53565300) /* "SVS\0" */
|
||||
/* checks */
|
||||
if (!check_extensions(streamFile, "sdt"))
|
||||
goto fail;
|
||||
#endif
|
||||
|
||||
loop_flag = (read_32bitBE(0x04,streamFile)!=0);
|
||||
channel_count = 2;
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
channel_count = read_32bitBE(0x00,streamFile); /* assumed */
|
||||
loop_flag = (read_32bitBE(0x04,streamFile) != 0);
|
||||
start_offset = 0xA0;
|
||||
data_size = get_streamfile_size(streamFile) - start_offset;
|
||||
|
||||
if (channel_count != 2)
|
||||
goto fail; /* only seen this */
|
||||
if (read_32bitBE(0x08,streamFile) != read_32bitBE(0x08,streamFile))
|
||||
goto fail; /* sample rate agreement between channels */
|
||||
if (read_32bitBE(0x98,streamFile) != 0x8000)
|
||||
goto fail; /* expected interleave */
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channel_count,loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
/* fill in the vital statistics */
|
||||
start_offset = 0xA0;
|
||||
vgmstream->channels = channel_count; /*read_32bitBE(0x00,streamFile);*/
|
||||
vgmstream->meta_type = meta_SDT;
|
||||
vgmstream->sample_rate = read_32bitBE(0x08,streamFile);
|
||||
vgmstream->coding_type = coding_NGC_DSP;
|
||||
vgmstream->num_samples = read_32bitBE(0x14,streamFile)/8*14/channel_count;
|
||||
vgmstream->num_samples = dsp_bytes_to_samples(data_size, channel_count); /* maybe at @0x14 - 2? */
|
||||
if (loop_flag) {
|
||||
vgmstream->loop_start_sample = 0; /* (read_32bitLE(0x08,streamFile)-1)*28; */
|
||||
vgmstream->loop_end_sample = read_32bitBE(0x14,streamFile)/8*14/channel_count;
|
||||
vgmstream->loop_start_sample = 0; /* maybe @0x08? */
|
||||
vgmstream->loop_end_sample = vgmstream->num_samples; /* maybe @0x10? */
|
||||
}
|
||||
vgmstream->coding_type = coding_NGC_DSP;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = 0x8000;
|
||||
if (vgmstream->interleave_block_size)
|
||||
vgmstream->interleave_last_block_size =
|
||||
(data_size % (vgmstream->interleave_block_size*vgmstream->channels)) / vgmstream->channels;
|
||||
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = 0x8000;
|
||||
vgmstream->meta_type = meta_SDT;
|
||||
dsp_read_coefs_be(vgmstream,streamFile, 0x3c,0x2E);
|
||||
dsp_read_hist_be(vgmstream, streamFile, 0x60,0x2E);
|
||||
|
||||
|
||||
if (vgmstream->coding_type == coding_NGC_DSP) {
|
||||
int i;
|
||||
for (i=0;i<16;i++) {
|
||||
vgmstream->ch[0].adpcm_coef[i] = read_16bitBE(0x3C+i*2,streamFile);
|
||||
}
|
||||
if (vgmstream->channels) {
|
||||
for (i=0;i<16;i++) {
|
||||
vgmstream->ch[1].adpcm_coef[i] = read_16bitBE(0x6A+i*2,streamFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 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;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
|
||||
goto fail;
|
||||
return vgmstream;
|
||||
|
||||
/* clean up anything we may have opened */
|
||||
fail:
|
||||
if (vgmstream) close_vgmstream(vgmstream);
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
|
@ -265,6 +265,8 @@ static int parse_pk_header(ubi_bao_header * bao, STREAMFILE *streamFile) {
|
||||
size_t index_size, index_header_size;
|
||||
off_t bao_offset, resources_offset;
|
||||
int target_subsong = streamFile->stream_index;
|
||||
uint8_t *index_buffer = NULL;
|
||||
STREAMFILE *streamTest = NULL;
|
||||
|
||||
|
||||
/* class: 0x01=index, 0x02=BAO */
|
||||
@ -273,7 +275,7 @@ static int parse_pk_header(ubi_bao_header * bao, STREAMFILE *streamFile) {
|
||||
/* index and resources always LE */
|
||||
|
||||
/* 0x01(3): version, major/minor/release (numbering continues from .sb0/sm0) */
|
||||
index_size = read_32bitLE(0x04, streamFile); /* can be 0 */
|
||||
index_size = read_32bitLE(0x04, streamFile); /* can be 0, not including */
|
||||
resources_offset = read_32bitLE(0x08, streamFile); /* always found even if not used */
|
||||
/* 0x0c: always 0? */
|
||||
/* 0x10: unknown, null if no entries */
|
||||
@ -287,14 +289,26 @@ static int parse_pk_header(ubi_bao_header * bao, STREAMFILE *streamFile) {
|
||||
index_entries = index_size / 0x08;
|
||||
index_header_size = 0x40;
|
||||
|
||||
/* parse index to get target subsong N = Nth header BAO */
|
||||
/* pre-load to avoid too much I/O back and forth */
|
||||
if (index_size > (10000*0x08)) {
|
||||
VGM_LOG("BAO: index too big\n");
|
||||
goto fail;
|
||||
}
|
||||
index_buffer = malloc(index_size);
|
||||
read_streamfile(index_buffer, index_header_size, index_size, streamFile);
|
||||
|
||||
/* use smaller I/O buffer for performance, as this read lots of small BAO headers all over the place */
|
||||
streamTest = reopen_streamfile(streamFile, 0x100);
|
||||
if (!streamTest) goto fail;
|
||||
|
||||
/* parse index to get target subsong N = Nth audio header BAO */
|
||||
bao_offset = index_header_size + index_size;
|
||||
for (i = 0; i < index_entries; i++) {
|
||||
//uint32_t bao_id = read_32bitLE(index_header_size+0x08*i+0x00, streamFile);
|
||||
size_t bao_size = read_32bitLE(index_header_size+0x08*i+0x04, streamFile);
|
||||
//uint32_t bao_id = get_32bitLE(index_buffer + 0x08*i+ 0x00);
|
||||
size_t bao_size = get_32bitLE(index_buffer + 0x08*i + 0x04);
|
||||
|
||||
/* parse and continue to find out total_subsongs */
|
||||
if (!parse_bao(bao, streamFile, bao_offset))
|
||||
if (!parse_bao(bao, streamTest, bao_offset))
|
||||
goto fail;
|
||||
|
||||
bao_offset += bao_size; /* files simply concat BAOs */
|
||||
@ -393,8 +407,12 @@ static int parse_pk_header(ubi_bao_header * bao, STREAMFILE *streamFile) {
|
||||
|
||||
;VGM_LOG("BAO stream: id=%x, offset=%x, size=%x, res=%s\n", bao->stream_id, (uint32_t)bao->stream_offset, bao->stream_size, (bao->is_external ? bao->resource_name : "internal"));
|
||||
|
||||
free(index_buffer);
|
||||
close_streamfile(streamTest);
|
||||
return 1;
|
||||
fail:
|
||||
free(index_buffer);
|
||||
close_streamfile(streamTest);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -407,14 +425,14 @@ static int parse_bao(ubi_bao_header * bao, STREAMFILE *streamFile, off_t offset)
|
||||
|
||||
|
||||
/* 0x00(1): class? usually 0x02 but older BAOs have 0x01 too */
|
||||
bao_version = read_32bitBE(offset + 0x00, streamFile) & 0x00FFFFFF;
|
||||
bao_version = read_32bitBE(offset+0x00, streamFile) & 0x00FFFFFF;
|
||||
|
||||
/* detect endianness */
|
||||
if (read_32bitLE(offset+0x04, streamFile) < 0x0000FFFF) {
|
||||
read_32bit = read_32bitLE;
|
||||
} else {
|
||||
/* this could be done once as all BAOs share endianness */
|
||||
if (guess_endianness32bit(offset+0x04, streamFile)) {
|
||||
read_32bit = read_32bitBE;
|
||||
bao->big_endian = 1;
|
||||
} else {
|
||||
read_32bit = read_32bitLE;
|
||||
}
|
||||
|
||||
header_size = read_32bit(offset+0x04, streamFile); /* mainly 0x28, rarely 0x24 */
|
||||
|
@ -82,7 +82,7 @@ typedef struct {
|
||||
int header_idx;
|
||||
off_t header_offset;
|
||||
|
||||
|
||||
int subtypes[16];
|
||||
} ubi_sb_header;
|
||||
|
||||
static VGMSTREAM * init_vgmstream_ubi_sb_main(ubi_sb_header *sb, STREAMFILE *streamFile);
|
||||
@ -91,8 +91,9 @@ static int config_sb_header_version(ubi_sb_header * sb, STREAMFILE *streamFile);
|
||||
|
||||
/* .SBx - banks from Ubisoft's sound engine ("DARE" / "UbiSound Driver") games in ~2000-2008 */
|
||||
VGMSTREAM * init_vgmstream_ubi_sb(STREAMFILE *streamFile) {
|
||||
STREAMFILE *streamTest = NULL;
|
||||
int32_t(*read_32bit)(off_t, STREAMFILE*) = NULL;
|
||||
int16_t(*read_16bit)(off_t, STREAMFILE*) = NULL;
|
||||
//int16_t(*read_16bit)(off_t, STREAMFILE*) = NULL;
|
||||
ubi_sb_header sb = { 0 };
|
||||
int ok;
|
||||
int target_stream = streamFile->stream_index;
|
||||
@ -152,10 +153,10 @@ VGMSTREAM * init_vgmstream_ubi_sb(STREAMFILE *streamFile) {
|
||||
sb.platform == UBI_WII);
|
||||
if (sb.big_endian) {
|
||||
read_32bit = read_32bitBE;
|
||||
read_16bit = read_16bitBE;
|
||||
//read_16bit = read_16bitBE;
|
||||
} else {
|
||||
read_32bit = read_32bitLE;
|
||||
read_16bit = read_16bitLE;
|
||||
//read_16bit = read_16bitLE;
|
||||
}
|
||||
|
||||
/* file layout is: base header, section1, section2, extra section, section3, data (all except base header can be null) */
|
||||
@ -181,20 +182,27 @@ VGMSTREAM * init_vgmstream_ubi_sb(STREAMFILE *streamFile) {
|
||||
sb.sounds_offset = sb.section3_offset + sb.section3_entry_size * sb.section3_num;
|
||||
sb.is_map = 0;
|
||||
|
||||
/* use smaller I/O buffer for performance, as this read lots of small headers all over the place */
|
||||
streamTest = reopen_streamfile(streamFile, 0x100);
|
||||
if (!streamTest) goto fail;
|
||||
|
||||
/* main parse */
|
||||
if (!parse_sb_header(&sb, streamFile, target_stream))
|
||||
if (!parse_sb_header(&sb, streamTest, target_stream))
|
||||
goto fail;
|
||||
|
||||
close_streamfile(streamTest);
|
||||
return init_vgmstream_ubi_sb_main(&sb, streamFile);
|
||||
|
||||
fail:
|
||||
close_streamfile(streamTest);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* .SMx - essentially a set of SBx files, one per map, compiled into one file */
|
||||
VGMSTREAM * init_vgmstream_ubi_sm(STREAMFILE *streamFile) {
|
||||
STREAMFILE *streamTest = NULL;
|
||||
int32_t(*read_32bit)(off_t, STREAMFILE*) = NULL;
|
||||
int16_t(*read_16bit)(off_t, STREAMFILE*) = NULL;
|
||||
//int16_t(*read_16bit)(off_t, STREAMFILE*) = NULL;
|
||||
ubi_sb_header sb = { 0 };
|
||||
size_t map_entry_size;
|
||||
int ok, i;
|
||||
@ -251,10 +259,10 @@ VGMSTREAM * init_vgmstream_ubi_sm(STREAMFILE *streamFile) {
|
||||
sb.platform == UBI_WII);
|
||||
if (sb.big_endian) {
|
||||
read_32bit = read_32bitBE;
|
||||
read_16bit = read_16bitBE;
|
||||
//read_16bit = read_16bitBE;
|
||||
} else {
|
||||
read_32bit = read_32bitLE;
|
||||
read_16bit = read_16bitLE;
|
||||
//read_16bit = read_16bitLE;
|
||||
}
|
||||
|
||||
sb.is_map = 1;
|
||||
@ -268,6 +276,10 @@ VGMSTREAM * init_vgmstream_ubi_sm(STREAMFILE *streamFile) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* use smaller I/O buffer for performance, as this read lots of small headers all over the place */
|
||||
streamTest = reopen_streamfile(streamFile, 0x100);
|
||||
if (!streamTest) goto fail;
|
||||
|
||||
map_entry_size = (sb.map_version < 2) ? 0x30 : 0x34;
|
||||
|
||||
for (i = 0; i < sb.map_num; i++) {
|
||||
@ -309,7 +321,7 @@ VGMSTREAM * init_vgmstream_ubi_sm(STREAMFILE *streamFile) {
|
||||
sb.extra_section_offset += sb.section4_offset;
|
||||
}
|
||||
|
||||
if (!parse_sb_header(&sb, streamFile, target_stream))
|
||||
if (!parse_sb_header(&sb, streamTest, target_stream))
|
||||
goto fail;
|
||||
}
|
||||
|
||||
@ -323,9 +335,11 @@ VGMSTREAM * init_vgmstream_ubi_sm(STREAMFILE *streamFile) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
close_streamfile(streamTest);
|
||||
return init_vgmstream_ubi_sb_main(&sb, streamFile);
|
||||
|
||||
fail:
|
||||
close_streamfile(streamTest);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -482,7 +496,7 @@ static VGMSTREAM * init_vgmstream_ubi_sb_main(ubi_sb_header *sb, STREAMFILE *str
|
||||
uint8_t buf[0x100];
|
||||
uint32_t sec1_num, sec2_num, sec3_num, num_frames, bits_per_frame;
|
||||
uint8_t flag;
|
||||
size_t bytes, frame_size, chunk_size, header_size, data_size;
|
||||
size_t bytes, chunk_size, header_size, data_size;
|
||||
off_t header_offset;
|
||||
|
||||
chunk_size = 0x20;
|
||||
@ -581,6 +595,180 @@ fail:
|
||||
}
|
||||
|
||||
|
||||
/* debug stuff, for now */
|
||||
static void parse_descriptor_subtype(ubi_sb_header * sb, uint32_t descriptor_subtype, off_t offset) {
|
||||
|
||||
/* type may be flags? */
|
||||
/* all types may contain memory garbage, making it harder to identify */
|
||||
|
||||
switch(descriptor_subtype) {
|
||||
/* standard audio */
|
||||
case 0x01: sb->subtypes[0x01]++; break;
|
||||
|
||||
/* audio/garbage? [Splinter Cell: Chaos Theory] */
|
||||
/* audio/garbage? [Surf's Up-map] */
|
||||
/* audio/garbage? [TMNT-map] */
|
||||
/* config? [Brothers in Arms: D-Day] */
|
||||
/* config? [Surf's Up-bank] */
|
||||
case 0x02: sb->subtypes[0x02]++; break;
|
||||
|
||||
/* audio/garbage? [Splinter Cell: Chaos Theory] */
|
||||
/* audio/garbage? [TMNT-map] */
|
||||
/* config? [Prince of Persia: The Two Thrones] */
|
||||
case 0x03: sb->subtypes[0x03]++; break;
|
||||
|
||||
/* audio/garbage? [Splinter Cell: Chaos Theory] */
|
||||
/* audio/garbage? [TMNT-map] */
|
||||
/* config? [Rainbow Six 3] */
|
||||
/* config? [Prince of Persia: Warrior Within] */
|
||||
/* config? [Prince of Persia: The Two Thrones] */
|
||||
/* config? [TMNT-bank] */
|
||||
/* config? [Surf's Up-map] */
|
||||
/* audio/garbage? [Myst IV demo] */
|
||||
/* config? [Brothers in Arms: D-Day] */
|
||||
/* config? [Surf's Up-bank] */
|
||||
case 0x04: sb->subtypes[0x04]++; break;
|
||||
|
||||
/* audio/garbage? [Splinter Cell: Chaos Theory] */
|
||||
/* audio/garbage? [TMNT-map] */
|
||||
/* config? [Prince of Persia: Warrior Within] */
|
||||
/* config? [Prince of Persia: The Two Thrones] */
|
||||
/* config? [Prince of Persia: Revelations] */
|
||||
/* config? [TMNT-bank] */
|
||||
case 0x05: sb->subtypes[0x05]++; break;
|
||||
|
||||
/* layer? [Surf's Up-map] */
|
||||
/* layer [TMNT-bank] */
|
||||
/* layer [TMNT-map] */
|
||||
case 0x06: sb->subtypes[0x06]++; break;
|
||||
|
||||
/* audio/garbage? [Splinter Cell: Chaos Theory] */
|
||||
/* config? [Brothers in Arms: D-Day] */
|
||||
/* config? [Surf's Up-bank] */
|
||||
case 0x07: sb->subtypes[0x07]++; break;
|
||||
|
||||
/* audio/garbage? [Splinter Cell: Chaos Theory] */
|
||||
/* audio/garbage? [TMNT-map] */
|
||||
/* config? [Prince of Persia: Warrior Within] */
|
||||
/* config? [Prince of Persia: The Two Thrones] */
|
||||
/* config? [TMNT-bank] */
|
||||
/* config? [Surf's Up-map] */
|
||||
/* config? [Brothers in Arms: D-Day] */
|
||||
/* config? [Surf's Up-bank] */
|
||||
case 0x08: sb->subtypes[0x08]++; break;
|
||||
|
||||
/* related to voices? [Prince of Persia: Sands of Time] */
|
||||
/* config? [Rainbow Six 3] */
|
||||
/* config? [Myst IV demo] */
|
||||
case 0x0a: sb->subtypes[0x0a]++; break;
|
||||
|
||||
/* config? [Prince of Persia: Sands of Time] */
|
||||
/* config? [Rainbow Six 3] */
|
||||
case 0x0c: sb->subtypes[0x0c]++; break;
|
||||
|
||||
/* layer [Prince of Persia: Sands of Time] */
|
||||
/* layer [Rainbow Six 3] */
|
||||
case 0x0d: sb->subtypes[0x0d]++; break;
|
||||
|
||||
/* config? [Rainbow Six 3] */
|
||||
/* config? [Myst IV demo] */
|
||||
case 0x0f: sb->subtypes[0x0f]++; break;
|
||||
|
||||
default:
|
||||
VGM_LOG("UBI SB: unknown subtype %x at %x size %x\n", descriptor_subtype, (uint32_t)offset, sb->section2_entry_size);
|
||||
break; //goto fail;
|
||||
}
|
||||
|
||||
//;VGM_ASSERT(descriptor_subtype != 0x01, "UBI SB: subtype %x at %x size %x\n", descriptor_subtype, (uint32_t)offset, sb->section2_entry_size);
|
||||
|
||||
|
||||
/* 0x06 layer [TMNT-bank] */
|
||||
/* - subtype header:
|
||||
* 0x1c: external flag?
|
||||
* 0x20: layers/channels?
|
||||
* 0x24: layers/channels?
|
||||
* 0x28: config? same for all
|
||||
* 0x2c: stream size
|
||||
* 0x30: stream offset
|
||||
* 0x38: always 0x037c
|
||||
* (todo sample rate?)
|
||||
*
|
||||
* - layer header at stream_offset:
|
||||
* 0x00: version? (0x08000B00)
|
||||
* 0x04: 0x0e?
|
||||
* 0x08: layers/channels?
|
||||
* 0x0c: blocks count
|
||||
* 0x10: block header size
|
||||
* 0x14: size of header sizes + codec headers
|
||||
* 0x18: block size
|
||||
* - per layer:
|
||||
* 0x00: layer header size
|
||||
* - per layer
|
||||
* 0x00~0x20: standard Ubi IMA header (version 0x05)
|
||||
*
|
||||
* - blocked data:
|
||||
* 0x00: always 0x03
|
||||
* 0x04: block size
|
||||
* - per layer:
|
||||
* 0x00: layer data size (varies between blocks, and one layer may have more than other)
|
||||
*/
|
||||
|
||||
/* 0x06 layer [TMNT-map] */
|
||||
/* - subtype header:
|
||||
* 0x20: layers/channels?
|
||||
* 0x24: layers/channels * 2?
|
||||
* 0x28: config? same for all
|
||||
* 0x2c: stream size
|
||||
* 0x30: stream offset
|
||||
* 0x38: 0x01D0/0118/etc
|
||||
* 0x40: external flag?
|
||||
* 0x48: codec?
|
||||
* 0x54: codec?
|
||||
* (todo sample rate?)
|
||||
*
|
||||
* - layer header at stream_offset:
|
||||
* - blocked data:
|
||||
* same as TMNT-bank, but codec header size is 0
|
||||
*/
|
||||
|
||||
//todo Surf's Up-map
|
||||
|
||||
/* 0x0d layer [Prince of Persia: Sands of Time] */
|
||||
/* 0x0d layer [Rainbow Six 3] */ //todo also check layer header
|
||||
/* - subtype header (bizarrely but thankfully doesn't change between platforms):
|
||||
* 0x1c: sample rate * layers
|
||||
* 0x20: layers/channels?
|
||||
* 0x2c: external flag?
|
||||
* 0x30: external name
|
||||
* 0x58: stream offset
|
||||
* 0x5c: original rate * layers?
|
||||
* 0x60: stream size (not including padding)
|
||||
* 0x64: number of samples
|
||||
*
|
||||
* - layer header at stream_offset:
|
||||
* 0x00: version? (0x04000000)
|
||||
* 0x04: layers
|
||||
* 0x08: stream size (not including padding)
|
||||
* 0x0c: blocks count
|
||||
* 0x10: block header size
|
||||
* 0x14: block size
|
||||
* 0x18: ?
|
||||
* 0x1c: size of next section?
|
||||
* - per layer:
|
||||
* 0x00: layer header size
|
||||
* codec header per layer
|
||||
* 0x00~0x20: standard Ubi IMA header (version 0x05)
|
||||
*
|
||||
* - blocked data:
|
||||
* 0x00: block number (from 0x01 to block_count)
|
||||
* 0x04: current offset (within stream_offset)
|
||||
* 0x08: always 0x03
|
||||
* - per layer:
|
||||
* 0x00: layer data size (varies between blocks, and one layer may have more than other)
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
static int parse_sb_header(ubi_sb_header * sb, STREAMFILE *streamFile, int target_stream) {
|
||||
int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL;
|
||||
int16_t (*read_16bit)(off_t,STREAMFILE*) = NULL;
|
||||
@ -599,11 +787,15 @@ static int parse_sb_header(ubi_sb_header * sb, STREAMFILE *streamFile, int targe
|
||||
/* find target stream info in section2 */
|
||||
for (i = 0; i < sb->section2_num; i++) {
|
||||
off_t offset = sb->section2_offset + sb->section2_entry_size*i;
|
||||
uint32_t descriptor_subtype;
|
||||
|
||||
/* ignore non-audio entry (other types seem to have config data) */
|
||||
if (read_32bit(offset + 0x04, streamFile) != 0x01)
|
||||
descriptor_subtype = read_32bit(offset + 0x04, streamFile);
|
||||
parse_descriptor_subtype(sb, descriptor_subtype, offset);
|
||||
|
||||
/* ignore non-audio entries */
|
||||
if (descriptor_subtype != 0x01)
|
||||
continue;
|
||||
//;VGM_LOG("SB at %lx\n", offset);
|
||||
//;VGM_LOG("UBI SB: type at %lx\n", offset);
|
||||
|
||||
/* weird case when there is no internal substream ID and just seem to rotate every time type changes, joy */
|
||||
if (sb->has_rotating_ids) { /* assumes certain configs can't happen in this case */
|
||||
@ -655,6 +847,7 @@ static int parse_sb_header(ubi_sb_header * sb, STREAMFILE *streamFile, int targe
|
||||
continue;
|
||||
//;VGM_LOG("target at offset=%lx (size=%x)\n", offset, sb->section2_entry_size);
|
||||
|
||||
/* parse audio entry based on config */
|
||||
sb->header_offset = offset;
|
||||
sb->header_idx = i;
|
||||
|
||||
@ -719,6 +912,17 @@ static int parse_sb_header(ubi_sb_header * sb, STREAMFILE *streamFile, int targe
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
{
|
||||
int i;
|
||||
VGM_LOG("UBI subtypes: ");
|
||||
for (i = 0; i < 16; i++) {
|
||||
VGM_ASSERT(sb->subtypes[i], "%02x=%i ",i,sb->subtypes[i]);
|
||||
}
|
||||
VGM_LOG("\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
if (sb->is_map) {
|
||||
if (bank_streams == 0 || target_stream <= prev_streams || target_stream > sb->total_streams)
|
||||
return 1; /* Target stream is not in this map */
|
||||
@ -857,9 +1061,9 @@ static int parse_sb_header(ubi_sb_header * sb, STREAMFILE *streamFile, int targe
|
||||
for (i = 0; i < sb->section3_num; i++) {
|
||||
off_t offset = sb->section3_offset + 0x14 * i;
|
||||
off_t table_offset = read_32bit(offset + 0x04, streamFile) + sb->section3_offset;
|
||||
off_t table_num = read_32bit(offset + 0x08, streamFile);
|
||||
uint32_t table_num = read_32bit(offset + 0x08, streamFile);
|
||||
off_t table2_offset = read_32bit(offset + 0x0c, streamFile) + sb->section3_offset;
|
||||
off_t table2_num = read_32bit(offset + 0x10, streamFile);
|
||||
uint32_t table2_num = read_32bit(offset + 0x10, streamFile);
|
||||
|
||||
for (j = 0; j < table_num; j++) {
|
||||
int idx = read_32bit(table_offset + 0x08 * j + 0x00, streamFile) & 0x0000FFFF;
|
||||
|
@ -2,7 +2,7 @@
|
||||
#include "../layout/layout.h"
|
||||
#include "../coding/coding.h"
|
||||
|
||||
/* CD-XA - from Sony PS1 CDs */
|
||||
/* CD-XA - from Sony PS1 and Philips CD-i CD audio */
|
||||
VGMSTREAM * init_vgmstream_xa(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
off_t start_offset;
|
||||
@ -10,6 +10,7 @@ VGMSTREAM * init_vgmstream_xa(STREAMFILE *streamFile) {
|
||||
int is_blocked;
|
||||
size_t file_size = get_streamfile_size(streamFile);
|
||||
|
||||
|
||||
/* checks
|
||||
* .xa: common, .str: sometimes (mainly videos)
|
||||
* .adp: Phantasy Star Collection (SAT) raw XA */
|
||||
@ -53,7 +54,7 @@ VGMSTREAM * init_vgmstream_xa(STREAMFILE *streamFile) {
|
||||
for (i = 0; i < (sector_size/block_size); i++) {
|
||||
/* XA headers checks: filter indexes should be 0..3, and shifts 0..C */
|
||||
for (j = 0; j < 16; j++) {
|
||||
uint8_t header = (uint8_t)read_8bit(test_offset + i, streamFile);
|
||||
uint8_t header = (uint8_t)read_8bit(test_offset + j, streamFile);
|
||||
if (((header >> 4) & 0xF) > 0x03)
|
||||
goto fail;
|
||||
if (((header >> 0) & 0xF) > 0x0c)
|
||||
|
@ -87,8 +87,9 @@ VGMSTREAM * init_vgmstream_xwb(STREAMFILE *streamFile) {
|
||||
|
||||
/* checks */
|
||||
/* .xwb: standard
|
||||
* .xna: Touhou Makukasai ~ Fantasy Danmaku Festival (PC) */
|
||||
if (!check_extensions(streamFile,"xwb,xna"))
|
||||
* .xna: Touhou Makukasai ~ Fantasy Danmaku Festival (PC)
|
||||
* (extensionless): Grabbed by the Ghoulies (Xbox) */
|
||||
if (!check_extensions(streamFile,"xwb,xna,"))
|
||||
goto fail;
|
||||
if ((read_32bitBE(0x00,streamFile) != 0x57424E44) && /* "WBND" (LE) */
|
||||
(read_32bitBE(0x00,streamFile) != 0x444E4257)) /* "DNBW" (BE) */
|
||||
|
@ -800,6 +800,15 @@ STREAMFILE * open_streamfile_by_filename(STREAMFILE *streamFile, const char * na
|
||||
return streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE);
|
||||
}
|
||||
|
||||
STREAMFILE * reopen_streamfile(STREAMFILE *streamFile, size_t buffer_size) {
|
||||
char pathname[PATH_LIMIT];
|
||||
|
||||
if (buffer_size == 0)
|
||||
buffer_size = STREAMFILE_DEFAULT_BUFFER_SIZE;
|
||||
streamFile->get_name(streamFile,pathname,sizeof(pathname));
|
||||
return streamFile->open(streamFile,pathname,buffer_size);
|
||||
}
|
||||
|
||||
|
||||
/* Read a line into dst. The source files are lines separated by CRLF (Windows) / LF (Unux) / CR (Mac).
|
||||
* The line will be null-terminated and CR/LF removed if found.
|
||||
|
@ -123,6 +123,10 @@ STREAMFILE * open_streamfile_by_ext(STREAMFILE *streamFile, const char * ext);
|
||||
* Can be used to get companion files. */
|
||||
STREAMFILE * open_streamfile_by_filename(STREAMFILE *streamFile, const char * filename);
|
||||
|
||||
/* Reopen a STREAMFILE with a different buffer size, for fine-tuned bigfile parsing.
|
||||
* Uses default buffer size when buffer_size is 0 */
|
||||
STREAMFILE * reopen_streamfile(STREAMFILE *streamFile, size_t buffer_size);
|
||||
|
||||
|
||||
/* close a file, destroy the STREAMFILE object */
|
||||
static inline void close_streamfile(STREAMFILE * streamfile) {
|
||||
|
@ -84,6 +84,7 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = {
|
||||
init_vgmstream_pos,
|
||||
init_vgmstream_nwa,
|
||||
init_vgmstream_ea_1snh,
|
||||
init_vgmstream_ea_eacs,
|
||||
init_vgmstream_xss,
|
||||
init_vgmstream_sl3,
|
||||
init_vgmstream_hgc1,
|
||||
|
@ -462,6 +462,7 @@ typedef enum {
|
||||
meta_EA_SCHL_fixed, /* Electronic Arts SCHl with fixed header */
|
||||
meta_EA_BNK, /* Electronic Arts BNK */
|
||||
meta_EA_1SNH, /* Electronic Arts 1SNh/EACS */
|
||||
meta_EA_EACS,
|
||||
|
||||
meta_RAW, /* RAW PCM file */
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user