Fix various PS2/GC/Wii/PC/Xbox/PSP .mul/emff [TR Legend, TR Underworld]

This commit is contained in:
bnnm 2018-12-22 19:47:17 +01:00
parent 87972f55eb
commit 47ab52f4f2
8 changed files with 189 additions and 198 deletions

View File

@ -128,7 +128,7 @@ static const char* extension_list[] = {
"e4x", "e4x",
"eam", "eam",
"emff", "emff", //fake extension for .mul (to be removed)
"enm", "enm",
"eno", "eno",
"ens", "ens",
@ -248,6 +248,7 @@ static const char* extension_list[] = {
"msvp", "msvp",
"mta2", "mta2",
"mtaf", "mtaf",
"mul",
"mus", "mus",
"musc", "musc",
"musx", "musx",
@ -673,8 +674,7 @@ static const layout_info layout_info_list[] = {
{layout_blocked_matx, "blocked (Matrix .matx)"}, {layout_blocked_matx, "blocked (Matrix .matx)"},
{layout_blocked_dec, "blocked (DEC)"}, {layout_blocked_dec, "blocked (DEC)"},
{layout_blocked_vs, "blocked (VS)"}, {layout_blocked_vs, "blocked (VS)"},
{layout_blocked_emff_ps2, "blocked (EMFF PS2)"}, {layout_blocked_mul, "blocked (MUL)"},
{layout_blocked_emff_ngc, "blocked (EMFF NGC)"},
{layout_blocked_gsb, "blocked (GSB)"}, {layout_blocked_gsb, "blocked (GSB)"},
{layout_blocked_thp, "blocked (THP Movie Audio)"}, {layout_blocked_thp, "blocked (THP Movie Audio)"},
{layout_blocked_filp, "blocked (FILP)"}, {layout_blocked_filp, "blocked (FILP)"},
@ -879,8 +879,7 @@ static const meta_info meta_info_list[] = {
{meta_VGS, "Guitar Hero VGS Header"}, {meta_VGS, "Guitar Hero VGS Header"},
{meta_DC_DCSW_DCS, "Evil Twin DCS file with helper"}, {meta_DC_DCSW_DCS, "Evil Twin DCS file with helper"},
{meta_WII_SMP, "SMP DSP Header"}, {meta_WII_SMP, "SMP DSP Header"},
{meta_EMFF_PS2, "Eidos Music File Format Header"}, {meta_MUL, "Crystal Dynamics .MUL header"},
{meta_EMFF_NGC, "Eidos Music File Format Header"},
{meta_THP, "THP Movie File Format Header"}, {meta_THP, "THP Movie File Format Header"},
{meta_STS_WII, "Shikigami no Shiro (WII) Header"}, {meta_STS_WII, "Shikigami no Shiro (WII) Header"},
{meta_PS2_P2BT, "Pop'n'Music 7 Header"}, {meta_PS2_P2BT, "Pop'n'Music 7 Header"},

View File

@ -127,11 +127,8 @@ void block_update(off_t block_offset, VGMSTREAM * vgmstream) {
case layout_blocked_dec: case layout_blocked_dec:
block_update_dec(block_offset,vgmstream); block_update_dec(block_offset,vgmstream);
break; break;
case layout_blocked_emff_ps2: case layout_blocked_mul:
block_update_emff_ps2(block_offset,vgmstream); block_update_mul(block_offset,vgmstream);
break;
case layout_blocked_emff_ngc:
block_update_emff_ngc(block_offset,vgmstream);
break; break;
case layout_blocked_gsb: case layout_blocked_gsb:
block_update_gsb(block_offset,vgmstream); block_update_gsb(block_offset,vgmstream);

View File

@ -1,34 +1,50 @@
#include "layout.h" #include "layout.h"
#include "../vgmstream.h" #include "../vgmstream.h"
/* set up for the block at the given offset */ /* process headered blocks with sub-headers */
void block_update_emff_ps2(off_t block_offset, VGMSTREAM * vgmstream) { void block_update_mul(off_t block_offset, VGMSTREAM * vgmstream) {
int i; STREAMFILE* streamFile = vgmstream->ch[0].streamfile;
int i, block_type;
size_t block_size, block_header, data_size, data_header;
int32_t (*read_32bit)(off_t,STREAMFILE*) = vgmstream->codec_endian ? read_32bitBE : read_32bitLE;
block_type = read_32bit(block_offset + 0x00,streamFile);
block_size = read_32bit(block_offset + 0x04,streamFile); /* not including main header */
switch(vgmstream->coding_type) {
case coding_NGC_DSP:
block_header = 0x20;
data_header = 0x20;
break;
default:
block_header = 0x10;
data_header = 0x10;
break;
}
if (block_type == 0x00 && block_size == 0) {
/* oddity in some vid+audio files? bad extraction? */
block_header = 0x10;
data_header = 0x00;
data_size = 0;
}
if (block_type == 0x00 && block_size != 0) {
/* read audio sub-header */
data_size = read_32bit(block_offset + block_header + 0x00,streamFile);
}
else {
/* non-audio or empty audio block */
data_header = 0x00;
data_size = 0;
}
vgmstream->current_block_offset = block_offset; vgmstream->current_block_offset = block_offset;
vgmstream->current_block_size = read_32bitLE( vgmstream->current_block_size = data_size / vgmstream->channels;
vgmstream->current_block_offset+0x10, vgmstream->next_block_offset = block_offset + block_header + block_size;
vgmstream->ch[0].streamfile);
vgmstream->next_block_offset = vgmstream->current_block_offset + vgmstream->current_block_size+0x20;
vgmstream->current_block_size/=vgmstream->channels;
for (i = 0; i < vgmstream->channels; i++) { for (i = 0; i < vgmstream->channels; i++) {
vgmstream->ch[i].offset = vgmstream->current_block_offset+0x20+(vgmstream->current_block_size*i); vgmstream->ch[i].offset = block_offset + block_header + data_header + vgmstream->current_block_size*i;
//VGM_LOG("ch%i of=%lx\n", i, vgmstream->ch[i].offset);
} }
//getchar();
} }
void block_update_emff_ngc(off_t block_offset, VGMSTREAM * vgmstream) {
int i;
vgmstream->current_block_offset = block_offset;
vgmstream->current_block_size = read_32bitBE(
vgmstream->current_block_offset+0x20,
vgmstream->ch[0].streamfile);
vgmstream->next_block_offset = vgmstream->current_block_offset + vgmstream->current_block_size+0x40;
vgmstream->current_block_size/=vgmstream->channels;
for (i=0;i<vgmstream->channels;i++) {
vgmstream->ch[i].offset = vgmstream->current_block_offset+0x40+(vgmstream->current_block_size*i);
}
}

View File

@ -21,8 +21,7 @@ void block_update_ws_aud(off_t block_offset, VGMSTREAM * vgmstream);
void block_update_matx(off_t block_offset, VGMSTREAM * vgmstream); void block_update_matx(off_t block_offset, VGMSTREAM * vgmstream);
void block_update_dec(off_t block_offset, VGMSTREAM * vgmstream); void block_update_dec(off_t block_offset, VGMSTREAM * vgmstream);
void block_update_vs(off_t block_offset, VGMSTREAM * vgmstream); void block_update_vs(off_t block_offset, VGMSTREAM * vgmstream);
void block_update_emff_ps2(off_t block_offset, VGMSTREAM * vgmstream); void block_update_mul(off_t block_offset, VGMSTREAM * vgmstream);
void block_update_emff_ngc(off_t block_offset, VGMSTREAM * vgmstream);
void block_update_gsb(off_t block_offset, VGMSTREAM * vgmstream); void block_update_gsb(off_t block_offset, VGMSTREAM * vgmstream);
void block_update_xvas(off_t block_offset, VGMSTREAM * vgmstream); void block_update_xvas(off_t block_offset, VGMSTREAM * vgmstream);
void block_update_thp(off_t block_offset, VGMSTREAM * vgmstream); void block_update_thp(off_t block_offset, VGMSTREAM * vgmstream);

View File

@ -1,179 +1,164 @@
#include "meta.h" #include "meta.h"
#include "../layout/layout.h" #include "../layout/layout.h"
#include "../util.h" #include "../coding/coding.h"
/* EMFF - Eidos Music File Format (PS2), typedef enum { NONE, PSX, DSP, XBOX } mul_codec;
Legacy of Kain - Defiance, possibly more... */
VGMSTREAM * init_vgmstream_emff_ps2(STREAMFILE *streamFile) { /* .MUL - from Crystal Dynamics games [Legacy of Kain: Defiance (PS2), Tomb Raider Underworld (multi)] */
VGMSTREAM * init_vgmstream_mul(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL; VGMSTREAM * vgmstream = NULL;
char filename[PATH_LIMIT]; off_t start_offset, coefs_offset = 0;
off_t start_offset; int loop_flag, channel_count, sample_rate, num_samples, loop_start;
int loop_flag = 0; int big_endian;
int channel_count; mul_codec codec = NONE;
int frequency; int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL;
int i;
/* check extension, case insensitive */
streamFile->get_name(streamFile,filename,sizeof(filename));
if (strcasecmp("emff",filename_extension(filename))) goto fail;
/* do some checks on the file, cause we have no magic words to check the header... /* checks */
it seems if 0x800 and 0x804 = 0 then the file has only audio, if 0x800 = 1 /* .mul: found in the exe, used by the bigfile extractor (Gibbed.TombRaider)
it has a text section, if both are 1 it's video with a text section included... */ * (some files have companion .mus/sam files but seem to be sequences/control stuff)
if (read_32bitBE(0x800,streamFile) == 0x01000000 || /* "0x01000000" */ * .(extensionless): filenames as found in the bigfile
read_32bitBE(0x804,streamFile) == 0x01000000) /* "0x01000000" */ * .emff: fake extension ('Eidos Music File Format') */
if (!check_extensions(streamFile, "mul,,emff"))
goto fail;
if (read_32bitBE(0x10,streamFile) != 0 ||
read_32bitBE(0x14,streamFile) != 0 ||
read_32bitBE(0x18,streamFile) != 0 ||
read_32bitBE(0x1c,streamFile) != 0)
goto fail; goto fail;
frequency = read_32bitLE(0x0,streamFile); big_endian = guess_endianness32bit(0x00, streamFile);
channel_count = read_32bitLE(0xC,streamFile); read_32bit = big_endian ? read_32bitBE : read_32bitLE;
if (frequency > 48000 || sample_rate = read_32bit(0x00,streamFile);
channel_count > 8) { loop_start = read_32bit(0x04,streamFile);
num_samples = read_32bit(0x08,streamFile);
channel_count = read_32bit(0x0C,streamFile);
if (sample_rate < 8000 || sample_rate > 48000 || channel_count > 8)
goto fail;
/* 0x20: flag when file has non-audio blocks (ignored by the layout) */
/* 0x24: same? */
/* 0x28: loop offset within audio data (not file offset) */
/* 0x2c: some value related to loop? */
/* 0x34: id? */
/* 0x38+: channel config until ~0x100? (multiple 0x3F800000 depending on the number of channels) */
/* test known versions (later versions start from 0x24 instead of 0x20) */
if (!(read_32bit(0x38,streamFile) == 0x3F800000 ||
read_32bit(0x3c,streamFile) == 0x3F800000)) /* Tomb Raider Underworld */
goto fail;
loop_flag = (loop_start >= 0); /* 0xFFFFFFFF when not looping */
start_offset = 0x800;
/* format is pretty limited so we need to guess codec */
if (big_endian) {
/* test DSP (GC/Wii): check known coef locations */
if (read_32bitBE(0xC8,streamFile) != 0) { /* Tomb Raider Legend (GC) */
codec = DSP;
coefs_offset = 0xC8;
}
else if (read_32bitBE(0xCC,streamFile) != 0) { /* Tomb Raider Anniversary (Wii) */
codec = DSP;
coefs_offset = 0xCC;
}
else if (read_32bitBE(0x2D0,streamFile) != 0) { /* Tomb Raider Underworld (Wii) */
codec = DSP;
coefs_offset = 0x2D0;
}
// todo test XMA1 (X360): mono streams, each block has 1 sub-blocks of 0x800 packet per channel
// todo test ? (PS3)
}
else {
int i;
off_t offset = start_offset;
size_t file_size = get_streamfile_size(streamFile);
size_t frame_size;
/* check first audio frame */
while (offset < file_size) {
uint32_t block_type = read_32bit(offset+0x00, streamFile);
uint32_t block_size = read_32bit(offset+0x04, streamFile);
uint32_t data_size = read_32bit(offset+0x10, streamFile);
if (block_type != 0x00) {
offset += 0x10 + block_size;
continue; /* not audio */
}
/* test PS-ADPCM (PS2/PSP): flag is always 2 in .mul */
frame_size = 0x10;
for (i = 0; i < data_size / frame_size; i++) {
if (read_8bit(offset + 0x20 + frame_size*i + 0x01, streamFile) != 0x02)
break;
}
if (i == data_size / frame_size) {
codec = PSX;
break;
}
/* test XBOX-IMA (PC/Xbox): reserved frame header value is always 0 */
frame_size = 0x24;
for (i = 0; i < data_size / frame_size; i++) {
if (read_8bit(offset + 0x20 + frame_size*i + 0x03, streamFile) != 0x00)
break;
}
if (i == data_size / frame_size) {
codec = XBOX;
break;
}
break;
}
}
if (codec == NONE) {
VGM_LOG("MUL: unknown codec\n");
goto fail; goto fail;
} }
loop_flag = (read_32bitLE(0x4,streamFile) != 0xFFFFFFFF);
/* build the VGMSTREAM */ /* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag); vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail; if (!vgmstream) goto fail;
/* fill in the vital statistics */ vgmstream->meta_type = meta_MUL;
start_offset = 0x800; vgmstream->sample_rate = sample_rate;
vgmstream->sample_rate = frequency; vgmstream->num_samples = num_samples;
vgmstream->channels = channel_count; vgmstream->loop_start_sample = loop_start;
vgmstream->loop_end_sample = num_samples;
vgmstream->codec_endian = big_endian;
switch(codec) {
case PSX:
vgmstream->coding_type = coding_PSX; vgmstream->coding_type = coding_PSX;
vgmstream->layout_type = layout_blocked_mul;
break;
vgmstream->layout_type = layout_blocked_emff_ps2; case DSP:
vgmstream->interleave_block_size = 0x10;
vgmstream->meta_type = meta_EMFF_PS2;
/* open the file for reading */
{
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;
}
}
/* Calc num_samples */
block_update_emff_ps2(start_offset,vgmstream);
vgmstream->num_samples = read_32bitLE(0x8,streamFile);
if (loop_flag) {
vgmstream->loop_start_sample = (read_32bitLE(0x28,streamFile)-start_offset)*28/16/channel_count;
vgmstream->loop_end_sample = read_32bitLE(0x8,streamFile);
}
return vgmstream;
/* clean up anything we may have opened */
fail:
if (vgmstream) close_vgmstream(vgmstream);
return NULL;
}
/* EMFF - Eidos Music File Format (NGC/WII),
found in Tomb Raider Legend/Anniversary/Underworld, possibly more... */
VGMSTREAM * init_vgmstream_emff_ngc(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
char filename[PATH_LIMIT];
off_t start_offset;
int loop_flag = 0;
int channel_count;
int frequency;
int i;
int j;
/* check extension, case insensitive */
streamFile->get_name(streamFile,filename,sizeof(filename));
if (strcasecmp("emff",filename_extension(filename))) goto fail;
/* do some checks on the file, cause we have no magic words to check the header...
it seems if 0x800 and 0x804 = 0 then the file has only audio, if 0x800 = 1
it has a text section, if both are 1 it's video with a text section included... */
if (read_32bitBE(0x800,streamFile) == 0x00000001 || /* "0x00000001" */
read_32bitBE(0x804,streamFile) == 0x00000001) /* "0x00000001" */
goto fail;
frequency = read_32bitBE(0x0,streamFile);
channel_count = read_32bitBE(0xC,streamFile);
if (frequency > 48000 ||
channel_count > 8) {
goto fail;
}
loop_flag = (read_32bitBE(0x4,streamFile) != 0xFFFFFFFF);
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail;
/* fill in the vital statistics */
start_offset = 0x800;
vgmstream->sample_rate = frequency;
vgmstream->channels = channel_count;
vgmstream->coding_type = coding_NGC_DSP; vgmstream->coding_type = coding_NGC_DSP;
vgmstream->layout_type = layout_blocked_mul;
/* Retrieving coefs and loops, depending on the file layout... */ dsp_read_coefs_be(vgmstream,streamFile,coefs_offset+0x00,0x2e);
/* Found in Tomb Raider - Legend for GameCube */ dsp_read_hist_be (vgmstream,streamFile,coefs_offset+0x24,0x2e);
if (read_32bitBE(0xC8,streamFile) > 0x0) { break;
off_t coef_table[8] = {0xC8,0xF6,0x124,0x152,0x180,0x1AE,0x1DC,0x20A};
for (j=0;j<vgmstream->channels;j++) {
for (i=0;i<16;i++) {
vgmstream->ch[j].adpcm_coef[i] = read_16bitBE(coef_table[j]+i*2,streamFile);
}
}
/* Found in Tomb Raider - Anniversary for WII */
} else if (read_32bitBE(0xCC,streamFile) > 0x0) {
off_t coef_table[8] = {0xCC,0xFA,0x128,0x156,0x184,0x1B2,0x1E0,0x20E};
for (j=0;j<vgmstream->channels;j++) {
for (i=0;i<16;i++) {
vgmstream->ch[j].adpcm_coef[i] = read_16bitBE(coef_table[j]+i*2,streamFile);
}
}
/* Found in Tomb Raider - Underworld for WII */
} else if (read_32bitBE(0x2D0,streamFile) > 0x0) {
off_t coef_table[8] = {0x2D0,0x2FE,0x32C,0x35A,0x388,0x3B6,0x3E4,0x412};
for (j=0;j<vgmstream->channels;j++) {
for (i=0;i<16;i++) {
vgmstream->ch[j].adpcm_coef[i] = read_16bitBE(coef_table[j]+i*2,streamFile);
}
}
} else { case XBOX:
vgmstream->coding_type = coding_XBOX_IMA_int;
vgmstream->layout_type = layout_blocked_mul;
break;
default:
goto fail; goto fail;
} }
vgmstream->layout_type = layout_blocked_emff_ngc; if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
vgmstream->interleave_block_size = 0x10; goto fail;
vgmstream->meta_type = meta_EMFF_NGC;
/* open the file for reading */
{
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;
}
}
/* Calc num_samples */
block_update_emff_ngc(start_offset,vgmstream);
vgmstream->num_samples = read_32bitBE(0x8,streamFile);;
if (loop_flag) {
vgmstream->loop_start_sample = (read_32bitBE(0x28,streamFile))*14/8/channel_count;
vgmstream->loop_end_sample = read_32bitBE(0x8,streamFile);
}
return vgmstream; return vgmstream;
/* clean up anything we may have opened */
fail: fail:
if (vgmstream) close_vgmstream(vgmstream); close_vgmstream(vgmstream);
return NULL; return NULL;
} }

View File

@ -361,8 +361,7 @@ VGMSTREAM * init_vgmstream_dc_dcsw_dcs(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_wii_smp(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_wii_smp(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_emff_ps2(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_mul(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_emff_ngc(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_thp(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_thp(STREAMFILE *streamFile);

View File

@ -192,8 +192,7 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = {
init_vgmstream_vgs, init_vgmstream_vgs,
init_vgmstream_dc_dcsw_dcs, init_vgmstream_dc_dcsw_dcs,
init_vgmstream_wii_smp, init_vgmstream_wii_smp,
init_vgmstream_emff_ps2, init_vgmstream_mul,
init_vgmstream_emff_ngc,
init_vgmstream_thp, init_vgmstream_thp,
init_vgmstream_wii_sts, init_vgmstream_wii_sts,
init_vgmstream_ps2_p2bt, init_vgmstream_ps2_p2bt,
@ -1005,8 +1004,7 @@ void render_vgmstream(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstre
case layout_blocked_matx: case layout_blocked_matx:
case layout_blocked_dec: case layout_blocked_dec:
case layout_blocked_vs: case layout_blocked_vs:
case layout_blocked_emff_ps2: case layout_blocked_mul:
case layout_blocked_emff_ngc:
case layout_blocked_gsb: case layout_blocked_gsb:
case layout_blocked_xvas: case layout_blocked_xvas:
case layout_blocked_thp: case layout_blocked_thp:

View File

@ -229,8 +229,7 @@ typedef enum {
layout_blocked_dec, layout_blocked_dec,
layout_blocked_xvas, layout_blocked_xvas,
layout_blocked_vs, layout_blocked_vs,
layout_blocked_emff_ps2, layout_blocked_mul,
layout_blocked_emff_ngc,
layout_blocked_gsb, layout_blocked_gsb,
layout_blocked_thp, layout_blocked_thp,
layout_blocked_filp, layout_blocked_filp,
@ -441,8 +440,7 @@ typedef enum {
meta_DC_DCSW_DCS, /* Evil Twin - Cypriens Chronicles (DC) */ meta_DC_DCSW_DCS, /* Evil Twin - Cypriens Chronicles (DC) */
meta_WII_SMP, /* Mushroom Men - The Spore Wars */ meta_WII_SMP, /* Mushroom Men - The Spore Wars */
meta_WII_SNG, /* Excite Trucks */ meta_WII_SNG, /* Excite Trucks */
meta_EMFF_PS2, /* Eidos Music File Format for PS2*/ meta_MUL,
meta_EMFF_NGC, /* Eidos Music File Format for NGC/WII */
meta_SAT_BAKA, /* Crypt Killer */ meta_SAT_BAKA, /* Crypt Killer */
meta_PS2_VSF, /* Musashi: Samurai Legend */ meta_PS2_VSF, /* Musashi: Samurai Legend */
meta_PS2_VSF_TTA, /* Tiny Toon Adventures: Defenders of the Universe */ meta_PS2_VSF_TTA, /* Tiny Toon Adventures: Defenders of the Universe */