mirror of
https://github.com/vgmstream/vgmstream.git
synced 2025-01-31 04:13:47 +01:00
Merge pull request #1552 from EdnessP/master
- Split up EA SCHl formats into individual files - Add EA Redwood Shores ABK variant - Add EA Redwood Shores extensions: - aiff: .mpc, .vp6 - schl_video: (SCHl) .mpc, .vp6 - Add THQ Australia VAG variant
This commit is contained in:
commit
b0329b573e
@ -62,7 +62,9 @@ static const char* extension_list[] = {
|
||||
"akb",
|
||||
"al", //txth/raw [Dominions 3 - The Awakening (PC)]
|
||||
"al2", //txth/raw [Conquest of Elysium 3 (PC)]
|
||||
"amb",
|
||||
"ams", //txth/reserved [Super Dragon Ball Z (PS2) ELF names]
|
||||
"amx",
|
||||
"an2",
|
||||
"ao",
|
||||
"ap",
|
||||
|
@ -434,7 +434,11 @@
|
||||
<ClCompile Include="meta\ea_eaac_tmx.c" />
|
||||
<ClCompile Include="meta\ea_sbk.c" />
|
||||
<ClCompile Include="meta\ea_schl.c" />
|
||||
<ClCompile Include="meta\ea_schl_abk.c" />
|
||||
<ClCompile Include="meta\ea_schl_fixed.c" />
|
||||
<ClCompile Include="meta\ea_schl_hdr_dat.c" />
|
||||
<ClCompile Include="meta\ea_schl_map_mpf_mus.c" />
|
||||
<ClCompile Include="meta\ea_schl_standard.c" />
|
||||
<ClCompile Include="meta\ea_swvr.c" />
|
||||
<ClCompile Include="meta\ea_wve_ad10.c" />
|
||||
<ClCompile Include="meta\ea_wve_au00.c" />
|
||||
|
@ -1123,9 +1123,21 @@
|
||||
<ClCompile Include="meta\ea_schl.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\ea_schl_abk.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\ea_schl_fixed.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\ea_schl_hdr_dat.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\ea_schl_map_mpf_mus.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\ea_schl_standard.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\ea_swvr.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
|
@ -102,6 +102,8 @@ VGMSTREAM* init_vgmstream_aifc(STREAMFILE* sf) {
|
||||
* .adp: Sonic Jam (SAT)
|
||||
* .ai: Dragon Force (SAT)
|
||||
* .pcm: Road Rash (SAT)
|
||||
* .vp6: The Godfather (PS3/X360) (logo.vp6)
|
||||
* .mpc: The Godfather (PC) (writercredit.mpc)
|
||||
*/
|
||||
if (check_extensions(sf, "aif,laif,wav,lwav,aiff,laiff,")) {
|
||||
is_aifc_ext = 1;
|
||||
@ -110,7 +112,7 @@ VGMSTREAM* init_vgmstream_aifc(STREAMFILE* sf) {
|
||||
else if (check_extensions(sf, "aifc,laifc,afc,cbd2,bgm,fda,n64,xa,caf")) {
|
||||
is_aifc_ext = 1;
|
||||
}
|
||||
else if (check_extensions(sf, "acm,adp,ai,pcm")) {
|
||||
else if (check_extensions(sf, "acm,adp,ai,pcm,vp6,mpc,lmpc")) {
|
||||
is_aiff_ext = 1;
|
||||
}
|
||||
else {
|
||||
|
@ -1,11 +1,70 @@
|
||||
#include "meta.h"
|
||||
#include "../util/endianness.h"
|
||||
|
||||
static VGMSTREAM* init_vgmstream_ea_abk_eaac_main(STREAMFILE* sf);
|
||||
static VGMSTREAM* parse_s10a_header(STREAMFILE* sf, off_t offset, uint16_t target_index, off_t ast_offset);
|
||||
|
||||
/* .ABK - standard */
|
||||
VGMSTREAM* init_vgmstream_ea_abk_eaac(STREAMFILE* sf) {
|
||||
if (!check_extensions(sf, "abk"))
|
||||
return NULL;
|
||||
return init_vgmstream_ea_abk_eaac_main(sf);
|
||||
}
|
||||
|
||||
/* .AMB/AMX - EA Redwood Shores variant [The Godfather (PS3/X360), The Simpsons Game (PS3/360)] */
|
||||
VGMSTREAM* init_vgmstream_ea_amb_eaac(STREAMFILE* sf) {
|
||||
/* container with .ABK ("ABKC") and .CSI ("MOIR") data */
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
STREAMFILE* sf_abk = NULL;
|
||||
off_t abk_offset;
|
||||
size_t abk_size;
|
||||
read_u32_t read_u32;
|
||||
|
||||
if (!check_extensions(sf, "amb,amx"))
|
||||
return NULL;
|
||||
|
||||
read_u32 = guess_read_u32(0x00, sf);
|
||||
if (read_u32(0x00, sf) != 0x09) /* version */
|
||||
return NULL;
|
||||
|
||||
abk_offset = 0x40;
|
||||
/* 0x04: MOIR offset (+ abk_offset)
|
||||
* 0x08: MOIR size
|
||||
* 0x0C: unk offset (same as MOIR)
|
||||
* 0x10: unk size (always 0?)
|
||||
* 0x14: unk (some hash?)
|
||||
* 0x18: always 1.0f?
|
||||
* 0x1C: always 2.0f?
|
||||
* 0x20: always 100.0f?
|
||||
* 0x24: unk (some bitfield? sometimes 0x10000)
|
||||
*/
|
||||
abk_size = read_u32(0x04, sf);
|
||||
|
||||
if (read_u32(0x0C, sf) != abk_size)
|
||||
goto fail;
|
||||
|
||||
/* in case stricter checks are needed: */
|
||||
//if (!is_id32be(abk_offset + abk_size, sf, "MOIR"))
|
||||
// goto fail;
|
||||
|
||||
sf_abk = open_wrap_streamfile(sf);
|
||||
sf_abk = open_clamp_streamfile(sf_abk, abk_offset, abk_size);
|
||||
if (!sf_abk) goto fail;
|
||||
|
||||
vgmstream = init_vgmstream_ea_abk_eaac_main(sf_abk);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
close_streamfile(sf_abk);
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_vgmstream(vgmstream);
|
||||
close_streamfile(sf_abk);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* EA ABK - ABK header seems to be same as in the old games but the sound table is different and it contains SNR/SNS sounds instead */
|
||||
VGMSTREAM* init_vgmstream_ea_abk_eaac(STREAMFILE* sf) {
|
||||
static VGMSTREAM* init_vgmstream_ea_abk_eaac_main(STREAMFILE* sf) {
|
||||
VGMSTREAM* vgmstream;
|
||||
int is_dupe, total_sounds = 0, target_stream = sf->stream_index;
|
||||
off_t bnk_offset, modules_table, module_data, player_offset, samples_table, entry_offset, ast_offset;
|
||||
@ -14,32 +73,30 @@ VGMSTREAM* init_vgmstream_ea_abk_eaac(STREAMFILE* sf) {
|
||||
uint16_t num_modules, bnk_index, bnk_target_index;
|
||||
uint8_t num_players;
|
||||
off_t sample_tables[0x400];
|
||||
int32_t(*read_32bit)(off_t, STREAMFILE*);
|
||||
int16_t(*read_16bit)(off_t, STREAMFILE*);
|
||||
read_u32_t read_u32;
|
||||
read_u16_t read_u16;
|
||||
|
||||
|
||||
/* checks */
|
||||
if (!is_id32be(0x00, sf, "ABKC"))
|
||||
return NULL;
|
||||
if (!check_extensions(sf, "abk"))
|
||||
return NULL;
|
||||
|
||||
/* use table offset to check endianness */
|
||||
if (guess_endian32(0x1C, sf)) {
|
||||
read_32bit = read_32bitBE;
|
||||
read_16bit = read_16bitBE;
|
||||
read_u32 = read_u32be;
|
||||
read_u16 = read_u16be;
|
||||
} else {
|
||||
read_32bit = read_32bitLE;
|
||||
read_16bit = read_16bitLE;
|
||||
read_u32 = read_u32le;
|
||||
read_u16 = read_u16le;
|
||||
}
|
||||
|
||||
if (target_stream == 0) target_stream = 1;
|
||||
if (target_stream < 0)
|
||||
goto fail;
|
||||
|
||||
num_modules = read_16bit(0x0A, sf);
|
||||
modules_table = read_32bit(0x1C, sf);
|
||||
bnk_offset = read_32bit(0x20, sf);
|
||||
num_modules = read_u16(0x0A, sf);
|
||||
modules_table = read_u32(0x1C, sf);
|
||||
bnk_offset = read_u32(0x20, sf);
|
||||
num_sample_tables = 0;
|
||||
bnk_target_index = 0xFFFF;
|
||||
ast_offset = 0;
|
||||
@ -67,13 +124,13 @@ VGMSTREAM* init_vgmstream_ea_abk_eaac(STREAMFILE* sf) {
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < num_modules; i++) {
|
||||
num_players = read_8bit(modules_table + cfg_num_players_off, sf);
|
||||
module_data = read_32bit(modules_table + cfg_module_data_off, sf);
|
||||
num_players = read_u8(modules_table + cfg_num_players_off, sf);
|
||||
module_data = read_u32(modules_table + cfg_module_data_off, sf);
|
||||
if (num_players == 0xff) goto fail; /* EOF read */
|
||||
|
||||
for (uint32_t j = 0; j < num_players; j++) {
|
||||
player_offset = read_32bit(modules_table + cfg_module_entry_size + 0x04 * j, sf);
|
||||
samples_table = read_32bit(module_data + player_offset + cfg_samples_table_off, sf);
|
||||
player_offset = read_u32(modules_table + cfg_module_entry_size + 0x04 * j, sf);
|
||||
samples_table = read_u32(module_data + player_offset + cfg_samples_table_off, sf);
|
||||
|
||||
/* multiple players may point at the same sound table */
|
||||
is_dupe = 0;
|
||||
@ -88,7 +145,7 @@ VGMSTREAM* init_vgmstream_ea_abk_eaac(STREAMFILE* sf) {
|
||||
continue;
|
||||
|
||||
sample_tables[num_sample_tables++] = samples_table;
|
||||
num_sounds = read_32bit(samples_table, sf);
|
||||
num_sounds = read_u32(samples_table, sf);
|
||||
if (num_sounds == 0xffffffff) goto fail; /* EOF read */
|
||||
|
||||
for (uint32_t k = 0; k < num_sounds; k++) {
|
||||
@ -97,7 +154,7 @@ VGMSTREAM* init_vgmstream_ea_abk_eaac(STREAMFILE* sf) {
|
||||
/* 0x03: azimuth */
|
||||
/* 0x08: streamed data offset */
|
||||
entry_offset = samples_table + 0x04 + 0x0C * k;
|
||||
bnk_index = read_16bit(entry_offset + 0x00, sf);
|
||||
bnk_index = read_u16(entry_offset + 0x00, sf);
|
||||
|
||||
/* some of these are dummies */
|
||||
if (bnk_index == 0xFFFF)
|
||||
@ -106,13 +163,13 @@ VGMSTREAM* init_vgmstream_ea_abk_eaac(STREAMFILE* sf) {
|
||||
total_sounds++;
|
||||
if (target_stream == total_sounds) {
|
||||
bnk_target_index = bnk_index;
|
||||
ast_offset = read_32bit(entry_offset + 0x08, sf);
|
||||
ast_offset = read_u32(entry_offset + 0x08, sf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* skip class controllers */
|
||||
num_players += read_8bit(modules_table + cfg_num_players_off + 0x03, sf);
|
||||
num_players += read_u8(modules_table + cfg_num_players_off + 0x03, sf);
|
||||
modules_table += cfg_module_entry_size + num_players * 0x04;
|
||||
}
|
||||
|
||||
@ -147,11 +204,11 @@ static VGMSTREAM* parse_s10a_header(STREAMFILE* sf, off_t offset, uint16_t targe
|
||||
if (!is_id32be(offset + 0x00, sf, "S10A"))
|
||||
return NULL;
|
||||
|
||||
num_sounds = read_32bitBE(offset + 0x08, sf);
|
||||
num_sounds = read_u32be(offset + 0x08, sf);
|
||||
if (num_sounds == 0 || target_index >= num_sounds)
|
||||
return NULL;
|
||||
|
||||
snr_offset = offset + read_32bitBE(offset + 0x0C + 0x04 * target_index, sf);
|
||||
snr_offset = offset + read_u32be(offset + 0x0C + 0x04 * target_index, sf);
|
||||
|
||||
if (sns_offset == 0xFFFFFFFF) {
|
||||
/* RAM asset */
|
||||
|
@ -15,7 +15,7 @@ VGMSTREAM* init_vgmstream_ea_hdr_sth_dat(STREAMFILE* sf) {
|
||||
uint16_t sth_offset, sth_offset2;
|
||||
uint8_t num_params, num_sounds, block_id;
|
||||
size_t dat_size;
|
||||
uint32_t(*read_u32)(off_t, STREAMFILE*);
|
||||
read_u32_t read_u32;
|
||||
eaac_meta_t info = {0};
|
||||
|
||||
/* 0x00: ID */
|
||||
|
@ -14,7 +14,7 @@ VGMSTREAM* init_vgmstream_ea_mpf_mus_eaac(STREAMFILE* sf) {
|
||||
return init_vgmstream_ea_mpf_mus_eaac_main(sf, NULL);
|
||||
}
|
||||
|
||||
/* .MSB/.MSX - EA Redwood Shores (MSB/MSX)+MUS [The Godfather (PS3/360), The Simpsons Game (PS3/360)] */
|
||||
/* .MSB/MSX - EA Redwood Shores (MSB/MSX)+MUS [The Godfather (PS3/360), The Simpsons Game (PS3/360)] */
|
||||
VGMSTREAM* init_vgmstream_ea_msb_mus_eaac(STREAMFILE* sf) {
|
||||
/* container with MPF, extra info, and a pre-defined MUS filename */
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
@ -85,8 +85,8 @@ static VGMSTREAM* init_vgmstream_ea_mpf_mus_eaac_main(STREAMFILE* sf, const char
|
||||
segmented_layout_data* data_s = NULL;
|
||||
int i;
|
||||
int target_stream = sf->stream_index, total_streams, is_ram = 0;
|
||||
uint32_t(*read_u32)(off_t, STREAMFILE *);
|
||||
uint16_t(*read_u16)(off_t, STREAMFILE *);
|
||||
read_u32_t read_u32;
|
||||
read_u16_t read_u16;
|
||||
|
||||
/* checks */
|
||||
if (is_id32be(0x00, sf, "PFDx")) {
|
||||
|
@ -17,9 +17,9 @@ VGMSTREAM* init_vgmstream_ea_sbr_harmony(STREAMFILE* sf) {
|
||||
char sound_name[STREAM_NAME_SIZE];
|
||||
int target_stream = sf->stream_index, total_sounds, local_target, is_streamed = 0;
|
||||
int i, j;
|
||||
uint64_t(*read_u64)(off_t, STREAMFILE *);
|
||||
uint32_t(*read_u32)(off_t, STREAMFILE*);
|
||||
uint16_t(*read_u16)(off_t, STREAMFILE*);
|
||||
read_u64_t read_u64;
|
||||
read_u32_t read_u32;
|
||||
read_u16_t read_u16;
|
||||
eaac_meta_t info = {0};
|
||||
|
||||
|
||||
|
1192
src/meta/ea_schl.c
1192
src/meta/ea_schl.c
File diff suppressed because it is too large
Load Diff
286
src/meta/ea_schl_abk.c
Normal file
286
src/meta/ea_schl_abk.c
Normal file
@ -0,0 +1,286 @@
|
||||
#include "meta.h"
|
||||
#include "../layout/layout.h"
|
||||
#include "../util/endianness.h"
|
||||
|
||||
#define EA_BLOCKID_HEADER 0x5343486C /* "SCHl" */
|
||||
|
||||
#define EA_BNK_HEADER_LE 0x424E4B6C /* "BNKl" */
|
||||
#define EA_BNK_HEADER_BE 0x424E4B62 /* "BNKb" */
|
||||
|
||||
static VGMSTREAM* init_vgmstream_ea_abk_schl_main(STREAMFILE* sf);
|
||||
|
||||
/* .ABK - standard */
|
||||
VGMSTREAM* init_vgmstream_ea_abk_schl(STREAMFILE* sf) {
|
||||
if (!check_extensions(sf, "abk"))
|
||||
return NULL;
|
||||
return init_vgmstream_ea_abk_schl_main(sf);
|
||||
}
|
||||
|
||||
/* .AMB/AMX - EA Redwood Shores variant [007: From Russia with Love, The Godfather (PC/PS2/Xbox/Wii)] */
|
||||
VGMSTREAM* init_vgmstream_ea_amb_schl(STREAMFILE* sf) {
|
||||
/* container with .ABK ("ABKC") and .CSI ("MOIR") data */
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
STREAMFILE* sf_abk = NULL;
|
||||
off_t abk_offset, header_offset = 0x00;
|
||||
size_t abk_size;
|
||||
uint32_t version;
|
||||
read_u32_t read_u32;
|
||||
|
||||
if (!check_extensions(sf, "amb,amx"))
|
||||
return NULL;
|
||||
|
||||
/* 0x08 covers both v4-v8 and v9 */
|
||||
read_u32 = guess_read_u32(0x08, sf);
|
||||
|
||||
if (read_u64le(0x00, sf) == 0) {
|
||||
header_offset = read_u32(0x08, sf);
|
||||
if (header_offset > 0x40) /* 0x20 (v4), 0x30/0x40 (v8) */
|
||||
return NULL;
|
||||
version = read_u32(header_offset, sf);
|
||||
if (version != 0x04 && version != 0x08)
|
||||
return NULL;
|
||||
}
|
||||
else if (read_u32(0x00, sf) != 0x09)
|
||||
return NULL;
|
||||
|
||||
|
||||
version = read_u32(header_offset + 0x00, sf);
|
||||
|
||||
abk_offset = header_offset + (version > 0x04 ? 0x40 : 0x20);
|
||||
/* Version 0x04: [007: From Russia with Love (Demo)]
|
||||
* 0x04: MOIR offset (+ abk offset)
|
||||
* 0x08: unk (always 0?)
|
||||
* 0x0C: unk (some hash?)
|
||||
* 0x10: always 2.0f?
|
||||
* 0x14: always 2.0f?
|
||||
* 0x18: always 100.0f?
|
||||
* 0x1C: unk (some bools? 0x0101)
|
||||
* 0x20: ABKC data
|
||||
*/
|
||||
/* Version 0x08: [007: From Russia with Love]
|
||||
* 0x04: MOIR offset (+ abk offset)
|
||||
* 0x08: unk offset (same as MOIR)
|
||||
* 0x0C: unk (always 0?)
|
||||
* 0x10: unk (some hash?)
|
||||
* 0x14: usually 2.0f? sometimes 1.0f or 5.0f
|
||||
* 0x18: always 2.0f?
|
||||
* 0x1C: usually 75.0f? sometimes 100.0f or 1000.0f
|
||||
* 0x20: unk (some bools? 0x0101)
|
||||
* 0x40: ABKC data
|
||||
*/
|
||||
/* Version 0x09: [The Godfather]
|
||||
* 0x04: MOIR offset (+ abk_offset)
|
||||
* 0x08: MOIR size
|
||||
* 0x0C: unk offset (same as MOIR, usually)
|
||||
* 0x10: unk size (always 0?)
|
||||
* 0x14: unk (some hash?)
|
||||
* 0x18: always 1.0f?
|
||||
* 0x1C: always 2.0f?
|
||||
* 0x20: always 100.0f?
|
||||
* 0x24: unk (some bools? sometimes 0x010000)
|
||||
* 0x40: ABKC data
|
||||
*/
|
||||
abk_size = read_u32(header_offset + 0x04, sf);
|
||||
|
||||
if (abk_size > get_streamfile_size(sf))
|
||||
goto fail;
|
||||
|
||||
/* in case stricter checks are needed: */
|
||||
//if (!is_id32be(abk_offset + abk_size, sf, "MOIR"))
|
||||
// goto fail;
|
||||
|
||||
/* (v9) rarely some other bad(?) value [The Godfather (PS2)] */
|
||||
//if (read_u32(0x0C, sf) != abk_size) goto fail;
|
||||
|
||||
sf_abk = open_wrap_streamfile(sf);
|
||||
sf_abk = open_clamp_streamfile(sf_abk, abk_offset, abk_size);
|
||||
if (!sf_abk) goto fail;
|
||||
|
||||
vgmstream = init_vgmstream_ea_abk_schl_main(sf_abk);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
close_streamfile(sf_abk);
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_vgmstream(vgmstream);
|
||||
close_streamfile(sf_abk);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* EA ABK - common soundbank format in 6th-gen games, can reference RAM and streamed assets */
|
||||
/* RAM assets are stored in embedded BNK file */
|
||||
/* streamed assets are stored externally in AST file (mostly seen in earlier 6th-gen games) */
|
||||
static VGMSTREAM* init_vgmstream_ea_abk_schl_main(STREAMFILE* sf) {
|
||||
int bnk_target_stream, is_dupe, total_sounds = 0, target_stream = sf->stream_index;
|
||||
off_t bnk_offset, modules_table, module_data, player_offset, samples_table, entry_offset, target_entry_offset, schl_offset, schl_loop_offset;
|
||||
uint32_t i, j, k, num_sounds, num_sample_tables;
|
||||
uint16_t num_modules;
|
||||
uint8_t sound_type, num_players;
|
||||
off_t sample_tables[0x400];
|
||||
STREAMFILE* sf_ast = NULL;
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
segmented_layout_data* data_s = NULL;
|
||||
read_u32_t read_u32;
|
||||
read_u16_t read_u16;
|
||||
|
||||
/* check extension */
|
||||
if (!is_id32be(0x00, sf, "ABKC"))
|
||||
return NULL;
|
||||
|
||||
/* use table offset to check endianness */
|
||||
if (guess_endian32(0x1C, sf)) {
|
||||
read_u32 = read_u32be;
|
||||
read_u16 = read_u16be;
|
||||
} else {
|
||||
read_u32 = read_u32le;
|
||||
read_u16 = read_u16le;
|
||||
}
|
||||
|
||||
if (target_stream == 0) target_stream = 1;
|
||||
if (target_stream < 0)
|
||||
goto fail;
|
||||
|
||||
num_modules = read_u16(0x0A, sf);
|
||||
modules_table = read_u32(0x1C, sf);
|
||||
bnk_offset = read_u32(0x20, sf);
|
||||
target_entry_offset = 0;
|
||||
num_sample_tables = 0;
|
||||
|
||||
/* check to avoid clashing with the newer ABK format */
|
||||
if (bnk_offset &&
|
||||
read_u32be(bnk_offset, sf) != EA_BNK_HEADER_LE &&
|
||||
read_u32be(bnk_offset, sf) != EA_BNK_HEADER_BE)
|
||||
goto fail;
|
||||
|
||||
for (i = 0; i < num_modules; i++) {
|
||||
num_players = read_u8(modules_table + 0x24, sf);
|
||||
module_data = read_u32(modules_table + 0x2C, sf);
|
||||
if (num_players == 0xff) goto fail; /* EOF read */
|
||||
|
||||
for (j = 0; j < num_players; j++) {
|
||||
player_offset = read_u32(modules_table + 0x3C + 0x04 * j, sf);
|
||||
samples_table = read_u32(module_data + player_offset + 0x04, sf);
|
||||
|
||||
/* multiple players may point at the same sound table */
|
||||
is_dupe = 0;
|
||||
for (k = 0; k < num_sample_tables; k++) {
|
||||
if (samples_table == sample_tables[k]) {
|
||||
is_dupe = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_dupe)
|
||||
continue;
|
||||
|
||||
sample_tables[num_sample_tables++] = samples_table;
|
||||
num_sounds = read_u32(samples_table, sf);
|
||||
if (num_sounds == 0xffffffff) goto fail; /* EOF read */
|
||||
|
||||
for (k = 0; k < num_sounds; k++) {
|
||||
entry_offset = samples_table + 0x04 + 0x0C * k;
|
||||
sound_type = read_u8(entry_offset + 0x00, sf);
|
||||
|
||||
/* some of these are dummies pointing at sound 0 in BNK */
|
||||
if (sound_type == 0x00 && read_u32(entry_offset + 0x04, sf) == 0)
|
||||
continue;
|
||||
|
||||
total_sounds++;
|
||||
if (target_stream == total_sounds)
|
||||
target_entry_offset = entry_offset;
|
||||
}
|
||||
}
|
||||
|
||||
/* skip class controllers */
|
||||
num_players += read_u8(modules_table + 0x27, sf);
|
||||
modules_table += 0x3C + num_players * 0x04;
|
||||
}
|
||||
|
||||
if (target_entry_offset == 0)
|
||||
goto fail;
|
||||
|
||||
/* 0x00: type (0x00 - RAM, 0x01 - streamed, 0x02 - streamed looped) */
|
||||
/* 0x01: priority */
|
||||
/* 0x02: padding */
|
||||
/* 0x04: index for RAM sounds, offset for streamed sounds */
|
||||
/* 0x08: loop offset for streamed sounds */
|
||||
sound_type = read_u8(target_entry_offset + 0x00, sf);
|
||||
|
||||
switch (sound_type) {
|
||||
case 0x00:
|
||||
if (!bnk_offset)
|
||||
goto fail;
|
||||
|
||||
bnk_target_stream = read_u32(target_entry_offset + 0x04, sf);
|
||||
vgmstream = load_vgmstream_ea_bnk(sf, bnk_offset, bnk_target_stream, 1);
|
||||
if (!vgmstream)
|
||||
goto fail;
|
||||
|
||||
break;
|
||||
|
||||
case 0x01:
|
||||
sf_ast = open_streamfile_by_ext(sf, "ast");
|
||||
if (!sf_ast)
|
||||
goto fail;
|
||||
|
||||
schl_offset = read_u32(target_entry_offset + 0x04, sf);
|
||||
if (read_u32be(schl_offset, sf_ast) != EA_BLOCKID_HEADER)
|
||||
goto fail;
|
||||
|
||||
vgmstream = load_vgmstream_ea_schl(sf_ast, schl_offset);
|
||||
if (!vgmstream)
|
||||
goto fail;
|
||||
|
||||
break;
|
||||
|
||||
case 0x02:
|
||||
sf_ast = open_streamfile_by_ext(sf, "ast");
|
||||
if (!sf_ast) {
|
||||
vgm_logi("EA ABK: .ast file not found (find and put together)\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* looped sounds basically consist of two independent segments
|
||||
* the first one is loop start, the second one is loop body */
|
||||
schl_offset = read_u32(target_entry_offset + 0x04, sf);
|
||||
schl_loop_offset = read_u32(target_entry_offset + 0x08, sf);
|
||||
|
||||
if (read_u32be(schl_offset, sf_ast) != EA_BLOCKID_HEADER ||
|
||||
read_u32be(schl_loop_offset, sf_ast) != EA_BLOCKID_HEADER)
|
||||
goto fail;
|
||||
|
||||
/* init layout */
|
||||
data_s = init_layout_segmented(2);
|
||||
if (!data_s) goto fail;
|
||||
|
||||
/* load intro and loop segments */
|
||||
data_s->segments[0] = load_vgmstream_ea_schl(sf_ast, schl_offset);
|
||||
if (!data_s->segments[0]) goto fail;
|
||||
data_s->segments[1] = load_vgmstream_ea_schl(sf_ast, schl_loop_offset);
|
||||
if (!data_s->segments[1]) goto fail;
|
||||
|
||||
/* setup segmented VGMSTREAMs */
|
||||
if (!setup_layout_segmented(data_s))
|
||||
goto fail;
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_segmented_vgmstream(data_s, 1, 1, 1);
|
||||
if (!vgmstream)
|
||||
goto fail;
|
||||
break;
|
||||
|
||||
default:
|
||||
goto fail;
|
||||
break;
|
||||
}
|
||||
|
||||
vgmstream->num_streams = total_sounds;
|
||||
close_streamfile(sf_ast);
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_streamfile(sf_ast);
|
||||
free_layout_segmented(data_s);
|
||||
return NULL;
|
||||
}
|
@ -32,13 +32,13 @@ VGMSTREAM* init_vgmstream_ea_schl_fixed(STREAMFILE* sf) {
|
||||
|
||||
/* checks */
|
||||
if (!is_id32be(0x00,sf, "SCHl"))
|
||||
goto fail;
|
||||
return NULL;
|
||||
|
||||
/* .asf: original [NHK 97 (PC)]
|
||||
* .lasf: fake for plugins
|
||||
* .cnk: ps1 [NBA Live 97 (PS1)] */
|
||||
if (!check_extensions(sf,"asf,lasf,cnk"))
|
||||
goto fail;
|
||||
return NULL;
|
||||
|
||||
/* see ea_schl.c for more info about blocks */
|
||||
//TODO: handle SCCl? [NBA Live 97 (PS1)]
|
||||
|
194
src/meta/ea_schl_hdr_dat.c
Normal file
194
src/meta/ea_schl_hdr_dat.c
Normal file
@ -0,0 +1,194 @@
|
||||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
#include "../util/endianness.h"
|
||||
|
||||
#define EA_BLOCKID_HEADER 0x5343486C /* "SCHl" */
|
||||
|
||||
/* EA HDR/DAT v1 (2004-2005) - used for storing speech, sometimes streamed SFX */
|
||||
VGMSTREAM* init_vgmstream_ea_hdr_dat(STREAMFILE* sf) {
|
||||
VGMSTREAM* vgmstream;
|
||||
STREAMFILE* sf_dat = NULL, * temp_sf = NULL;
|
||||
int target_stream = sf->stream_index;
|
||||
uint32_t offset_mult, sound_offset, sound_size;
|
||||
uint8_t num_params, num_sounds;
|
||||
size_t dat_size;
|
||||
|
||||
/* checks */
|
||||
if (!check_extensions(sf, "hdr"))
|
||||
return NULL;
|
||||
|
||||
/* main header is machine endian but it's not important here */
|
||||
/* 0x00: ID */
|
||||
/* 0x02: speaker ID (used for different police voices in NFS games) */
|
||||
/* 0x04: number of parameters */
|
||||
/* 0x05: number of samples */
|
||||
/* 0x06: sample repeat (alt number of samples?) */
|
||||
/* 0x07: block size (offset multiplier) */
|
||||
/* 0x08: number of blocks (DAT size divided by block size) */
|
||||
/* 0x0a: number of sub-banks */
|
||||
/* 0x0c: table start */
|
||||
|
||||
/* no nice way to validate these so we do what we can */
|
||||
if (read_u16be(0x0a, sf) != 0)
|
||||
return NULL;
|
||||
|
||||
/* first offset is always zero */
|
||||
if (read_u16be(0x0c, sf) != 0)
|
||||
return NULL;
|
||||
|
||||
/* must be accompanied by DAT file with SCHl or VAG sounds */
|
||||
sf_dat = open_streamfile_by_ext(sf, "dat");
|
||||
if (!sf_dat)
|
||||
goto fail;
|
||||
|
||||
if (read_u32be(0x00, sf_dat) != EA_BLOCKID_HEADER &&
|
||||
read_u32be(0x00, sf_dat) != 0x56414770) /* "VAGp" */
|
||||
goto fail;
|
||||
|
||||
num_params = read_u8(0x04, sf) & 0x7F;
|
||||
num_sounds = read_u8(0x05, sf);
|
||||
offset_mult = read_u8(0x07, sf) * 0x0100 + 0x0100;
|
||||
|
||||
if (read_u8(0x06, sf) > num_sounds)
|
||||
goto fail;
|
||||
|
||||
dat_size = get_streamfile_size(sf_dat);
|
||||
if (read_u16le(0x08, sf) * offset_mult > dat_size &&
|
||||
read_u16be(0x08, sf) * offset_mult > dat_size)
|
||||
goto fail;
|
||||
|
||||
if (target_stream == 0) target_stream = 1;
|
||||
if (target_stream < 0 || num_sounds == 0 || target_stream > num_sounds)
|
||||
goto fail;
|
||||
|
||||
/* offsets are always big endian */
|
||||
sound_offset = read_u16be(0x0C + (0x02 + num_params) * (target_stream - 1), sf) * offset_mult;
|
||||
if (read_u32be(sound_offset, sf_dat) == EA_BLOCKID_HEADER) { /* "SCHl" */
|
||||
vgmstream = load_vgmstream_ea_schl(sf_dat, sound_offset);
|
||||
if (!vgmstream)
|
||||
goto fail;
|
||||
}
|
||||
else if (read_u32be(sound_offset, sf_dat) == 0x56414770) { /* "VAGp" */
|
||||
/* Need for Speed: Hot Pursuit 2 (PS2) */
|
||||
sound_size = read_u32be(sound_offset + 0x0c, sf_dat) + 0x30;
|
||||
temp_sf = setup_subfile_streamfile(sf_dat, sound_offset, sound_size, "vag");
|
||||
if (!temp_sf) goto fail;
|
||||
|
||||
vgmstream = init_vgmstream_vag(temp_sf);
|
||||
if (!vgmstream) goto fail;
|
||||
close_streamfile(temp_sf);
|
||||
}
|
||||
else {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (num_params != 0) {
|
||||
uint8_t val;
|
||||
char buf[8];
|
||||
int i;
|
||||
for (i = 0; i < num_params; i++) {
|
||||
val = read_u8(0x0C + (0x02 + num_params) * (target_stream - 1) + 0x02 + i, sf);
|
||||
snprintf(buf, sizeof(buf), "%u", val);
|
||||
concatn(STREAM_NAME_SIZE, vgmstream->stream_name, buf);
|
||||
if (i != num_params - 1)
|
||||
concatn(STREAM_NAME_SIZE, vgmstream->stream_name, ", ");
|
||||
}
|
||||
}
|
||||
|
||||
vgmstream->num_streams = num_sounds;
|
||||
close_streamfile(sf_dat);
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_streamfile(sf_dat);
|
||||
close_streamfile(temp_sf);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* EA HDR/DAT v2 (2006-2014) */
|
||||
VGMSTREAM* init_vgmstream_ea_hdr_dat_v2(STREAMFILE* sf) {
|
||||
VGMSTREAM* vgmstream;
|
||||
STREAMFILE* sf_dat = NULL;
|
||||
int target_stream = sf->stream_index;
|
||||
uint32_t offset_mult, sound_offset;
|
||||
uint8_t num_params, num_sounds;
|
||||
size_t dat_size;
|
||||
|
||||
/* checks */
|
||||
if (!check_extensions(sf, "hdr"))
|
||||
return NULL;
|
||||
|
||||
/* main header is machine endian but it's not important here */
|
||||
/* 0x00: ID */
|
||||
/* 0x02: number of parameters */
|
||||
/* 0x03: number of samples */
|
||||
/* 0x04: speaker ID (used for different police voices in NFS games) */
|
||||
/* 0x08: sample repeat (alt number of samples?) */
|
||||
/* 0x09: block size (offset multiplier) */
|
||||
/* 0x0a: number of blocks (DAT size divided by block size) */
|
||||
/* 0x0c: number of sub-banks (always zero?) */
|
||||
/* 0x0e: padding */
|
||||
/* 0x10: table start */
|
||||
|
||||
/* no nice way to validate these so we do what we can */
|
||||
if (read_u32be(0x0c, sf) != 0)
|
||||
return NULL;
|
||||
|
||||
/* first offset is always zero */
|
||||
if (read_u16be(0x10, sf) != 0)
|
||||
return NULL;
|
||||
|
||||
/* must be accompanied by DAT file with SCHl sounds */
|
||||
sf_dat = open_streamfile_by_ext(sf, "dat");
|
||||
if (!sf_dat)
|
||||
goto fail;
|
||||
|
||||
if (read_u32be(0x00, sf_dat) != EA_BLOCKID_HEADER)
|
||||
goto fail;
|
||||
|
||||
num_params = read_u8(0x02, sf) & 0x7F;
|
||||
num_sounds = read_u8(0x03, sf);
|
||||
offset_mult = read_u8(0x09, sf) * 0x0100 + 0x0100;
|
||||
|
||||
if (read_u8(0x08, sf) > num_sounds)
|
||||
goto fail;
|
||||
|
||||
dat_size = get_streamfile_size(sf_dat);
|
||||
if (read_u16le(0x0a, sf) * offset_mult > dat_size &&
|
||||
read_u16be(0x0a, sf) * offset_mult > dat_size)
|
||||
goto fail;
|
||||
|
||||
if (target_stream == 0) target_stream = 1;
|
||||
if (target_stream < 0 || num_sounds == 0 || target_stream > num_sounds)
|
||||
goto fail;
|
||||
|
||||
/* offsets are always big endian */
|
||||
sound_offset = read_u16be(0x10 + (0x02 + num_params) * (target_stream - 1), sf) * offset_mult;
|
||||
if (read_u32be(sound_offset, sf_dat) != EA_BLOCKID_HEADER)
|
||||
goto fail;
|
||||
|
||||
vgmstream = load_vgmstream_ea_schl(sf_dat, sound_offset);
|
||||
if (!vgmstream)
|
||||
goto fail;
|
||||
|
||||
if (num_params != 0) {
|
||||
uint8_t val;
|
||||
char buf[8];
|
||||
int i;
|
||||
for (i = 0; i < num_params; i++) {
|
||||
val = read_u8(0x10 + (0x02 + num_params) * (target_stream - 1) + 0x02 + i, sf);
|
||||
snprintf(buf, sizeof(buf), "%u", val);
|
||||
concatn(STREAM_NAME_SIZE, vgmstream->stream_name, buf);
|
||||
if (i != num_params - 1)
|
||||
concatn(STREAM_NAME_SIZE, vgmstream->stream_name, ", ");
|
||||
}
|
||||
}
|
||||
|
||||
vgmstream->num_streams = num_sounds;
|
||||
close_streamfile(sf_dat);
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_streamfile(sf_dat);
|
||||
return NULL;
|
||||
}
|
526
src/meta/ea_schl_map_mpf_mus.c
Normal file
526
src/meta/ea_schl_map_mpf_mus.c
Normal file
@ -0,0 +1,526 @@
|
||||
#include "meta.h"
|
||||
#include "../util/endianness.h"
|
||||
#include "../layout/layout.h"
|
||||
#include "../util/companion_files.h"
|
||||
|
||||
#define EA_BLOCKID_HEADER 0x5343486C /* "SCHl" */
|
||||
|
||||
#define EA_BNK_HEADER_LE 0x424E4B6C /* "BNKl" */
|
||||
#define EA_BNK_HEADER_BE 0x424E4B62 /* "BNKb" */
|
||||
|
||||
static VGMSTREAM* init_vgmstream_ea_mpf_mus_schl_main(STREAMFILE* sf, const char* mus_name);
|
||||
static STREAMFILE* open_mapfile_pair(STREAMFILE* sf, int track /*, int num_tracks*/);
|
||||
|
||||
/* EA MAP/MUS combo - used in older games for interactive music (for EA's PathFinder tool) */
|
||||
/* seen in Need for Speed II, Need for Speed III: Hot Pursuit, SSX */
|
||||
VGMSTREAM* init_vgmstream_ea_map_mus(STREAMFILE* sf) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
STREAMFILE* sf_mus = NULL;
|
||||
uint32_t schl_offset;
|
||||
uint8_t version, num_sounds, num_events, num_sections;
|
||||
off_t section_offset;
|
||||
int target_stream = sf->stream_index;
|
||||
|
||||
/* check extension */
|
||||
if (!check_extensions(sf, "map,lin,mpf"))
|
||||
return NULL;
|
||||
|
||||
/* always big endian */
|
||||
if (!is_id32be(0x00, sf, "PFDx"))
|
||||
return NULL;
|
||||
|
||||
version = read_u8(0x04, sf);
|
||||
if (version > 1) goto fail;
|
||||
|
||||
sf_mus = open_mapfile_pair(sf, 0); //, 1
|
||||
if (!sf_mus) goto fail;
|
||||
|
||||
/*
|
||||
* 0x04: version
|
||||
* 0x05: starting node
|
||||
* 0x06: number of nodes
|
||||
* 0x07: number of events
|
||||
* 0x08: three zeroes
|
||||
* 0x0b: number of sections
|
||||
* 0x0c: data start
|
||||
*/
|
||||
num_sounds = read_u8(0x06, sf);
|
||||
num_events = read_u8(0x07, sf);
|
||||
num_sections = read_u8(0x0b, sf);
|
||||
section_offset = 0x0c;
|
||||
|
||||
/* section 1: nodes, contains information about segment playback order */
|
||||
section_offset += num_sounds * 0x1c;
|
||||
|
||||
/* section 2: events, specific to game and track */
|
||||
section_offset += num_events * num_sections;
|
||||
|
||||
if (target_stream == 0) target_stream = 1;
|
||||
if (target_stream < 0 || num_sounds == 0 || target_stream > num_sounds)
|
||||
goto fail;
|
||||
|
||||
/* section 3: samples */
|
||||
schl_offset = read_u32be(section_offset + (target_stream - 1) * 0x04, sf);
|
||||
if (read_u32be(schl_offset, sf_mus) != EA_BLOCKID_HEADER)
|
||||
goto fail;
|
||||
|
||||
vgmstream = load_vgmstream_ea_schl(sf_mus, schl_offset);
|
||||
if (!vgmstream)
|
||||
goto fail;
|
||||
|
||||
vgmstream->num_streams = num_sounds;
|
||||
get_streamfile_filename(sf_mus, vgmstream->stream_name, STREAM_NAME_SIZE);
|
||||
close_streamfile(sf_mus);
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_streamfile(sf_mus);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* .MPF - Standard EA MPF+MUS */
|
||||
VGMSTREAM* init_vgmstream_ea_mpf_mus_schl(STREAMFILE* sf) {
|
||||
if (!check_extensions(sf, "mpf"))
|
||||
return NULL;
|
||||
return init_vgmstream_ea_mpf_mus_schl_main(sf, NULL);
|
||||
}
|
||||
|
||||
/* .MSB/MSX - EA Redwood Shores (MSB/MSX)+MUS [007: From Russia with Love, The Godfather (PC/PS2/Xbox/Wii)] */
|
||||
VGMSTREAM* init_vgmstream_ea_msb_mus_schl(STREAMFILE* sf) {
|
||||
/* container with MPF, extra info, and a pre-defined MUS filename */
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
STREAMFILE* sf_mpf = NULL;
|
||||
char mus_name[0x20 + 1];
|
||||
size_t header_size;
|
||||
off_t info_offset, mus_name_offset;
|
||||
read_u32_t read_u32;
|
||||
|
||||
if (read_u64be(0x00, sf) != 0)
|
||||
return NULL;
|
||||
if (!check_extensions(sf, "msb,msx"))
|
||||
return NULL;
|
||||
|
||||
header_size = 0x50;
|
||||
mus_name_offset = 0x30;
|
||||
|
||||
/* 0x08: buffer size of the pre-defined .mus filename? (always 0x20)
|
||||
* 0x20: mpf version number? (always 0x05)
|
||||
* 0x24: offset to a chunk of plaintext data w/ event and node info & names
|
||||
* 0x2C: some hash?
|
||||
* 0x30: intended .mus filename */
|
||||
read_u32 = guess_read_u32(0x08, sf);
|
||||
|
||||
/* extra checks to fail faster before streamfile'ing */
|
||||
if (read_u32(0x08, sf) != 0x20)
|
||||
return NULL;
|
||||
if (read_u32(0x20, sf) != 0x05)
|
||||
return NULL;
|
||||
|
||||
/* not exactly the same as mpf size since it's aligned, but correct size is only needed for v3 */
|
||||
info_offset = read_u32(0x24, sf); //+ header_size;
|
||||
read_string(mus_name, sizeof(mus_name), mus_name_offset, sf);
|
||||
|
||||
sf_mpf = open_wrap_streamfile(sf);
|
||||
sf_mpf = open_clamp_streamfile(sf_mpf, header_size, info_offset);
|
||||
if (!sf_mpf) goto fail;
|
||||
|
||||
vgmstream = init_vgmstream_ea_mpf_mus_schl_main(sf_mpf, mus_name);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
close_streamfile(sf_mpf);
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_vgmstream(vgmstream);
|
||||
close_streamfile(sf_mpf);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* EA MPF/MUS combo - used in 6th gen games for interactive music (for EA's PathFinder tool) */
|
||||
static VGMSTREAM* init_vgmstream_ea_mpf_mus_schl_main(STREAMFILE* sf, const char* mus_name) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
STREAMFILE* sf_mus = NULL;
|
||||
segmented_layout_data* data_s = NULL;
|
||||
uint32_t tracks_table, tracks_data, samples_table = 0, section_offset, entry_offset = 0, eof_offset = 0, sound_offset,
|
||||
off_mult = 0, track_start, track_end = 0, track_checksum = 0;
|
||||
uint16_t num_nodes, num_subbanks = 0;
|
||||
uint8_t version, sub_version, num_tracks, num_sections, num_events, num_routers, num_vars, subentry_num = 0;
|
||||
int i;
|
||||
int target_stream = sf->stream_index, total_streams, big_endian, is_ram = 0;
|
||||
read_u32_t read_u32;
|
||||
read_u16_t read_u16;
|
||||
|
||||
/* detect endianness */
|
||||
if (is_id32be(0x00, sf, "PFDx")) {
|
||||
read_u32 = read_u32be;
|
||||
read_u16 = read_u16be;
|
||||
big_endian = 1;
|
||||
}
|
||||
else if (is_id32le(0x00, sf, "PFDx")) {
|
||||
read_u32 = read_u32le;
|
||||
read_u16 = read_u16le;
|
||||
big_endian = 0;
|
||||
}
|
||||
else {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
version = read_u8(0x04, sf);
|
||||
sub_version = read_u8(0x05, sf);
|
||||
|
||||
if (version < 3 || version > 5) goto fail;
|
||||
if (version == 5 && sub_version > 3) goto fail;
|
||||
|
||||
num_tracks = read_u8(0x0d, sf);
|
||||
num_sections = read_u8(0x0e, sf);
|
||||
num_events = read_u8(0x0f, sf);
|
||||
num_routers = read_u8(0x10, sf);
|
||||
num_vars = read_u8(0x11, sf);
|
||||
num_nodes = read_u16(0x12, sf);
|
||||
|
||||
/* Some structs here use C bitfields which are different on LE and BE AND their
|
||||
* implementation is compiler dependent, fun times.
|
||||
* Earlier versions don't have section offsets so we have to go through all of them
|
||||
* to get to the samples table. */
|
||||
|
||||
if (target_stream == 0) target_stream = 1;
|
||||
|
||||
if (version == 3 && (sub_version == 1 || sub_version == 2))
|
||||
/* SSX Tricky, Sled Storm */ {
|
||||
section_offset = 0x24;
|
||||
entry_offset = read_u16(section_offset + (num_nodes - 1) * 0x02, sf) * 0x04;
|
||||
subentry_num = read_u8(entry_offset + 0x0b, sf);
|
||||
section_offset = entry_offset + 0x0c + subentry_num * 0x04;
|
||||
|
||||
section_offset += align_size_to_block(num_events * num_tracks * num_sections, 0x04);
|
||||
section_offset += num_routers * 0x04;
|
||||
section_offset += num_vars * 0x04;
|
||||
|
||||
tracks_table = read_u32(section_offset, sf) * 0x04;
|
||||
samples_table = tracks_table + num_tracks * 0x04;
|
||||
eof_offset = get_streamfile_size(sf);
|
||||
total_streams = (eof_offset - samples_table) / 0x08;
|
||||
off_mult = 0x04;
|
||||
|
||||
track_start = total_streams;
|
||||
|
||||
for (i = num_tracks - 1; i >= 0; i--) {
|
||||
track_end = track_start;
|
||||
track_start = read_u32(tracks_table + i * 0x04, sf) * 0x04;
|
||||
track_start = (track_start - samples_table) / 0x08;
|
||||
if (track_start <= target_stream - 1)
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (version == 3 && sub_version == 4)
|
||||
/* Harry Potter and the Chamber of Secrets, Shox */ {
|
||||
section_offset = 0x24;
|
||||
entry_offset = read_u16(section_offset + (num_nodes - 1) * 0x02, sf) * 0x04;
|
||||
if (big_endian) {
|
||||
subentry_num = (read_u32be(entry_offset + 0x04, sf) >> 19) & 0x1F;
|
||||
} else {
|
||||
subentry_num = (read_u32be(entry_offset + 0x04, sf) >> 16) & 0x1F;
|
||||
}
|
||||
section_offset = entry_offset + 0x0c + subentry_num * 0x04;
|
||||
|
||||
section_offset += align_size_to_block(num_events * num_tracks * num_sections, 0x04);
|
||||
section_offset += num_routers * 0x04;
|
||||
section_offset += num_vars * 0x04;
|
||||
|
||||
tracks_table = read_u32(section_offset, sf) * 0x04;
|
||||
samples_table = tracks_table + (num_tracks + 1) * 0x04;
|
||||
eof_offset = read_u32(tracks_table + num_tracks * 0x04, sf) * 0x04;
|
||||
total_streams = (eof_offset - samples_table) / 0x08;
|
||||
off_mult = 0x04;
|
||||
|
||||
track_start = total_streams;
|
||||
|
||||
for (i = num_tracks - 1; i >= 0; i--) {
|
||||
track_end = track_start;
|
||||
track_start = read_u32(tracks_table + i * 0x04, sf) * 0x04;
|
||||
track_start = (track_start - samples_table) / 0x08;
|
||||
if (track_start <= target_stream - 1)
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (version == 4) {
|
||||
/* Need for Speed: Underground 2, SSX 3, Harry Potter and the Prisoner of Azkaban */
|
||||
section_offset = 0x20;
|
||||
entry_offset = read_u16(section_offset + (num_nodes - 1) * 0x02, sf) * 0x04;
|
||||
if (big_endian) {
|
||||
subentry_num = (read_u32be(entry_offset + 0x04, sf) >> 15) & 0x0F;
|
||||
} else {
|
||||
subentry_num = (read_u32be(entry_offset + 0x04, sf) >> 20) & 0x0F;
|
||||
}
|
||||
section_offset = entry_offset + 0x10 + subentry_num * 0x04;
|
||||
|
||||
entry_offset = read_u16(section_offset + (num_events - 1) * 0x02, sf) * 0x04;
|
||||
if (big_endian) {
|
||||
subentry_num = (read_u32be(entry_offset + 0x0c, sf) >> 10) & 0x3F;
|
||||
} else {
|
||||
subentry_num = (read_u32be(entry_offset + 0x0c, sf) >> 8) & 0x3F;
|
||||
}
|
||||
section_offset = entry_offset + 0x10 + subentry_num * 0x10;
|
||||
|
||||
section_offset += num_routers * 0x04;
|
||||
section_offset = read_u32(section_offset, sf) * 0x04;
|
||||
|
||||
tracks_table = section_offset;
|
||||
samples_table = tracks_table + (num_tracks + 1) * 0x04;
|
||||
eof_offset = read_u32(tracks_table + num_tracks * 0x04, sf) * 0x04;
|
||||
total_streams = (eof_offset - samples_table) / 0x08;
|
||||
off_mult = 0x80;
|
||||
|
||||
track_start = total_streams;
|
||||
|
||||
for (i = num_tracks - 1; i >= 0; i--) {
|
||||
track_end = track_start;
|
||||
track_start = read_u32(tracks_table + i * 0x04, sf) * 0x04;
|
||||
track_start = (track_start - samples_table) / 0x08;
|
||||
if (track_start <= target_stream - 1)
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (version == 5) {
|
||||
/* Need for Speed: Most Wanted, Need for Speed: Carbon, SSX on Tour */
|
||||
tracks_table = read_u32(0x2c, sf);
|
||||
tracks_data = read_u32(0x30, sf);
|
||||
samples_table = read_u32(0x34, sf);
|
||||
eof_offset = read_u32(0x38, sf);
|
||||
total_streams = (eof_offset - samples_table) / 0x08;
|
||||
off_mult = 0x80;
|
||||
|
||||
/* check to distinguish it from SNR/SNS version (first streamed sample is always at 0x00 or 0x100) */
|
||||
if (read_u16(tracks_data + 0x04, sf) == 0 && read_u32(samples_table + 0x00, sf) > 0x02)
|
||||
goto fail;
|
||||
|
||||
track_start = total_streams;
|
||||
|
||||
for (i = num_tracks - 1; i >= 0; i--) {
|
||||
track_end = track_start;
|
||||
entry_offset = read_u32(tracks_table + i * 0x04, sf) * 0x04;
|
||||
track_start = read_u32(entry_offset + 0x00, sf);
|
||||
|
||||
if (track_start == 0 && i != 0)
|
||||
continue; /* empty track */
|
||||
|
||||
if (track_start <= target_stream - 1) {
|
||||
num_subbanks = read_u16(entry_offset + 0x04, sf);
|
||||
track_checksum = read_u32be(entry_offset + 0x08, sf);
|
||||
is_ram = (num_subbanks != 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (target_stream < 0 || total_streams == 0 || target_stream > total_streams)
|
||||
goto fail;
|
||||
|
||||
/* open MUS file that matches this track */
|
||||
sf_mus = mus_name ? open_streamfile_by_filename(sf, mus_name) : open_mapfile_pair(sf, i); //, num_tracks
|
||||
if (!sf_mus)
|
||||
goto fail;
|
||||
|
||||
if (version < 5) {
|
||||
is_ram = (read_u32be(0x00, sf_mus) == (big_endian ? EA_BNK_HEADER_BE : EA_BNK_HEADER_LE));
|
||||
}
|
||||
|
||||
/* 0x00 - offset/BNK index, 0x04 - duration (in milliseconds) */
|
||||
sound_offset = read_u32(samples_table + (target_stream - 1) * 0x08 + 0x00, sf);
|
||||
|
||||
if (is_ram) {
|
||||
/* for some reason, RAM segments are almost always split into multiple sounds (usually 4) */
|
||||
off_t bnk_offset = version < 5 ? 0x00 : 0x100;
|
||||
uint32_t bnk_sound_index = (sound_offset & 0x0000FFFF);
|
||||
uint32_t bnk_index = (sound_offset & 0xFFFF0000) >> 16;
|
||||
uint32_t next_entry;
|
||||
uint32_t bnk_total_sounds = read_u16(bnk_offset + 0x06, sf_mus);
|
||||
int bnk_segments;
|
||||
|
||||
if (version == 5 && bnk_index != 0) {
|
||||
/* HACK: open proper .mus now since open_mapfile_pair doesn't let us adjust the name */
|
||||
char filename[PATH_LIMIT], basename[PATH_LIMIT], ext[32];
|
||||
int basename_len;
|
||||
STREAMFILE* sf_temp;
|
||||
|
||||
get_streamfile_basename(sf_mus, basename, PATH_LIMIT);
|
||||
basename_len = strlen(basename);
|
||||
get_streamfile_ext(sf_mus, ext, sizeof(ext));
|
||||
|
||||
/* strip off 0 at the end */
|
||||
basename[basename_len - 1] = '\0';
|
||||
|
||||
/* append bank index to the name */
|
||||
snprintf(filename, PATH_LIMIT, "%s%u.%s", basename, bnk_index, ext);
|
||||
|
||||
sf_temp = open_streamfile_by_filename(sf_mus, filename);
|
||||
if (!sf_temp) goto fail;
|
||||
bnk_total_sounds = read_u16(bnk_offset + 0x06, sf_temp);
|
||||
close_streamfile(sf_mus);
|
||||
sf_mus = sf_temp;
|
||||
}
|
||||
|
||||
if (version == 5) {
|
||||
track_checksum = read_u32be(entry_offset + 0x14 + 0x10 * bnk_index, sf);
|
||||
if (track_checksum && read_u32be(0x00, sf_mus) != track_checksum)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (read_u32be(bnk_offset, sf_mus) != (big_endian ? EA_BNK_HEADER_BE : EA_BNK_HEADER_LE))
|
||||
goto fail;
|
||||
|
||||
/* play until the next entry in MPF track or the end of BNK */
|
||||
if (target_stream < track_end) {
|
||||
next_entry = read_u32(samples_table + (target_stream - 0) * 0x08 + 0x00, sf);
|
||||
if (((next_entry & 0xFFFF0000) >> 16) == bnk_index) {
|
||||
bnk_segments = (next_entry & 0x0000FFFF) - bnk_sound_index;
|
||||
} else {
|
||||
bnk_segments = bnk_total_sounds - bnk_sound_index;
|
||||
}
|
||||
} else {
|
||||
bnk_segments = bnk_total_sounds - bnk_sound_index;
|
||||
}
|
||||
|
||||
/* init layout */
|
||||
data_s = init_layout_segmented(bnk_segments);
|
||||
if (!data_s) goto fail;
|
||||
|
||||
for (i = 0; i < bnk_segments; i++) {
|
||||
data_s->segments[i] = load_vgmstream_ea_bnk(sf_mus, bnk_offset, bnk_sound_index + i, 1);
|
||||
if (!data_s->segments[i]) goto fail;
|
||||
}
|
||||
|
||||
/* setup segmented VGMSTREAMs */
|
||||
if (!setup_layout_segmented(data_s)) goto fail;
|
||||
vgmstream = allocate_segmented_vgmstream(data_s, 0, 0, 0);
|
||||
} else {
|
||||
if (version == 5 && track_checksum && read_u32be(0x00, sf_mus) != track_checksum)
|
||||
goto fail;
|
||||
|
||||
sound_offset *= off_mult;
|
||||
if (read_u32be(sound_offset, sf_mus) != EA_BLOCKID_HEADER)
|
||||
goto fail;
|
||||
|
||||
vgmstream = load_vgmstream_ea_schl(sf_mus, sound_offset);
|
||||
}
|
||||
|
||||
if (!vgmstream)
|
||||
goto fail;
|
||||
|
||||
vgmstream->num_streams = total_streams;
|
||||
get_streamfile_filename(sf_mus, vgmstream->stream_name, STREAM_NAME_SIZE);
|
||||
close_streamfile(sf_mus);
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_streamfile(sf_mus);
|
||||
free_layout_segmented(data_s);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* open map/mpf+mus pairs that aren't exact pairs, since EA's games can load any combo */
|
||||
static STREAMFILE* open_mapfile_pair(STREAMFILE* sf, int track /*, int num_tracks*/) {
|
||||
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) */
|
||||
{"AKA_Mus.mpf", "Track.mus"}, /* Boogie */
|
||||
{"SSX4FE.mpf", "TrackFE.mus"}, /* SSX On Tour */
|
||||
{"SSX4Path.mpf", "Track.mus"},
|
||||
{"SSX4.mpf", "moments0.mus,main.mus,load_loop0.mus"}, /* SSX Blur */
|
||||
{"*.mpf", "*_main.mus"}, /* 007: Everything or Nothing */
|
||||
/* EA loads pairs manually, so complex cases needs .txtm to map
|
||||
* NSF2:
|
||||
* - ZTRxxROK.MAP > ZTRxx.TRJ
|
||||
* - ZTRxxTEC.MAP > ZTRxx.TRM
|
||||
* - ZZSHOW.MAP and ZZSHOW2.MAP > ZZSHOW.MUS
|
||||
* NSF3:
|
||||
* - ZTRxxROK.MAP > ZZZTRxxA.TRJ
|
||||
* - ZTRxxTEC.MAP > ZZZTRxxB.TRM
|
||||
* - ZTR00R0A.MAP and ZTR00R0B.MAP > ZZZTR00A.TRJ
|
||||
* SSX 3:
|
||||
* - *.mpf > *.mus,xxloops0.mus
|
||||
*/
|
||||
};
|
||||
STREAMFILE* sf_mus = NULL;
|
||||
char file_name[PATH_LIMIT];
|
||||
int pair_count = (sizeof(mapfile_pairs) / sizeof(mapfile_pairs[0]));
|
||||
int i, j;
|
||||
size_t file_len, map_len;
|
||||
|
||||
/* try parsing TXTM if present */
|
||||
sf_mus = read_filemap_file(sf, track);
|
||||
if (sf_mus) return sf_mus;
|
||||
|
||||
/* if loading the first track, try opening MUS with the same name first (most common scenario) */
|
||||
if (track == 0) {
|
||||
sf_mus = open_streamfile_by_ext(sf, "mus");
|
||||
if (sf_mus) return sf_mus;
|
||||
}
|
||||
|
||||
get_streamfile_filename(sf, 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];
|
||||
char buf[PATH_LIMIT] = { 0 };
|
||||
char* pch;
|
||||
int use_mask = 0;
|
||||
map_len = strlen(map_name);
|
||||
|
||||
/* replace map_name with expected mus_name */
|
||||
if (file_len < map_len)
|
||||
continue;
|
||||
|
||||
if (map_name[0] == '*') {
|
||||
use_mask = 1;
|
||||
map_name++;
|
||||
map_len--;
|
||||
|
||||
if (strcmp(file_name + (file_len - map_len), map_name) != 0)
|
||||
continue;
|
||||
} else {
|
||||
if (strcmp(file_name, map_name) != 0)
|
||||
continue;
|
||||
}
|
||||
|
||||
strncpy(buf, mus_name, PATH_LIMIT - 1);
|
||||
pch = strtok(buf, ","); //TODO: not thread safe in std C
|
||||
for (j = 0; j < track && pch; j++) {
|
||||
pch = strtok(NULL, ",");
|
||||
}
|
||||
if (!pch) continue; /* invalid track */
|
||||
|
||||
if (use_mask) {
|
||||
file_name[file_len - map_len] = '\0';
|
||||
strncat(file_name, pch + 1, PATH_LIMIT - 1);
|
||||
} else {
|
||||
strncpy(file_name, pch, PATH_LIMIT - 1);
|
||||
}
|
||||
|
||||
sf_mus = open_streamfile_by_filename(sf, file_name);
|
||||
if (sf_mus) return sf_mus;
|
||||
|
||||
get_streamfile_filename(sf, file_name, PATH_LIMIT); /* reset for next loop */
|
||||
}
|
||||
|
||||
/* 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 II (PS1), Need for Speed III (PS1)] */
|
||||
{
|
||||
char* mod_name = strchr(file_name, '+');
|
||||
if (mod_name)
|
||||
{
|
||||
mod_name[0] = '\0';
|
||||
sf_mus = open_streamfile_by_filename(sf, file_name);
|
||||
if (sf_mus) return sf_mus;
|
||||
}
|
||||
}
|
||||
|
||||
vgm_logi("EA MPF: .mus file not found (find and put together)\n");
|
||||
return NULL;
|
||||
}
|
185
src/meta/ea_schl_standard.c
Normal file
185
src/meta/ea_schl_standard.c
Normal file
@ -0,0 +1,185 @@
|
||||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
#include "../util/endianness.h"
|
||||
|
||||
#define EA_BLOCKID_HEADER 0x5343486C /* "SCHl" */
|
||||
|
||||
#define EA_BLOCKID_LOC_HEADER 0x53480000 /* "SH" */
|
||||
|
||||
#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 */
|
||||
|
||||
/* 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* sf) {
|
||||
|
||||
/* check extension */
|
||||
/* they don't seem enforced by EA's tools but usually:
|
||||
* .asf: ~early (audio stream file?) [ex. Need for Speed II (PC)]
|
||||
* .lasf: fake for plugins
|
||||
* .str: ~early [ex. FIFA 98 (PS1), FIFA 2002 (PS1)]
|
||||
* .chk: ~early [ex. NBA Live 98 (PS1)]
|
||||
* .eam: ~mid?
|
||||
* .exa: ~mid [ex. 007 - From Russia with Love]
|
||||
* .sng: ~late (FIFA games)
|
||||
* .aud: ~late [ex. FIFA 14 (3DS)]
|
||||
* .strm: MySims Kingdom (Wii)
|
||||
* .stm: FIFA 12 (3DS)
|
||||
* .sx: FIFA 98 (SAT)
|
||||
* .xa: ?
|
||||
* .hab: GoldenEye - Rogue Agent (inside .big)
|
||||
* .xsf: 007 - Agent Under Fire (Xbox)
|
||||
* .gsf: 007 - Everything or Nothing (GC)
|
||||
* (extensionless): SSX (PS2) (inside .big)
|
||||
* .r: The Sims 2: Pets (PSP) (not l/r, shorter "res") */
|
||||
if (!check_extensions(sf, "asf,lasf,str,chk,eam,exa,sng,aud,sx,xa,strm,stm,hab,xsf,gsf,,r"))
|
||||
return NULL;
|
||||
|
||||
/* check header */
|
||||
if (read_u32be(0x00, sf) != EA_BLOCKID_HEADER && /* "SCHl" */
|
||||
read_u32be(0x00, sf) != (EA_BLOCKID_LOC_HEADER | EA_BLOCKID_LOC_EN) && /* "SHEN" */
|
||||
read_u32be(0x00, sf) != (EA_BLOCKID_LOC_HEADER | EA_BLOCKID_LOC_FR) && /* "SHFR" */
|
||||
read_u32be(0x00, sf) != (EA_BLOCKID_LOC_HEADER | EA_BLOCKID_LOC_GE) && /* "SHGE" */
|
||||
read_u32be(0x00, sf) != (EA_BLOCKID_LOC_HEADER | EA_BLOCKID_LOC_DE) && /* "SHDE" */
|
||||
read_u32be(0x00, sf) != (EA_BLOCKID_LOC_HEADER | EA_BLOCKID_LOC_IT) && /* "SHIT" */
|
||||
read_u32be(0x00, sf) != (EA_BLOCKID_LOC_HEADER | EA_BLOCKID_LOC_SP) && /* "SHSP" */
|
||||
read_u32be(0x00, sf) != (EA_BLOCKID_LOC_HEADER | EA_BLOCKID_LOC_ES) && /* "SHES" */
|
||||
read_u32be(0x00, sf) != (EA_BLOCKID_LOC_HEADER | EA_BLOCKID_LOC_MX) && /* "SHMX" */
|
||||
read_u32be(0x00, sf) != (EA_BLOCKID_LOC_HEADER | EA_BLOCKID_LOC_RU) && /* "SHRU" */
|
||||
read_u32be(0x00, sf) != (EA_BLOCKID_LOC_HEADER | EA_BLOCKID_LOC_JA) && /* "SHJA" */
|
||||
read_u32be(0x00, sf) != (EA_BLOCKID_LOC_HEADER | EA_BLOCKID_LOC_JP) && /* "SHJP" */
|
||||
read_u32be(0x00, sf) != (EA_BLOCKID_LOC_HEADER | EA_BLOCKID_LOC_PL) && /* "SHPL" */
|
||||
read_u32be(0x00, sf) != (EA_BLOCKID_LOC_HEADER | EA_BLOCKID_LOC_BR)) /* "SHBR" */
|
||||
return NULL;
|
||||
|
||||
/* Stream is divided into blocks/chunks: SCHl=audio header, SCCl=count of SCDl, SCDl=data xN, SCLl=loop end, SCEl=end.
|
||||
* Video uses picture blocks (MVhd/MV0K/etc) and sometimes multiaudio blocks (SHxx/SCxx/SDxx/SExx where xx=language).
|
||||
* The number/size is affected by: block rate setting, sample rate, channels, CPU location (SPU/main/DSP/others), etc */
|
||||
return load_vgmstream_ea_schl(sf, 0x00);
|
||||
}
|
||||
|
||||
/* EA BNK with variable header - from EA games SFXs; also created by sx.exe */
|
||||
VGMSTREAM* init_vgmstream_ea_bnk(STREAMFILE* sf) {
|
||||
int target_stream = sf->stream_index;
|
||||
|
||||
/* check extension */
|
||||
/* .bnk: common
|
||||
* .sdt: Harry Potter games, Burnout games (PSP)
|
||||
* .hdt/ldt: Burnout games (PSP)
|
||||
* .abk: GoldenEye - Rogue Agent
|
||||
* .ast: FIFA 2004 (inside .big)
|
||||
* .cat: FIFA 2000 (PC, chant.cat)
|
||||
* (extensionless): The Sims 2 spinoffs (PSP) */
|
||||
if (!check_extensions(sf, "bnk,sdt,hdt,ldt,abk,ast,cat,"))
|
||||
return NULL;
|
||||
|
||||
if (target_stream == 0) target_stream = 1;
|
||||
return load_vgmstream_ea_bnk(sf, 0x00, target_stream - 1, 0);
|
||||
}
|
||||
|
||||
/* EA SCHl inside non-demuxed videos, used in current gen games too */
|
||||
VGMSTREAM* init_vgmstream_ea_schl_video(STREAMFILE* sf) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
off_t offset = 0, start_offset = 0;
|
||||
int blocks_done = 0;
|
||||
int total_subsongs, target_subsong = sf->stream_index;
|
||||
read_u32_t read_u32;
|
||||
|
||||
|
||||
/* checks */
|
||||
/* .uv: early */
|
||||
/* .dct: early-mid [ex. Need for Speed II SE (PC), FIFA 98 (PC)] */
|
||||
/* .wve: early-mid [Madden NFL 99 (PC)] */
|
||||
/* .mad: mid */
|
||||
/* .vp6: late */
|
||||
/* .mpc: SSX Tricky (PS2) */
|
||||
if (is_id32be(0x00, sf, "SCHl")) {
|
||||
if (!check_extensions(sf, "uv,dct,mpc,lmpc,vp6"))
|
||||
return NULL;
|
||||
}
|
||||
else if (is_id32be(0x00, sf, "MADk")) {
|
||||
if (!check_extensions(sf, "mad,wve"))
|
||||
return NULL;
|
||||
}
|
||||
else if (is_id32be(0x00, sf, "MVhd")) {
|
||||
if (!check_extensions(sf, "vp6"))
|
||||
return NULL;
|
||||
}
|
||||
else if (is_id32be(0x00, sf, "MPCh")) {
|
||||
if (!check_extensions(sf, "mpc,lmpc"))
|
||||
return NULL;
|
||||
}
|
||||
else {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* use block size to check endianness */
|
||||
read_u32 = guess_endian32(0x04, sf) ? read_u32be : read_u32le;
|
||||
|
||||
/* find starting valid header for the parser */
|
||||
while (offset < get_streamfile_size(sf)) {
|
||||
uint32_t block_id = read_u32be(offset + 0x00, sf);
|
||||
uint32_t block_size = read_u32(offset + 0x04, sf);
|
||||
|
||||
/* find "SCHl" or "SHxx" blocks */
|
||||
if ((block_id == EA_BLOCKID_HEADER) || ((block_id & 0xFFFF0000) == EA_BLOCKID_LOC_HEADER)) {
|
||||
start_offset = offset;
|
||||
break;
|
||||
}
|
||||
|
||||
if (block_size == 0xFFFFFFFF)
|
||||
goto fail;
|
||||
if (blocks_done > 10)
|
||||
goto fail; /* unlikely to contain music */
|
||||
|
||||
blocks_done++;
|
||||
offset += block_size;
|
||||
}
|
||||
|
||||
if (offset >= get_streamfile_size(sf))
|
||||
goto fail;
|
||||
|
||||
/* find target subsong (one per each SHxx multilang block) */
|
||||
total_subsongs = 1;
|
||||
if (target_subsong == 0) target_subsong = 1;
|
||||
offset = start_offset;
|
||||
while (offset < get_streamfile_size(sf)) {
|
||||
uint32_t block_id = read_u32be(offset + 0x00, sf);
|
||||
uint32_t block_size = read_u32(offset + 0x04, sf);
|
||||
|
||||
/* no more subsongs (assumes all SHxx headers go together) */
|
||||
if (((block_id & 0xFFFF0000) != EA_BLOCKID_LOC_HEADER)) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (target_subsong == total_subsongs) {
|
||||
start_offset = offset;
|
||||
/* keep counting subsongs */
|
||||
}
|
||||
|
||||
total_subsongs++;
|
||||
offset += block_size;
|
||||
}
|
||||
|
||||
if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail;
|
||||
|
||||
vgmstream = load_vgmstream_ea_schl(sf, start_offset);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->num_streams = total_subsongs;
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
@ -603,13 +603,15 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE * streamFile);
|
||||
VGMSTREAM * init_vgmstream_ea_schl(STREAMFILE *streamFile);
|
||||
VGMSTREAM * init_vgmstream_ea_schl_video(STREAMFILE *streamFile);
|
||||
VGMSTREAM * init_vgmstream_ea_bnk(STREAMFILE * streamFile);
|
||||
VGMSTREAM * init_vgmstream_ea_abk(STREAMFILE * streamFile);
|
||||
VGMSTREAM * init_vgmstream_ea_abk_schl(STREAMFILE * streamFile);
|
||||
VGMSTREAM * init_vgmstream_ea_amb_schl(STREAMFILE * streamFile);
|
||||
VGMSTREAM * init_vgmstream_ea_hdr_dat(STREAMFILE * streamFile);
|
||||
VGMSTREAM * init_vgmstream_ea_hdr_dat_v2(STREAMFILE * streamFile);
|
||||
VGMSTREAM * init_vgmstream_ea_map_mus(STREAMFILE * streamFile);
|
||||
VGMSTREAM * init_vgmstream_ea_mpf_mus(STREAMFILE * streamFile);
|
||||
VGMSTREAM * init_vgmstream_ea_msb_mus(STREAMFILE * streamFile);
|
||||
VGMSTREAM * init_vgmstream_ea_mpf_mus_schl(STREAMFILE * streamFile);
|
||||
VGMSTREAM * init_vgmstream_ea_msb_mus_schl(STREAMFILE * streamFile);
|
||||
VGMSTREAM* load_vgmstream_ea_bnk(STREAMFILE* sf, off_t offset, int target_stream, int is_embedded);
|
||||
VGMSTREAM* load_vgmstream_ea_schl(STREAMFILE* sf, off_t offset);
|
||||
|
||||
VGMSTREAM * init_vgmstream_ea_schl_fixed(STREAMFILE * streamFile);
|
||||
|
||||
@ -652,6 +654,7 @@ VGMSTREAM * init_vgmstream_ea_snu(STREAMFILE * streamFile);
|
||||
VGMSTREAM * init_vgmstream_ea_snr_sns(STREAMFILE * streamFile);
|
||||
VGMSTREAM * init_vgmstream_ea_sps(STREAMFILE * streamFile);
|
||||
VGMSTREAM * init_vgmstream_ea_abk_eaac(STREAMFILE * streamFile);
|
||||
VGMSTREAM * init_vgmstream_ea_amb_eaac(STREAMFILE * streamFile);
|
||||
VGMSTREAM * init_vgmstream_ea_hdr_sth_dat(STREAMFILE * streamFile);
|
||||
VGMSTREAM * init_vgmstream_ea_mpf_mus_eaac(STREAMFILE * streamFile);
|
||||
VGMSTREAM * init_vgmstream_ea_msb_mus_eaac(STREAMFILE * streamFile);
|
||||
|
@ -298,6 +298,15 @@ VGMSTREAM* init_vgmstream_vag(STREAMFILE* sf) {
|
||||
loop_start_sample = 0;
|
||||
loop_end_sample = ps_bytes_to_samples(channel_size,1);
|
||||
}
|
||||
else if (version == 0x00000020 && channel_size == file_size + 0x10) {
|
||||
/* THQ Australia [Jimmy Neutron: Attack of the Twonkies, SpongeBob: Lights, Camera, Pants!] */
|
||||
start_offset = 0x30;
|
||||
interleave = 0;
|
||||
channels = 1;
|
||||
|
||||
channel_size -= 0x40;
|
||||
loop_flag = ps_find_loop_offsets(sf, start_offset, channel_size, channels, interleave, &loop_start_sample, &loop_end_sample);
|
||||
}
|
||||
else {
|
||||
/* standard PS1/PS2/PS3 .vag [Ecco the Dolphin (PS2), Legasista (PS3)] */
|
||||
start_offset = 0x30;
|
||||
|
@ -290,12 +290,13 @@ init_vgmstream_t init_vgmstream_functions[] = {
|
||||
init_vgmstream_xa_xa30,
|
||||
init_vgmstream_xa_04sw,
|
||||
init_vgmstream_ea_bnk,
|
||||
init_vgmstream_ea_abk,
|
||||
init_vgmstream_ea_abk_schl,
|
||||
init_vgmstream_ea_amb_schl,
|
||||
init_vgmstream_ea_hdr_dat,
|
||||
init_vgmstream_ea_hdr_dat_v2,
|
||||
init_vgmstream_ea_map_mus,
|
||||
init_vgmstream_ea_mpf_mus,
|
||||
init_vgmstream_ea_msb_mus,
|
||||
init_vgmstream_ea_mpf_mus_schl,
|
||||
init_vgmstream_ea_msb_mus_schl,
|
||||
init_vgmstream_ea_schl_fixed,
|
||||
init_vgmstream_sk_aud,
|
||||
init_vgmstream_stma,
|
||||
@ -321,6 +322,7 @@ init_vgmstream_t init_vgmstream_functions[] = {
|
||||
init_vgmstream_ea_snr_sns,
|
||||
init_vgmstream_ea_sps,
|
||||
init_vgmstream_ea_abk_eaac,
|
||||
init_vgmstream_ea_amb_eaac,
|
||||
init_vgmstream_ea_hdr_sth_dat,
|
||||
init_vgmstream_ea_mpf_mus_eaac,
|
||||
init_vgmstream_ea_msb_mus_eaac,
|
||||
|
Loading…
x
Reference in New Issue
Block a user