vgmstream/src/meta/awb.c

214 lines
7.9 KiB
C
Raw Normal View History

2020-08-01 20:16:09 +02:00
#include "meta.h"
#include "../coding/coding.h"
2021-09-15 23:22:17 +02:00
//typedef enum { ADX, HCA, VAG, RIFF, CWAV, DSP, CWAC, M4A } awb_type_t;
2020-08-01 20:16:09 +02:00
2020-08-01 20:22:26 +02:00
static void load_awb_name(STREAMFILE* sf, STREAMFILE* sf_acb, VGMSTREAM* vgmstream, int waveid);
2020-08-01 20:16:09 +02:00
/* AFS2/AWB (Atom Wave Bank) - CRI container of streaming audio, often together with a .acb cue sheet */
VGMSTREAM* init_vgmstream_awb(STREAMFILE* sf) {
return init_vgmstream_awb_memory(sf, NULL);
}
VGMSTREAM* init_vgmstream_awb_memory(STREAMFILE* sf, STREAMFILE* sf_acb) {
VGMSTREAM* vgmstream = NULL;
STREAMFILE* temp_sf = NULL;
2021-09-15 23:22:17 +02:00
uint32_t offset, subfile_offset, subfile_next, subfile_size;
2020-08-01 20:16:09 +02:00
int total_subsongs, target_subsong = sf->stream_index;
uint8_t offset_size;
uint16_t alignment, subkey;
int waveid;
2021-09-15 23:22:17 +02:00
/* checks */
if (!is_id32be(0x00,sf, "AFS2"))
goto fail;
/* .awb: standard
2020-08-01 20:16:09 +02:00
* .afs2: sometimes [Okami HD (PS4)] */
if (!check_extensions(sf, "awb,afs2"))
goto fail;
/* 0x04(1): version? 0x01=common, 0x02=2018+ (no apparent differences) */
offset_size = read_u8(0x05,sf);
/* 0x06(2): always 0x0002? */
total_subsongs = read_s32le(0x08,sf);
alignment = read_u16le(0x0c,sf);
subkey = read_u16le(0x0e,sf);
if (target_subsong == 0) target_subsong = 1;
if (target_subsong > total_subsongs || total_subsongs <= 0) goto fail;
offset = 0x10;
2021-09-15 23:22:17 +02:00
/* id table: read target */
2020-08-01 20:16:09 +02:00
{
2021-09-15 23:22:17 +02:00
uint32_t waveid_offset = offset + (target_subsong-1) * 0x02;
2020-08-01 20:16:09 +02:00
waveid = read_u16le(waveid_offset,sf);
offset += total_subsongs * 0x02;
}
/* offset table: find target */
{
2021-09-15 23:22:17 +02:00
uint32_t file_size = get_streamfile_size(sf);
2020-08-01 20:16:09 +02:00
/* last sub-offset is always file end, so table entries = total_subsongs+1 */
offset += (target_subsong-1) * offset_size;
switch(offset_size) {
case 0x04: /* common */
subfile_offset = read_u32le(offset+0x00,sf);
subfile_next = read_u32le(offset+0x04,sf);
break;
case 0x02: /* mostly sfx in .acb */
subfile_offset = read_u16le(offset+0x00,sf);
subfile_next = read_u16le(offset+0x02,sf);
break;
default:
2021-09-15 23:22:17 +02:00
vgm_logi("AWB: unknown offset size (report)\n");
2020-08-01 20:16:09 +02:00
goto fail;
}
/* offset are absolute but sometimes misaligned (specially first that just points to offset table end) */
subfile_offset += (subfile_offset % alignment) ?
alignment - (subfile_offset % alignment) : 0;
subfile_next += (subfile_next % alignment) && subfile_next < file_size ?
alignment - (subfile_next % alignment) : 0;
subfile_size = subfile_next - subfile_offset;
}
2021-09-15 23:22:17 +02:00
//;VGM_LOG("awb: subfile offset=%x + %x\n", subfile_offset, subfile_size);
2020-08-01 20:16:09 +02:00
/* autodetect as there isn't anything, plus can mix types
* (waveid<>codec info is usually in the companion .acb) */
2021-09-15 23:22:17 +02:00
{
VGMSTREAM* (*init_vgmstream)(STREAMFILE* sf) = NULL;
VGMSTREAM* (*init_vgmstream_subkey)(STREAMFILE* sf, uint16_t subkey) = NULL;
const char* extension = NULL;
2020-08-01 20:16:09 +02:00
2021-09-19 23:54:38 +02:00
if (read_u16be(subfile_offset, sf) == 0x8000) { /* (type 0=ADX, also 3?) */
2021-09-15 23:22:17 +02:00
init_vgmstream_subkey = init_vgmstream_adx_subkey; /* Okami HD (PS4) */
extension = "adx";
}
else if ((read_u32be(subfile_offset,sf) & 0x7f7f7f7f) == get_id32be("HCA\0")) { /* (type 2=HCA, 6=HCA-MX) */
init_vgmstream_subkey = init_vgmstream_hca_subkey; /* most common */
extension = "hca";
}
else if (is_id32be(subfile_offset,sf, "VAGp") == 0x56414770) { /* (type 7=VAG, 10=HEVAG) */
init_vgmstream = init_vgmstream_vag; /* Ukiyo no Roushi (Vita) */
extension = "vag";
}
2021-09-19 23:54:38 +02:00
else if (is_id32be(subfile_offset,sf, "RIFF")) { /* (type 8=ATRAC3, 11=ATRAC9, also 18=ATRAC9?) */
2021-09-15 23:22:17 +02:00
init_vgmstream = init_vgmstream_riff; /* Ukiyo no Roushi (Vita) */
extension = "wav";
subfile_size = read_u32le(subfile_offset + 0x04,sf) + 0x08; /* padded size, use RIFF's */
}
else if (is_id32be(subfile_offset,sf, "CWAV")) { /* (type 9=CWAV) */
init_vgmstream = init_vgmstream_rwsd; /* Sonic: Lost World (3DS) */
extension = "bcwav";
}
else if (read_u32be(subfile_offset + 0x08,sf) >= 8000 && read_u32be(subfile_offset + 0x08,sf) <= 48000 &&
read_u16be(subfile_offset + 0x0e,sf) == 0 &&
read_u32be(subfile_offset + 0x18,sf) == 2 &&
2021-09-19 23:54:38 +02:00
read_u32be(subfile_offset + 0x50,sf) == 0) { /* (type 13=DSP, also 4=Wii?, 5=NDS?), probably should call some check function */
2021-09-15 23:22:17 +02:00
init_vgmstream = init_vgmstream_ngc_dsp_std; /* Sonic: Lost World (WiiU) */
extension = "dsp";
}
else if (is_id32be(subfile_offset,sf, "CWAC")) { /* (type 13=DSP, again) */
init_vgmstream = init_vgmstream_dsp_cwac; /* Mario & Sonic at the Rio 2016 Olympic Games (WiiU) */
extension = "dsp";
}
#ifdef VGM_USE_FFMPEG
2021-09-15 23:22:17 +02:00
else if (read_u32be(subfile_offset+0x00,sf) == 0x00000018 && is_id32be(subfile_offset+0x04,sf, "ftyp")) { /* (type 19=M4A) */
init_vgmstream = init_vgmstream_mp4_aac_ffmpeg; /* Imperial SaGa Eclipse (Browser) */
extension = "m4a";
}
#endif
2021-09-19 23:54:38 +02:00
else { /* 12=XMA? */
2021-09-15 23:22:17 +02:00
vgm_logi("AWB: unknown codec (report)\n");
2020-08-01 20:16:09 +02:00
goto fail;
2021-09-15 23:22:17 +02:00
}
2020-08-01 20:16:09 +02:00
2021-09-15 23:22:17 +02:00
temp_sf = setup_subfile_streamfile(sf, subfile_offset, subfile_size, extension);
if (!temp_sf) goto fail;
if (init_vgmstream_subkey)
vgmstream = init_vgmstream_subkey(temp_sf, subkey);
else
vgmstream = init_vgmstream(temp_sf);
if (!vgmstream) goto fail;
vgmstream->num_streams = total_subsongs;
}
2020-08-01 20:16:09 +02:00
/* try to load cue names */
load_awb_name(sf, sf_acb, vgmstream, waveid);
close_streamfile(temp_sf);
return vgmstream;
fail:
close_streamfile(temp_sf);
close_vgmstream(vgmstream);
return NULL;
}
static void load_awb_name(STREAMFILE* sf, STREAMFILE* sf_acb, VGMSTREAM* vgmstream, int waveid) {
int is_memory = (sf_acb != NULL);
int port = 0;
2020-08-01 20:16:09 +02:00
/* .acb is passed when loading memory .awb inside .acb */
if (!is_memory) {
/* load companion .acb using known pairs */ //todo improve, see xsb code
char filename[PATH_LIMIT];
int len_name, len_cmp;
/* try parsing TXTM if present */
sf_acb = read_filemap_file_pos(sf, 0, &port);
2020-08-01 20:16:09 +02:00
/* try (name).awb + (name).awb */
2020-11-09 22:26:07 +01:00
if (!sf_acb) {
sf_acb = open_streamfile_by_ext(sf, "acb");
}
2020-08-01 20:16:09 +02:00
/* try (name)_streamfiles.awb + (name).acb */
if (!sf_acb) {
char *cmp = "_streamfiles";
get_streamfile_basename(sf, filename, sizeof(filename));
len_name = strlen(filename);
len_cmp = strlen(cmp);
if (len_name > len_cmp && strcmp(filename + len_name - len_cmp, cmp) == 0) {
filename[len_name - len_cmp] = '\0';
strcat(filename, ".acb");
sf_acb = open_streamfile_by_filename(sf, filename);
}
}
/* try (name)_STR.awb + (name).acb */
if (!sf_acb) {
char *cmp = "_STR";
get_streamfile_basename(sf, filename, sizeof(filename));
len_name = strlen(filename);
len_cmp = strlen(cmp);
if (len_name > len_cmp && strcmp(filename + len_name - len_cmp, cmp) == 0) {
filename[len_name - len_cmp] = '\0';
strcat(filename, ".acb");
sf_acb = open_streamfile_by_filename(sf, filename);
}
}
2021-09-04 22:19:36 +02:00
/* probably loaded */
load_acb_wave_name(sf_acb, vgmstream, waveid, port, is_memory);
2020-08-01 20:16:09 +02:00
close_streamfile(sf_acb);
}
else {
load_acb_wave_name(sf_acb, vgmstream, waveid, port, is_memory);
2020-08-01 20:16:09 +02:00
}
}