mirror of
https://github.com/vgmstream/vgmstream.git
synced 2025-02-21 04:48:21 +01:00
Show multiple cue names in SQEX .sab
Also allow dummy entries for easier cue<>wave matching
This commit is contained in:
parent
8b4bf139b3
commit
78eafcba70
@ -1,5 +1,6 @@
|
||||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
#include "../util/endianness.h"
|
||||
#include "sqex_streamfile.h"
|
||||
|
||||
|
||||
@ -20,65 +21,67 @@ typedef struct {
|
||||
int sample_rate;
|
||||
int loop_start;
|
||||
int loop_end;
|
||||
off_t mtrl_offset;
|
||||
off_t extradata_offset;
|
||||
size_t extradata_size;
|
||||
size_t stream_size;
|
||||
uint32_t mtrl_offset;
|
||||
uint32_t extradata_offset;
|
||||
uint32_t extradata_size;
|
||||
uint32_t stream_size;
|
||||
uint16_t extradata_id;
|
||||
|
||||
off_t filename_offset;
|
||||
size_t filename_size;
|
||||
off_t muscname_offset;
|
||||
size_t muscname_size;
|
||||
off_t sectname_offset;
|
||||
size_t sectname_size;
|
||||
off_t modename_offset;
|
||||
size_t modename_size;
|
||||
off_t instname_offset;
|
||||
size_t instname_size;
|
||||
off_t sndname_offset;
|
||||
size_t sndname_size;
|
||||
uint32_t filename_offset;
|
||||
uint32_t filename_size;
|
||||
uint32_t muscname_offset;
|
||||
uint32_t muscname_size;
|
||||
uint32_t sectname_offset;
|
||||
uint32_t sectname_size;
|
||||
uint32_t modename_offset;
|
||||
uint32_t modename_size;
|
||||
uint32_t instname_offset;
|
||||
uint32_t instname_size;
|
||||
//uint32_t sndname_offset;
|
||||
//uint32_t sndname_size;
|
||||
|
||||
off_t sections_offset;
|
||||
off_t snd_section_offset;
|
||||
off_t seq_section_offset;
|
||||
off_t trk_section_offset;
|
||||
off_t musc_section_offset;
|
||||
off_t inst_section_offset;
|
||||
off_t mtrl_section_offset;
|
||||
uint32_t sections_offset;
|
||||
uint32_t snd_section_offset;
|
||||
uint32_t seq_section_offset;
|
||||
uint32_t trk_section_offset;
|
||||
uint32_t musc_section_offset;
|
||||
uint32_t inst_section_offset;
|
||||
uint32_t mtrl_section_offset;
|
||||
|
||||
char readable_name[STREAM_NAME_SIZE];
|
||||
|
||||
} sead_header;
|
||||
} sead_header_t;
|
||||
|
||||
static int parse_sead(sead_header *sead, STREAMFILE *sf);
|
||||
static int parse_sead(sead_header_t* sead, STREAMFILE* sf);
|
||||
|
||||
|
||||
/* SABF/MABF - Square Enix's "sead" audio games [Dragon Quest Builders (PS3), Dissidia Opera Omnia (mobile), FF XV (PS4)] */
|
||||
VGMSTREAM* init_vgmstream_sqex_sead(STREAMFILE* sf) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
sead_header sead = {0};
|
||||
sead_header_t sead = {0};
|
||||
off_t start_offset;
|
||||
int target_subsong = sf->stream_index;
|
||||
uint32_t (*read_u32)(off_t,STREAMFILE*) = NULL;
|
||||
uint16_t (*read_u16)(off_t,STREAMFILE*) = NULL;
|
||||
read_u32_t read_u32 = NULL;
|
||||
read_u16_t read_u16 = NULL;
|
||||
|
||||
|
||||
/* checks */
|
||||
if (is_id32be(0x00, sf, "sabf")) {
|
||||
sead.is_sab = 1;
|
||||
}
|
||||
else if (is_id32be(0x00, sf, "mabf")) {
|
||||
sead.is_mab = 1;
|
||||
}
|
||||
else {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* .sab: sound/bgm
|
||||
* .mab: music
|
||||
* .sbin: Dissidia Opera Omnia .sab */
|
||||
if (!check_extensions(sf,"sab,mab,sbin"))
|
||||
goto fail;
|
||||
|
||||
if (read_u32be(0x00, sf) == 0x73616266) { /* "sabf" */
|
||||
sead.is_sab = 1;
|
||||
} else if (read_u32be(0x00, sf) == 0x6D616266) { /* "mabf" */
|
||||
sead.is_mab = 1;
|
||||
} else {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* SEAD handles both sab/mab in the same lib (libsead), and other similar files (config, engine, etc).
|
||||
* Has some chunks pointing to sections, and each section entry (usually starting with section
|
||||
* version/reserved/size) is always padded to 0x10. Most values are unsigned.
|
||||
@ -89,7 +92,7 @@ VGMSTREAM* init_vgmstream_sqex_sead(STREAMFILE* sf) {
|
||||
* or is a separate audio lib derived from this "SEAD Engine". */
|
||||
|
||||
|
||||
sead.big_endian = guess_endianness16bit(0x06, sf); /* no flag, use size */
|
||||
sead.big_endian = guess_endian16(0x06, sf); /* no flag, use size */
|
||||
if (sead.big_endian) {
|
||||
read_u32 = read_u32be;
|
||||
read_u16 = read_u16be;
|
||||
@ -115,6 +118,14 @@ VGMSTREAM* init_vgmstream_sqex_sead(STREAMFILE* sf) {
|
||||
strcpy(vgmstream->stream_name, sead.readable_name);
|
||||
|
||||
switch(sead.codec) {
|
||||
case 0x00: /* NONE */
|
||||
vgmstream->coding_type = coding_SILENCE;
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
||||
vgmstream->num_samples = sead.sample_rate;
|
||||
|
||||
start_offset = 0;
|
||||
break;
|
||||
|
||||
case 0x01: { /* PCM [Chrono Trigger (PC) sfx] */
|
||||
start_offset = sead.extradata_offset + sead.extradata_size;
|
||||
@ -157,7 +168,7 @@ VGMSTREAM* init_vgmstream_sqex_sead(STREAMFILE* sf) {
|
||||
|
||||
#ifdef VGM_USE_VORBIS
|
||||
case 0x03: { /* VORBIS (Ogg subfile) [Final Fantasy XV Benchmark sfx (PC)] */
|
||||
VGMSTREAM *ogg_vgmstream = NULL;
|
||||
VGMSTREAM* ogg_vgmstream = NULL;
|
||||
ogg_vorbis_meta_info_t ovmi = {0};
|
||||
off_t subfile_offset = sead.extradata_offset + sead.extradata_size;
|
||||
|
||||
@ -254,8 +265,8 @@ VGMSTREAM* init_vgmstream_sqex_sead(STREAMFILE* sf) {
|
||||
#endif
|
||||
|
||||
case 0x07: { /* HCA (subfile) [Dissidia Opera Omnia (Mobile), Final Fantaxy XV (PS4)] */
|
||||
VGMSTREAM *temp_vgmstream = NULL;
|
||||
STREAMFILE *temp_sf = NULL;
|
||||
VGMSTREAM* temp_vgmstream = NULL;
|
||||
STREAMFILE* temp_sf = NULL;
|
||||
off_t subfile_offset = sead.extradata_offset + 0x10;
|
||||
size_t subfile_size = sead.stream_size + sead.extradata_size - 0x10;
|
||||
|
||||
@ -299,11 +310,10 @@ VGMSTREAM* init_vgmstream_sqex_sead(STREAMFILE* sf) {
|
||||
}
|
||||
}
|
||||
|
||||
case 0x00: /* NONE / dummy entry */
|
||||
case 0x05: /* XMA2 (extradata may be a XMA2 fmt extra chunk) */
|
||||
case 0x08: /* SWITCHOPUS (no extradata?) */
|
||||
default:
|
||||
VGM_LOG("SQEX SEAD: unknown codec %x\n", sead.codec);
|
||||
vgm_logi("SQEX SEAD: unknown codec %x\n", sead.codec);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
@ -319,54 +329,76 @@ fail:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//todo safeops, avoid recalc lens
|
||||
static void sead_cat(char* dst, int dst_max, const char* src) {
|
||||
int dst_len = strlen(dst);
|
||||
int src_len = strlen(dst);
|
||||
if (dst_len + src_len > dst_max - 1)
|
||||
return;
|
||||
strcat(dst, src);
|
||||
}
|
||||
|
||||
static void build_readable_name(char * buf, size_t buf_size, sead_header *sead, STREAMFILE *sf) {
|
||||
static void build_readable_sab_name(sead_header_t* sead, STREAMFILE* sf, uint32_t sndname_offset, uint32_t sndname_size) {
|
||||
char * buf = sead->readable_name;
|
||||
int buf_size = sizeof(sead->readable_name);
|
||||
char descriptor[255], name[255];
|
||||
|
||||
if (sead->is_sab) {
|
||||
char descriptor[255], name[255];
|
||||
if (sead->filename_size > 255 || sndname_size > 255)
|
||||
goto fail;
|
||||
|
||||
if (sead->filename_size > 255 || sead->sndname_size > 255) goto fail;
|
||||
|
||||
read_string(descriptor,sead->filename_size+1,sead->filename_offset, sf);
|
||||
read_string(name,sead->sndname_size+1,sead->sndname_offset, sf);
|
||||
if (buf[0] == '\0') { /* init */
|
||||
read_string(descriptor,sead->filename_size+1, sead->filename_offset, sf);
|
||||
read_string(name, sndname_size+1, sndname_offset, sf);
|
||||
|
||||
snprintf(buf,buf_size, "%s/%s", descriptor, name);
|
||||
}
|
||||
else {
|
||||
char descriptor[255], name[255], mode[255];
|
||||
else { /* add */
|
||||
read_string(name, sndname_size+1, sndname_offset, sf);
|
||||
|
||||
if (sead->filename_size > 255 || sead->muscname_size > 255 || sead->sectname_size > 255 || sead->modename_size > 255) goto fail;
|
||||
|
||||
read_string(descriptor,sead->filename_size+1,sead->filename_offset, sf);
|
||||
//read_string(filename,sead->muscname_size+1,sead->muscname_offset, sf); /* same as filename, not too interesting */
|
||||
if (sead->sectname_offset)
|
||||
read_string(name,sead->sectname_size+1,sead->sectname_offset, sf);
|
||||
else if (sead->instname_offset)
|
||||
read_string(name,sead->instname_size+1,sead->instname_offset, sf);
|
||||
else
|
||||
strcpy(name, "?");
|
||||
if (sead->modename_offset > 0)
|
||||
read_string(mode,sead->modename_size+1,sead->modename_offset, sf);
|
||||
|
||||
/* default mode in most files */
|
||||
if (sead->modename_offset == 0 || strcmp(mode, "Mode") == 0 || strcmp(mode, "Mode0") == 0)
|
||||
snprintf(buf,buf_size, "%s/%s", descriptor, name);
|
||||
else
|
||||
snprintf(buf,buf_size, "%s/%s/%s", descriptor, name, mode);
|
||||
sead_cat(buf, buf_size, "; ");
|
||||
sead_cat(buf, buf_size, name);
|
||||
}
|
||||
return;
|
||||
fail:
|
||||
VGM_LOG("SEAD: bad sab name found\n");
|
||||
}
|
||||
|
||||
static void build_readable_mab_name(sead_header_t* sead, STREAMFILE* sf) {
|
||||
char * buf = sead->readable_name;
|
||||
int buf_size = sizeof(sead->readable_name);
|
||||
char descriptor[255], name[255], mode[255];
|
||||
|
||||
if (sead->filename_size > 255 || sead->muscname_size > 255 || sead->sectname_size > 255 || sead->modename_size > 255)
|
||||
goto fail;
|
||||
|
||||
read_string(descriptor,sead->filename_size+1,sead->filename_offset, sf);
|
||||
//read_string(filename,sead->muscname_size+1,sead->muscname_offset, sf); /* same as filename, not too interesting */
|
||||
if (sead->sectname_offset)
|
||||
read_string(name,sead->sectname_size+1,sead->sectname_offset, sf);
|
||||
else if (sead->instname_offset)
|
||||
read_string(name,sead->instname_size+1,sead->instname_offset, sf);
|
||||
else
|
||||
strcpy(name, "?");
|
||||
if (sead->modename_offset > 0)
|
||||
read_string(mode,sead->modename_size+1,sead->modename_offset, sf);
|
||||
|
||||
/* default mode in most files */
|
||||
if (sead->modename_offset == 0 || strcmp(mode, "Mode") == 0 || strcmp(mode, "Mode0") == 0)
|
||||
snprintf(buf,buf_size, "%s/%s", descriptor, name);
|
||||
else
|
||||
snprintf(buf,buf_size, "%s/%s/%s", descriptor, name, mode);
|
||||
|
||||
return;
|
||||
fail:
|
||||
VGM_LOG("SEAD: bad name found\n");
|
||||
VGM_LOG("SEAD: bad mab name found\n");
|
||||
}
|
||||
|
||||
static void parse_sead_mab_name(sead_header *sead, STREAMFILE *sf) {
|
||||
uint32_t (*read_u32)(off_t,STREAMFILE*) = sead->big_endian ? read_u32be : read_u32le;
|
||||
uint16_t (*read_u16)(off_t,STREAMFILE*) = sead->big_endian ? read_u16be : read_u16le;
|
||||
static void parse_sead_mab_name(sead_header_t* sead, STREAMFILE* sf) {
|
||||
read_u32_t read_u32 = sead->big_endian ? read_u32be : read_u32le;
|
||||
read_u16_t read_u16 = sead->big_endian ? read_u16be : read_u16le;
|
||||
int i, j, k, entries;
|
||||
off_t target_muscname_offset = 0, target_sectname_offset = 0;
|
||||
size_t target_muscname_size = 0, target_sectname_size = 0;
|
||||
|
||||
uint32_t target_muscname_offset = 0, target_sectname_offset = 0;
|
||||
uint32_t target_muscname_size = 0, target_sectname_size = 0;
|
||||
|
||||
/* find names referencing to our material stream, usually:
|
||||
* - music > sections > layers (<> meters) > material index
|
||||
@ -608,9 +640,9 @@ static void parse_sead_mab_name(sead_header *sead, STREAMFILE *sf) {
|
||||
}
|
||||
}
|
||||
|
||||
static void parse_sead_sab_name(sead_header *sead, STREAMFILE *sf) {
|
||||
uint32_t (*read_u32)(off_t,STREAMFILE*) = sead->big_endian ? read_u32be : read_u32le;
|
||||
uint16_t (*read_u16)(off_t,STREAMFILE*) = sead->big_endian ? read_u16be : read_u16le;
|
||||
static void parse_sead_sab_name(sead_header_t* sead, STREAMFILE* sf) {
|
||||
read_u32_t read_u32 = sead->big_endian ? read_u32be : read_u32le;
|
||||
read_u16_t read_u16 = sead->big_endian ? read_u16be : read_u16le;
|
||||
int i, j, entries;
|
||||
|
||||
/* find names referencing to our material stream, usually:
|
||||
@ -627,9 +659,9 @@ static void parse_sead_sab_name(sead_header *sead, STREAMFILE *sf) {
|
||||
/* parse sounds */
|
||||
entries = read_u16(sead->snd_section_offset + 0x04, sf);
|
||||
for (i = 0; i < entries; i++) {
|
||||
off_t snd_offset = sead->snd_section_offset + read_u32(sead->snd_section_offset + 0x10 + i*0x04, sf);
|
||||
size_t snd_size, sndname_size;
|
||||
off_t seqi_start, seqi_offset, sndname_offset;
|
||||
uint32_t snd_offset = sead->snd_section_offset + read_u32(sead->snd_section_offset + 0x10 + i*0x04, sf);
|
||||
uint32_t snd_size, sndname_size;
|
||||
uint32_t seqi_start, seqi_offset, sndname_offset;
|
||||
int snd_version, seqi_count;
|
||||
|
||||
snd_version = read_u8(snd_offset + 0x00, sf);
|
||||
@ -677,8 +709,8 @@ static void parse_sead_sab_name(sead_header *sead, STREAMFILE *sf) {
|
||||
|
||||
/* parse sequence */
|
||||
{
|
||||
off_t seq_offset = sead->seq_section_offset + read_u32(sead->seq_section_offset + 0x10 + seq_index*0x04, sf);
|
||||
off_t cmnd_start, cmnd_offset;
|
||||
uint32_t seq_offset = sead->seq_section_offset + read_u32(sead->seq_section_offset + 0x10 + seq_index*0x04, sf);
|
||||
uint32_t cmnd_start, cmnd_offset;
|
||||
int seq_version;
|
||||
|
||||
seq_version = read_u8(seq_offset + 0x00, sf);
|
||||
@ -724,7 +756,7 @@ static void parse_sead_sab_name(sead_header *sead, STREAMFILE *sf) {
|
||||
|
||||
/* parse track */
|
||||
{
|
||||
off_t trk_offset = sead->trk_section_offset + read_u32(sead->trk_section_offset + 0x10 + trk_index*0x04, sf);
|
||||
uint32_t trk_offset = sead->trk_section_offset + read_u32(sead->trk_section_offset + 0x10 + trk_index*0x04, sf);
|
||||
uint8_t trk_type;
|
||||
uint16_t mtrl_index;
|
||||
|
||||
@ -743,8 +775,9 @@ static void parse_sead_sab_name(sead_header *sead, STREAMFILE *sf) {
|
||||
|
||||
/* assumes same bank, not sure if bank info is even inside .sab */
|
||||
if (mtrl_index == sead->mtrl_index) {
|
||||
sead->sndname_offset = sndname_offset;
|
||||
sead->sndname_size = sndname_size;
|
||||
build_readable_sab_name(sead, sf, sndname_offset, sndname_size);
|
||||
//sead->sndname_offset = sndname_offset;
|
||||
//sead->sndname_size = sndname_size;
|
||||
}
|
||||
}
|
||||
else if (trk_type == 0x02) {
|
||||
@ -760,8 +793,10 @@ static void parse_sead_sab_name(sead_header *sead, STREAMFILE *sf) {
|
||||
/* commands normally end when a type 0=none is found */
|
||||
if (cmnd_type <= 0x00 || cmnd_type > 0x08)
|
||||
break;
|
||||
if (sead->sndname_offset > 0)
|
||||
break;
|
||||
|
||||
/* keep reading names as N sounds may point to current material */
|
||||
//if (sead->sndname_offset > 0)
|
||||
// break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -770,9 +805,9 @@ static void parse_sead_sab_name(sead_header *sead, STREAMFILE *sf) {
|
||||
}
|
||||
|
||||
|
||||
static int parse_sead(sead_header* sead, STREAMFILE* sf) {
|
||||
uint32_t (*read_u32)(off_t,STREAMFILE*) = sead->big_endian ? read_u32be : read_u32le;
|
||||
uint16_t (*read_u16)(off_t,STREAMFILE*) = sead->big_endian ? read_u16be : read_u16le;
|
||||
static int parse_sead(sead_header_t* sead, STREAMFILE* sf) {
|
||||
read_u32_t read_u32 = sead->big_endian ? read_u32be : read_u32le;
|
||||
read_u16_t read_u16 = sead->big_endian ? read_u16be : read_u16le;
|
||||
|
||||
/** base header **/
|
||||
/* 0x00: id */
|
||||
@ -849,9 +884,9 @@ static int parse_sead(sead_header* sead, STREAMFILE* sf) {
|
||||
for (i = 0; i < entries; i++) {
|
||||
off_t entry_offset = sead->mtrl_section_offset + read_u32(sead->mtrl_section_offset + 0x10 + i*0x04, sf);
|
||||
|
||||
if (read_u8(entry_offset + 0x05, sf) == 0) {
|
||||
continue; /* codec 0 when dummy (see stream header) */
|
||||
}
|
||||
//if (read_u8(entry_offset + 0x05, sf) == 0) {
|
||||
// continue; /* codec 0 when dummy (see stream header) */
|
||||
//}
|
||||
|
||||
sead->total_subsongs++;
|
||||
if (!sead->mtrl_offset && sead->total_subsongs == sead->target_subsong) {
|
||||
@ -882,17 +917,24 @@ static int parse_sead(sead_header* sead, STREAMFILE* sf) {
|
||||
sead->extradata_id = read_u16(sead->mtrl_offset + 0x1c, sf);
|
||||
/* 0x1e: reserved */
|
||||
|
||||
if (sead->codec == 0x00) {
|
||||
/* dummy entries have null fields, put defaults to allow playing them */
|
||||
sead->channels = 1;
|
||||
sead->sample_rate = 48000;
|
||||
}
|
||||
|
||||
sead->loop_flag = (sead->loop_end > 0);
|
||||
sead->extradata_offset = sead->mtrl_offset + 0x20;
|
||||
|
||||
if (sead->is_sab) {
|
||||
parse_sead_sab_name(sead, sf);
|
||||
/* sab name is added during process */
|
||||
}
|
||||
else if (sead->is_mab) {
|
||||
parse_sead_mab_name(sead, sf);
|
||||
build_readable_mab_name(sead, sf);
|
||||
}
|
||||
|
||||
build_readable_name(sead->readable_name, sizeof(sead->readable_name), sead, sf);
|
||||
|
||||
return 1;
|
||||
fail:
|
||||
|
Loading…
x
Reference in New Issue
Block a user