mirror of
https://github.com/vgmstream/vgmstream.git
synced 2024-12-03 18:47:20 +01:00
commit
471dadb43f
@ -64,12 +64,7 @@
|
|||||||
*/
|
*/
|
||||||
#define DOUBLE_INTERRUPT_TIME 1.0
|
#define DOUBLE_INTERRUPT_TIME 1.0
|
||||||
|
|
||||||
/* TODO: Make sure this whole mess works for big-endian systems
|
#define LITTLE_ENDIAN_OUTPUT 1 /* untested in BE */
|
||||||
*/
|
|
||||||
#define LITTLE_ENDIAN_OUTPUT
|
|
||||||
|
|
||||||
#undef MIN
|
|
||||||
#define MIN(X, Y) ((X) < (Y) ? (X) : (Y))
|
|
||||||
|
|
||||||
|
|
||||||
#define DEFAULT_PARAMS { 0, -1, 2.0, 10.0, 0.0, 0, 0, 0, 0 }
|
#define DEFAULT_PARAMS { 0, -1, 2.0, 10.0, 0.0, 0, 0, 0, 0 }
|
||||||
@ -203,7 +198,7 @@ static int set_sample_format(int channels, int sample_rate) {
|
|||||||
format.channels = channels;
|
format.channels = channels;
|
||||||
format.rate = sample_rate;
|
format.rate = sample_rate;
|
||||||
format.byte_format =
|
format.byte_format =
|
||||||
#ifdef LITTLE_ENDIAN_OUTPUT
|
#if LITTLE_ENDIAN_OUTPUT
|
||||||
AO_FMT_LITTLE
|
AO_FMT_LITTLE
|
||||||
#else
|
#else
|
||||||
AO_FMT_BIG
|
AO_FMT_BIG
|
||||||
@ -397,7 +392,7 @@ static int play_vgmstream(const char *filename, song_settings_t *cfg) {
|
|||||||
|
|
||||||
render_vgmstream(buffer, to_do, vgmstream);
|
render_vgmstream(buffer, to_do, vgmstream);
|
||||||
|
|
||||||
#ifdef LITTLE_ENDIAN_OUTPUT
|
#if LITTLE_ENDIAN_OUTPUT
|
||||||
swap_samples_le(buffer, output_channels * to_do);
|
swap_samples_le(buffer, output_channels * to_do);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
471
src/meta/awb.c
471
src/meta/awb.c
@ -1,235 +1,236 @@
|
|||||||
#include "meta.h"
|
#include "meta.h"
|
||||||
#include "../coding/coding.h"
|
#include "../coding/coding.h"
|
||||||
|
|
||||||
typedef enum { ADX, HCA, VAG, RIFF, CWAV, DSP } awb_type;
|
typedef enum { ADX, HCA, VAG, RIFF, CWAV, DSP } awb_type;
|
||||||
|
|
||||||
static void load_awb_name(STREAMFILE *streamFile, STREAMFILE *acbFile, VGMSTREAM *vgmstream, int waveid);
|
static void load_awb_name(STREAMFILE* sf, STREAMFILE* sf_acb, VGMSTREAM* vgmstream, int waveid);
|
||||||
|
|
||||||
/* AFS2/AWB (Atom Wave Bank) - CRI container of streaming audio, often together with a .acb cue sheet */
|
/* AFS2/AWB (Atom Wave Bank) - CRI container of streaming audio, often together with a .acb cue sheet */
|
||||||
VGMSTREAM * init_vgmstream_awb(STREAMFILE *streamFile) {
|
VGMSTREAM* init_vgmstream_awb(STREAMFILE* sf) {
|
||||||
return init_vgmstream_awb_memory(streamFile, NULL);
|
return init_vgmstream_awb_memory(sf, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
VGMSTREAM * init_vgmstream_awb_memory(STREAMFILE *streamFile, STREAMFILE *acbFile) {
|
VGMSTREAM* init_vgmstream_awb_memory(STREAMFILE* sf, STREAMFILE* sf_acb) {
|
||||||
VGMSTREAM *vgmstream = NULL;
|
VGMSTREAM* vgmstream = NULL;
|
||||||
STREAMFILE *temp_streamFile = NULL;
|
STREAMFILE* temp_sf = NULL;
|
||||||
off_t offset, subfile_offset, subfile_next;
|
off_t offset, subfile_offset, subfile_next;
|
||||||
size_t subfile_size;
|
size_t subfile_size;
|
||||||
int total_subsongs, target_subsong = streamFile->stream_index;
|
int total_subsongs, target_subsong = sf->stream_index;
|
||||||
//uint32_t flags;
|
//uint32_t flags;
|
||||||
uint8_t offset_size;
|
uint8_t offset_size;
|
||||||
uint16_t alignment, subkey;
|
uint16_t alignment, subkey;
|
||||||
awb_type type;
|
awb_type type;
|
||||||
char *extension = NULL;
|
const char* extension = NULL;
|
||||||
int waveid;
|
int waveid;
|
||||||
|
|
||||||
|
|
||||||
/* checks
|
/* checks
|
||||||
* .awb: standard
|
* .awb: standard
|
||||||
* .afs2: sometimes [Okami HD (PS4)] */
|
* .afs2: sometimes [Okami HD (PS4)] */
|
||||||
if (!check_extensions(streamFile, "awb,afs2"))
|
if (!check_extensions(sf, "awb,afs2"))
|
||||||
goto fail;
|
goto fail;
|
||||||
if (read_u32be(0x00,streamFile) != 0x41465332) /* "AFS2" */
|
if (read_u32be(0x00,sf) != 0x41465332) /* "AFS2" */
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
||||||
//flags = read_32bitLE(0x08,streamFile);
|
//flags = read_32bitLE(0x08,sf);
|
||||||
/* 0x04(1): version? 0x01=common, 0x02=2018+ (no apparent differences) */
|
/* 0x04(1): version? 0x01=common, 0x02=2018+ (no apparent differences) */
|
||||||
offset_size = read_u8(0x05,streamFile);
|
offset_size = read_u8(0x05,sf);
|
||||||
/* 0x06(2): always 0x0002? */
|
/* 0x06(2): always 0x0002? */
|
||||||
total_subsongs = read_s32le(0x08,streamFile);
|
total_subsongs = read_s32le(0x08,sf);
|
||||||
alignment = read_u16le(0x0c,streamFile);
|
alignment = read_u16le(0x0c,sf);
|
||||||
subkey = read_u16le(0x0e,streamFile);
|
subkey = read_u16le(0x0e,sf);
|
||||||
|
|
||||||
if (target_subsong == 0) target_subsong = 1;
|
if (target_subsong == 0) target_subsong = 1;
|
||||||
if (target_subsong > total_subsongs || total_subsongs <= 0) goto fail;
|
if (target_subsong > total_subsongs || total_subsongs <= 0) goto fail;
|
||||||
|
|
||||||
offset = 0x10;
|
offset = 0x10;
|
||||||
|
|
||||||
/* id(?) table: read target */
|
/* id(?) table: read target */
|
||||||
{
|
{
|
||||||
off_t waveid_offset = offset + (target_subsong-1) * 0x02;
|
off_t waveid_offset = offset + (target_subsong-1) * 0x02;
|
||||||
|
|
||||||
waveid = read_u16le(waveid_offset,streamFile);
|
waveid = read_u16le(waveid_offset,sf);
|
||||||
|
|
||||||
offset += total_subsongs * 0x02;
|
offset += total_subsongs * 0x02;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* offset table: find target */
|
/* offset table: find target */
|
||||||
{
|
{
|
||||||
off_t file_size = get_streamfile_size(streamFile);
|
off_t file_size = get_streamfile_size(sf);
|
||||||
|
|
||||||
/* last sub-offset is always file end, so table entries = total_subsongs+1 */
|
/* last sub-offset is always file end, so table entries = total_subsongs+1 */
|
||||||
offset += (target_subsong-1) * offset_size;
|
offset += (target_subsong-1) * offset_size;
|
||||||
|
|
||||||
switch(offset_size) {
|
switch(offset_size) {
|
||||||
case 0x04: /* common */
|
case 0x04: /* common */
|
||||||
subfile_offset = read_u32le(offset+0x00,streamFile);
|
subfile_offset = read_u32le(offset+0x00,sf);
|
||||||
subfile_next = read_u32le(offset+0x04,streamFile);
|
subfile_next = read_u32le(offset+0x04,sf);
|
||||||
break;
|
break;
|
||||||
case 0x02: /* mostly sfx in .acb */
|
case 0x02: /* mostly sfx in .acb */
|
||||||
subfile_offset = read_u16le(offset+0x00,streamFile);
|
subfile_offset = read_u16le(offset+0x00,sf);
|
||||||
subfile_next = read_u16le(offset+0x02,streamFile);
|
subfile_next = read_u16le(offset+0x02,sf);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
VGM_LOG("AWB: unknown offset size\n");
|
VGM_LOG("AWB: unknown offset size\n");
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* offset are absolute but sometimes misaligned (specially first that just points to offset table end) */
|
/* offset are absolute but sometimes misaligned (specially first that just points to offset table end) */
|
||||||
subfile_offset += (subfile_offset % alignment) ?
|
subfile_offset += (subfile_offset % alignment) ?
|
||||||
alignment - (subfile_offset % alignment) : 0;
|
alignment - (subfile_offset % alignment) : 0;
|
||||||
subfile_next += (subfile_next % alignment) && subfile_next < file_size ?
|
subfile_next += (subfile_next % alignment) && subfile_next < file_size ?
|
||||||
alignment - (subfile_next % alignment) : 0;
|
alignment - (subfile_next % alignment) : 0;
|
||||||
subfile_size = subfile_next - subfile_offset;
|
subfile_size = subfile_next - subfile_offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
//;VGM_LOG("AWB: subfile offset=%lx + %x\n", subfile_offset, subfile_size);
|
//;VGM_LOG("AWB: subfile offset=%lx + %x\n", subfile_offset, subfile_size);
|
||||||
|
|
||||||
/* autodetect as there isn't anything, plus can mix types
|
/* autodetect as there isn't anything, plus can mix types
|
||||||
* (waveid<>codec info is usually in the companion .acb) */
|
* (waveid<>codec info is usually in the companion .acb) */
|
||||||
if (read_u16be(subfile_offset, streamFile) == 0x8000) { /* ADX id (type 0) */
|
if (read_u16be(subfile_offset, sf) == 0x8000) { /* ADX id (type 0) */
|
||||||
type = ADX;
|
type = ADX;
|
||||||
extension = "adx";
|
extension = "adx";
|
||||||
}
|
}
|
||||||
else if ((read_u32be(subfile_offset,streamFile) & 0x7f7f7f7f) == 0x48434100) { /* "HCA\0" (type 2=HCA, 6=HCA-MX) */
|
else if ((read_u32be(subfile_offset,sf) & 0x7f7f7f7f) == 0x48434100) { /* "HCA\0" (type 2=HCA, 6=HCA-MX) */
|
||||||
type = HCA;
|
type = HCA;
|
||||||
extension = "hca";
|
extension = "hca";
|
||||||
}
|
}
|
||||||
else if (read_u32be(subfile_offset,streamFile) == 0x56414770) { /* "VAGp" (type 7=VAG, 10=HEVAG) */
|
else if (read_u32be(subfile_offset,sf) == 0x56414770) { /* "VAGp" (type 7=VAG, 10=HEVAG) */
|
||||||
type = VAG;
|
type = VAG;
|
||||||
extension = "vag";
|
extension = "vag";
|
||||||
}
|
}
|
||||||
else if (read_u32be(subfile_offset,streamFile) == 0x52494646) { /* "RIFF" (type 8=ATRAC3, 11=ATRAC9) */
|
else if (read_u32be(subfile_offset,sf) == 0x52494646) { /* "RIFF" (type 8=ATRAC3, 11=ATRAC9) */
|
||||||
type = RIFF;
|
type = RIFF;
|
||||||
extension = "wav";
|
extension = "wav";
|
||||||
}
|
subfile_size = read_u32le(subfile_offset + 0x04,sf) + 0x08; /* rough size, use RIFF's */
|
||||||
else if (read_u32be(subfile_offset,streamFile) == 0x43574156) { /* "CWAV" (type 9) */
|
}
|
||||||
type = CWAV;
|
else if (read_u32be(subfile_offset,sf) == 0x43574156) { /* "CWAV" (type 9) */
|
||||||
extension = "bcwav";
|
type = CWAV;
|
||||||
}
|
extension = "bcwav";
|
||||||
else if (read_u32be(subfile_offset + 0x08,streamFile) >= 8000 &&
|
}
|
||||||
read_u32be(subfile_offset + 0x08,streamFile) <= 48000 &&
|
else if (read_u32be(subfile_offset + 0x08,sf) >= 8000 &&
|
||||||
read_u16be(subfile_offset + 0x0e,streamFile) == 0 &&
|
read_u32be(subfile_offset + 0x08,sf) <= 48000 &&
|
||||||
read_u32be(subfile_offset + 0x18,streamFile) == 2 &&
|
read_u16be(subfile_offset + 0x0e,sf) == 0 &&
|
||||||
read_u32be(subfile_offset + 0x50,streamFile) == 0) { /* probably should call some check function (type 13) */
|
read_u32be(subfile_offset + 0x18,sf) == 2 &&
|
||||||
type = DSP;
|
read_u32be(subfile_offset + 0x50,sf) == 0) { /* probably should call some check function (type 13) */
|
||||||
extension = "dsp";
|
type = DSP;
|
||||||
}
|
extension = "dsp";
|
||||||
else {
|
}
|
||||||
VGM_LOG("AWB: unknown codec\n");
|
else {
|
||||||
goto fail;
|
VGM_LOG("AWB: unknown codec\n");
|
||||||
}
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
temp_streamFile = setup_subfile_streamfile(streamFile, subfile_offset,subfile_size, extension);
|
|
||||||
if (!temp_streamFile) goto fail;
|
temp_sf = setup_subfile_streamfile(sf, subfile_offset, subfile_size, extension);
|
||||||
|
if (!temp_sf) goto fail;
|
||||||
switch(type) {
|
|
||||||
case HCA: /* most common */
|
switch(type) {
|
||||||
vgmstream = init_vgmstream_hca_subkey(temp_streamFile, subkey);
|
case HCA: /* most common */
|
||||||
if (!vgmstream) goto fail;
|
vgmstream = init_vgmstream_hca_subkey(temp_sf, subkey);
|
||||||
break;
|
if (!vgmstream) goto fail;
|
||||||
case ADX: /* Okami HD (PS4) */
|
break;
|
||||||
vgmstream = init_vgmstream_adx(temp_streamFile);
|
case ADX: /* Okami HD (PS4) */
|
||||||
if (!vgmstream) goto fail;
|
vgmstream = init_vgmstream_adx(temp_sf);
|
||||||
break;
|
if (!vgmstream) goto fail;
|
||||||
case VAG: /* Ukiyo no Roushi (Vita) */
|
break;
|
||||||
vgmstream = init_vgmstream_vag(temp_streamFile);
|
case VAG: /* Ukiyo no Roushi (Vita) */
|
||||||
if (!vgmstream) goto fail;
|
vgmstream = init_vgmstream_vag(temp_sf);
|
||||||
break;
|
if (!vgmstream) goto fail;
|
||||||
case RIFF: /* Ukiyo no Roushi (Vita) */
|
break;
|
||||||
vgmstream = init_vgmstream_riff(temp_streamFile);
|
case RIFF: /* Ukiyo no Roushi (Vita) */
|
||||||
if (!vgmstream) goto fail;
|
vgmstream = init_vgmstream_riff(temp_sf);
|
||||||
break;
|
if (!vgmstream) goto fail;
|
||||||
case CWAV: /* Sonic: Lost World (3DS) */
|
break;
|
||||||
vgmstream = init_vgmstream_rwsd(temp_streamFile);
|
case CWAV: /* Sonic: Lost World (3DS) */
|
||||||
if (!vgmstream) goto fail;
|
vgmstream = init_vgmstream_rwsd(temp_sf);
|
||||||
break;
|
if (!vgmstream) goto fail;
|
||||||
case DSP: /* Sonic: Lost World (WiiU) */
|
break;
|
||||||
vgmstream = init_vgmstream_ngc_dsp_std(temp_streamFile);
|
case DSP: /* Sonic: Lost World (WiiU) */
|
||||||
if (!vgmstream) goto fail;
|
vgmstream = init_vgmstream_ngc_dsp_std(temp_sf);
|
||||||
break;
|
if (!vgmstream) goto fail;
|
||||||
default:
|
break;
|
||||||
goto fail;
|
default:
|
||||||
}
|
goto fail;
|
||||||
|
}
|
||||||
vgmstream->num_streams = total_subsongs;
|
|
||||||
|
vgmstream->num_streams = total_subsongs;
|
||||||
/* try to load cue names */
|
|
||||||
load_awb_name(streamFile, acbFile, vgmstream, waveid);
|
/* try to load cue names */
|
||||||
|
load_awb_name(sf, sf_acb, vgmstream, waveid);
|
||||||
close_streamfile(temp_streamFile);
|
|
||||||
return vgmstream;
|
close_streamfile(temp_sf);
|
||||||
|
return vgmstream;
|
||||||
fail:
|
|
||||||
close_streamfile(temp_streamFile);
|
fail:
|
||||||
close_vgmstream(vgmstream);
|
close_streamfile(temp_sf);
|
||||||
return NULL;
|
close_vgmstream(vgmstream);
|
||||||
}
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
static void load_awb_name(STREAMFILE *streamFile, STREAMFILE *acbFile, VGMSTREAM* vgmstream, int waveid) {
|
|
||||||
int is_memory = (acbFile != NULL);
|
static void load_awb_name(STREAMFILE* sf, STREAMFILE* sf_acb, VGMSTREAM* vgmstream, int waveid) {
|
||||||
|
int is_memory = (sf_acb != NULL);
|
||||||
/* .acb is passed when loading memory .awb inside .acb */
|
|
||||||
if (!is_memory) {
|
/* .acb is passed when loading memory .awb inside .acb */
|
||||||
/* load companion .acb using known pairs */ //todo improve, see xsb code
|
if (!is_memory) {
|
||||||
char filename[PATH_LIMIT];
|
/* load companion .acb using known pairs */ //todo improve, see xsb code
|
||||||
int len_name, len_cmp;
|
char filename[PATH_LIMIT];
|
||||||
|
int len_name, len_cmp;
|
||||||
|
|
||||||
/* try (name).awb + (name).awb */
|
|
||||||
acbFile = open_streamfile_by_ext(streamFile, "acb");
|
/* try (name).awb + (name).awb */
|
||||||
|
sf_acb = open_streamfile_by_ext(sf, "acb");
|
||||||
/* try (name)_streamfiles.awb + (name).acb */
|
|
||||||
if (!acbFile) {
|
/* try (name)_streamfiles.awb + (name).acb */
|
||||||
char *cmp = "_streamfiles";
|
if (!sf_acb) {
|
||||||
get_streamfile_basename(streamFile, filename, sizeof(filename));
|
char *cmp = "_streamfiles";
|
||||||
len_name = strlen(filename);
|
get_streamfile_basename(sf, filename, sizeof(filename));
|
||||||
len_cmp = strlen(cmp);
|
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';
|
if (len_name > len_cmp && strcmp(filename + len_name - len_cmp, cmp) == 0) {
|
||||||
strcat(filename, ".acb");
|
filename[len_name - len_cmp] = '\0';
|
||||||
acbFile = open_streamfile_by_filename(streamFile, filename);
|
strcat(filename, ".acb");
|
||||||
}
|
sf_acb = open_streamfile_by_filename(sf, filename);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
/* try (name)_STR.awb + (name).acb */
|
|
||||||
if (!acbFile) {
|
/* try (name)_STR.awb + (name).acb */
|
||||||
char *cmp = "_STR";
|
if (!sf_acb) {
|
||||||
get_streamfile_basename(streamFile, filename, sizeof(filename));
|
char *cmp = "_STR";
|
||||||
len_name = strlen(filename);
|
get_streamfile_basename(sf, filename, sizeof(filename));
|
||||||
len_cmp = strlen(cmp);
|
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';
|
if (len_name > len_cmp && strcmp(filename + len_name - len_cmp, cmp) == 0) {
|
||||||
strcat(filename, ".acb");
|
filename[len_name - len_cmp] = '\0';
|
||||||
acbFile = open_streamfile_by_filename(streamFile, filename);
|
strcat(filename, ".acb");
|
||||||
}
|
sf_acb = open_streamfile_by_filename(sf, filename);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
/* try (name)_(name)_R001.awb + (name).acb [Sengoku Basara Battle Party (Mobile)] */
|
|
||||||
if (!acbFile) {
|
/* try (name)_(name)_R001.awb + (name).acb [Sengoku Basara Battle Party (Mobile)] */
|
||||||
char *cmp = "_R001";
|
if (!sf_acb) {
|
||||||
get_streamfile_basename(streamFile, filename, sizeof(filename));
|
char *cmp = "_R001";
|
||||||
len_name = strlen(filename);
|
get_streamfile_basename(sf, filename, sizeof(filename));
|
||||||
len_cmp = strlen(cmp);
|
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) / 2] = '\0';
|
if (len_name > len_cmp && strcmp(filename + len_name - len_cmp, cmp) == 0) {
|
||||||
strcat(filename, ".acb");
|
filename[(len_name - len_cmp) / 2] = '\0';
|
||||||
VGM_LOG("%s\n", filename);
|
strcat(filename, ".acb");
|
||||||
acbFile = open_streamfile_by_filename(streamFile, filename);
|
VGM_LOG("%s\n", filename);
|
||||||
}
|
sf_acb = open_streamfile_by_filename(sf, filename);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
/* probably loaded */
|
|
||||||
load_acb_wave_name(acbFile, vgmstream, waveid, is_memory);
|
/* probably loaded */
|
||||||
|
load_acb_wave_name(sf_acb, vgmstream, waveid, is_memory);
|
||||||
close_streamfile(acbFile);
|
|
||||||
}
|
close_streamfile(sf_acb);
|
||||||
else {
|
}
|
||||||
load_acb_wave_name(acbFile, vgmstream, waveid, is_memory);
|
else {
|
||||||
}
|
load_acb_wave_name(sf_acb, vgmstream, waveid, is_memory);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user