mirror of
https://github.com/vgmstream/vgmstream.git
synced 2024-11-24 15:00:11 +01:00
Merge pull request #948 from bnnm/psb-cri
- Improve .acb name reading performance - Fix some .psb [Legend of Mana (Switch), Judgment (PS4)] - Minor tweaks
This commit is contained in:
commit
0dd8bdd763
@ -463,7 +463,7 @@ You can also choose which channels to play using *TXTP*. For example, create
|
||||
a file named `song.adx#C1,2.txtp` to play only channels 1 and 2 from `song.adx`.
|
||||
*TXTP* also has command to set how files are downmixed.
|
||||
|
||||
### Logged errors and unplayable supported files
|
||||
## Logged errors and unplayable supported files
|
||||
Some formats should normally play, but somehow don't. In those cases plugins
|
||||
can print vgmstream's error info to console (for example, `.fsb` with an unknown
|
||||
codec, `.hca/awb` with missing decryption key, bank has no audio, `.txth` is
|
||||
|
@ -23,8 +23,9 @@ typedef struct {
|
||||
|
||||
bool m_file_opened; /* if foobar IO service opened the file */
|
||||
service_ptr_t<file> m_file; /* foobar IO service */
|
||||
abort_callback * p_abort; /* foobar error stuff */
|
||||
char* name; /* IO filename */
|
||||
abort_callback* p_abort; /* foobar error stuff */
|
||||
/*const*/ char* name; /* IO filename */
|
||||
int name_len; /* cache */
|
||||
offv_t offset; /* last read offset (info) */
|
||||
offv_t buf_offset; /* current buffer data start */
|
||||
uint8_t* buf; /* data buffer */
|
||||
@ -117,14 +118,21 @@ static offv_t foo_get_offset(FOO_STREAMFILE* sf) {
|
||||
return sf->offset;
|
||||
}
|
||||
static void foo_get_name(FOO_STREAMFILE* sf, char* name, size_t name_size) {
|
||||
/* Most crap only cares about the filename itself */
|
||||
size_t ourlen = strlen(sf->name);
|
||||
if (ourlen > name_size) {
|
||||
if (name_size) strcpy(name, sf->name + ourlen - name_size + 1);
|
||||
}
|
||||
else {
|
||||
int copy_size = sf->name_len + 1;
|
||||
if (copy_size > name_size)
|
||||
copy_size = name_size;
|
||||
|
||||
memcpy(name, sf->name, copy_size);
|
||||
name[copy_size - 1] = '\0';
|
||||
|
||||
/*
|
||||
//TODO: check again (looks like a truncate-from-the-end copy, probably a useless remnant of olden times)
|
||||
if (sf->name_len > name_size) {
|
||||
strcpy(name, sf->name + sf->name_len - name_size + 1);
|
||||
} else {
|
||||
strcpy(name, sf->name);
|
||||
}
|
||||
*/
|
||||
}
|
||||
static void foo_close(FOO_STREAMFILE* sf) {
|
||||
sf->m_file.release(); //release alloc'ed ptr
|
||||
@ -178,8 +186,11 @@ static STREAMFILE* open_foo_streamfile_buffer_by_file(service_ptr_t<file> m_file
|
||||
this_sf->buf_size = buf_size;
|
||||
this_sf->buf = buf;
|
||||
|
||||
//TODO: foobar filenames look like "file://C:\path\to\file.adx"
|
||||
// maybe should hide the internal protocol and restore on open?
|
||||
this_sf->name = strdup(filename);
|
||||
if (!this_sf->name) goto fail;
|
||||
this_sf->name_len = strlen(this_sf->name);
|
||||
|
||||
/* cache file_size */
|
||||
if (this_sf->m_file_opened)
|
||||
|
@ -456,7 +456,7 @@ static size_t make_oggs_page(uint8_t* buf, int buf_size, size_t data_size, int p
|
||||
}
|
||||
|
||||
segment_count = (int)(data_size / 0xFF + 1);
|
||||
put_u32be(buf+0x00, 0x4F676753); /* capture pattern ("OggS") */
|
||||
put_u32be(buf+0x00, get_id32be("OggS")); /* capture pattern */
|
||||
put_u8 (buf+0x04, 0); /* stream structure version, fixed */
|
||||
put_u8 (buf+0x05, header_type_flag); /* bitflags (0: normal, continued = 1, first = 2, last = 4) */
|
||||
put_u32le(buf+0x06, (uint32_t)(absolute_granule >> 0 & 0xFFFFFFFF)); /* lower */
|
||||
@ -517,8 +517,8 @@ static size_t make_opus_header(uint8_t* buf, int buf_size, opus_config *cfg) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
put_u32be(buf+0x00, 0x4F707573); /* "Opus" header magic */
|
||||
put_u32be(buf+0x04, 0x48656164); /* "Head" header magic */
|
||||
put_u32be(buf+0x00, get_id32be("Opus"));
|
||||
put_u32be(buf+0x04, get_id32be("Head"));
|
||||
put_u8 (buf+0x08, 1); /* version */
|
||||
put_u8 (buf+0x09, cfg->channels);
|
||||
put_s16le(buf+0x0A, cfg->skip);
|
||||
@ -575,19 +575,23 @@ fail:
|
||||
static size_t make_oggs_first(uint8_t* buf, int buf_size, opus_config* cfg) {
|
||||
int buf_done = 0;
|
||||
size_t bytes;
|
||||
size_t page_size = 0x1c; /* fixed for header page */
|
||||
|
||||
if (buf_size < 0x100) /* approx */
|
||||
goto fail;
|
||||
|
||||
/* make header */
|
||||
bytes = make_opus_header(buf+buf_done + 0x1c,buf_size, cfg);
|
||||
make_oggs_page(buf+buf_done + 0x00,buf_size, bytes, 0, 0);
|
||||
buf_done += 0x1c + bytes;
|
||||
/* make header (first data, then page for checksum) */
|
||||
bytes = make_opus_header(buf + page_size, buf_size - page_size, cfg);
|
||||
make_oggs_page(buf, buf_size, bytes, 0, 0);
|
||||
buf_done += (page_size + bytes);
|
||||
|
||||
buf += buf_done;
|
||||
buf_size -= buf_done;
|
||||
|
||||
/* make comment */
|
||||
bytes = make_opus_comment(buf+buf_done + 0x1c,buf_size);
|
||||
make_oggs_page(buf+buf_done + 0x00,buf_size, bytes, 1, 0);
|
||||
buf_done += 0x1c + bytes;
|
||||
bytes = make_opus_comment(buf + page_size, buf_size - page_size);
|
||||
make_oggs_page(buf, buf_size, bytes, 1, 0);
|
||||
buf_done += (page_size + bytes);
|
||||
|
||||
return buf_done;
|
||||
fail:
|
||||
|
@ -177,9 +177,6 @@ int setup_layout_segmented(segmented_layout_data* data) {
|
||||
for (i = 0; i < data->segment_count; i++) {
|
||||
int segment_input_channels, segment_output_channels;
|
||||
|
||||
/* allow config if set for fine-tuned parts (usually TXTP only) */
|
||||
data->segments[i]->config_enabled = data->segments[i]->config.config_set;
|
||||
|
||||
if (data->segments[i] == NULL) {
|
||||
VGM_LOG("SEGMENTED: no vgmstream in segment %i\n", i);
|
||||
goto fail;
|
||||
@ -190,6 +187,9 @@ int setup_layout_segmented(segmented_layout_data* data) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* allow config if set for fine-tuned parts (usually TXTP only) */
|
||||
data->segments[i]->config_enabled = data->segments[i]->config.config_set;
|
||||
|
||||
/* disable so that looping is controlled by render_vgmstream_segmented */
|
||||
if (data->segments[i]->loop_flag != 0) {
|
||||
VGM_LOG("SEGMENTED: segment %i is looped\n", i);
|
||||
|
1006
src/meta/acb.c
1006
src/meta/acb.c
File diff suppressed because it is too large
Load Diff
164
src/meta/awb.c
164
src/meta/awb.c
@ -1,7 +1,7 @@
|
||||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
|
||||
typedef enum { ADX, HCA, VAG, RIFF, CWAV, DSP, CWAC, M4A } awb_type;
|
||||
//typedef enum { ADX, HCA, VAG, RIFF, CWAV, DSP, CWAC, M4A } awb_type_t;
|
||||
|
||||
static void load_awb_name(STREAMFILE* sf, STREAMFILE* sf_acb, VGMSTREAM* vgmstream, int waveid);
|
||||
|
||||
@ -13,26 +13,21 @@ VGMSTREAM* init_vgmstream_awb(STREAMFILE* sf) {
|
||||
VGMSTREAM* init_vgmstream_awb_memory(STREAMFILE* sf, STREAMFILE* sf_acb) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
STREAMFILE* temp_sf = NULL;
|
||||
off_t offset, subfile_offset, subfile_next;
|
||||
size_t subfile_size;
|
||||
uint32_t offset, subfile_offset, subfile_next, subfile_size;
|
||||
int total_subsongs, target_subsong = sf->stream_index;
|
||||
//uint32_t flags;
|
||||
uint8_t offset_size;
|
||||
uint16_t alignment, subkey;
|
||||
awb_type type;
|
||||
const char* extension = NULL;
|
||||
int waveid;
|
||||
|
||||
|
||||
/* checks
|
||||
* .awb: standard
|
||||
/* checks */
|
||||
if (!is_id32be(0x00,sf, "AFS2"))
|
||||
goto fail;
|
||||
/* .awb: standard
|
||||
* .afs2: sometimes [Okami HD (PS4)] */
|
||||
if (!check_extensions(sf, "awb,afs2"))
|
||||
goto fail;
|
||||
if (read_u32be(0x00,sf) != 0x41465332) /* "AFS2" */
|
||||
goto fail;
|
||||
|
||||
//flags = read_32bitLE(0x08,sf);
|
||||
/* 0x04(1): version? 0x01=common, 0x02=2018+ (no apparent differences) */
|
||||
offset_size = read_u8(0x05,sf);
|
||||
/* 0x06(2): always 0x0002? */
|
||||
@ -45,9 +40,9 @@ VGMSTREAM* init_vgmstream_awb_memory(STREAMFILE* sf, STREAMFILE* sf_acb) {
|
||||
|
||||
offset = 0x10;
|
||||
|
||||
/* id(?) table: read target */
|
||||
/* id table: read target */
|
||||
{
|
||||
off_t waveid_offset = offset + (target_subsong-1) * 0x02;
|
||||
uint32_t waveid_offset = offset + (target_subsong-1) * 0x02;
|
||||
|
||||
waveid = read_u16le(waveid_offset,sf);
|
||||
|
||||
@ -56,7 +51,7 @@ VGMSTREAM* init_vgmstream_awb_memory(STREAMFILE* sf, STREAMFILE* sf_acb) {
|
||||
|
||||
/* offset table: find target */
|
||||
{
|
||||
off_t file_size = get_streamfile_size(sf);
|
||||
uint32_t file_size = get_streamfile_size(sf);
|
||||
|
||||
/* last sub-offset is always file end, so table entries = total_subsongs+1 */
|
||||
offset += (target_subsong-1) * offset_size;
|
||||
@ -71,7 +66,7 @@ VGMSTREAM* init_vgmstream_awb_memory(STREAMFILE* sf, STREAMFILE* sf_acb) {
|
||||
subfile_next = read_u16le(offset+0x02,sf);
|
||||
break;
|
||||
default:
|
||||
VGM_LOG("AWB: unknown offset size\n");
|
||||
vgm_logi("AWB: unknown offset size (report)\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
@ -83,97 +78,70 @@ VGMSTREAM* init_vgmstream_awb_memory(STREAMFILE* sf, STREAMFILE* sf_acb) {
|
||||
subfile_size = subfile_next - subfile_offset;
|
||||
}
|
||||
|
||||
//;VGM_LOG("AWB: subfile offset=%lx + %x\n", subfile_offset, subfile_size);
|
||||
//;VGM_LOG("awb: subfile offset=%x + %x\n", subfile_offset, subfile_size);
|
||||
|
||||
/* autodetect as there isn't anything, plus can mix types
|
||||
* (waveid<>codec info is usually in the companion .acb) */
|
||||
if (read_u16be(subfile_offset, sf) == 0x8000) { /* ADX id (type 0) */
|
||||
type = ADX;
|
||||
extension = "adx";
|
||||
}
|
||||
else if ((read_u32be(subfile_offset,sf) & 0x7f7f7f7f) == 0x48434100) { /* "HCA\0" (type 2=HCA, 6=HCA-MX) */
|
||||
type = HCA;
|
||||
extension = "hca";
|
||||
}
|
||||
else if (read_u32be(subfile_offset,sf) == 0x56414770) { /* "VAGp" (type 7=VAG, 10=HEVAG) */
|
||||
type = VAG;
|
||||
extension = "vag";
|
||||
}
|
||||
else if (read_u32be(subfile_offset,sf) == 0x52494646) { /* "RIFF" (type 8=ATRAC3, 11=ATRAC9) */
|
||||
type = RIFF;
|
||||
extension = "wav";
|
||||
subfile_size = read_u32le(subfile_offset + 0x04,sf) + 0x08; /* rough size, use RIFF's */
|
||||
}
|
||||
else if (read_u32be(subfile_offset,sf) == 0x43574156) { /* "CWAV" (type 9) */
|
||||
type = CWAV;
|
||||
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 &&
|
||||
read_u32be(subfile_offset + 0x50,sf) == 0) { /* probably should call some check function (type 13) */
|
||||
type = DSP;
|
||||
extension = "dsp";
|
||||
}
|
||||
else if (is_id32be(subfile_offset,sf, "CWAC")) { /* type 13 again */
|
||||
type = CWAC;
|
||||
extension = "dsp";
|
||||
}
|
||||
else if (read_u32be(subfile_offset+0x00,sf) == 0x00000018 &&
|
||||
read_u32be(subfile_offset+0x04,sf) == 0x66747970) { /* chunk size + "ftyp" (type 19) */
|
||||
type = M4A;
|
||||
extension = "m4a";
|
||||
}
|
||||
else {
|
||||
VGM_LOG("AWB: unknown codec\n");
|
||||
goto fail;
|
||||
}
|
||||
{
|
||||
VGMSTREAM* (*init_vgmstream)(STREAMFILE* sf) = NULL;
|
||||
VGMSTREAM* (*init_vgmstream_subkey)(STREAMFILE* sf, uint16_t subkey) = NULL;
|
||||
const char* extension = NULL;
|
||||
|
||||
|
||||
temp_sf = setup_subfile_streamfile(sf, subfile_offset, subfile_size, extension);
|
||||
if (!temp_sf) goto fail;
|
||||
|
||||
switch(type) {
|
||||
case HCA: /* most common */
|
||||
vgmstream = init_vgmstream_hca_subkey(temp_sf, subkey);
|
||||
if (!vgmstream) goto fail;
|
||||
break;
|
||||
case ADX: /* Okami HD (PS4) */
|
||||
vgmstream = init_vgmstream_adx_subkey(temp_sf, subkey);
|
||||
if (!vgmstream) goto fail;
|
||||
break;
|
||||
case VAG: /* Ukiyo no Roushi (Vita) */
|
||||
vgmstream = init_vgmstream_vag(temp_sf);
|
||||
if (!vgmstream) goto fail;
|
||||
break;
|
||||
case RIFF: /* Ukiyo no Roushi (Vita) */
|
||||
vgmstream = init_vgmstream_riff(temp_sf);
|
||||
if (!vgmstream) goto fail;
|
||||
break;
|
||||
case CWAV: /* Sonic: Lost World (3DS) */
|
||||
vgmstream = init_vgmstream_rwsd(temp_sf);
|
||||
if (!vgmstream) goto fail;
|
||||
break;
|
||||
case DSP: /* Sonic: Lost World (WiiU) */
|
||||
vgmstream = init_vgmstream_ngc_dsp_std(temp_sf);
|
||||
if (!vgmstream) goto fail;
|
||||
break;
|
||||
case CWAC: /* Mario & Sonic at the Rio 2016 Olympic Games (WiiU) */
|
||||
vgmstream = init_vgmstream_dsp_cwac(temp_sf);
|
||||
if (!vgmstream) goto fail;
|
||||
break;
|
||||
if (read_u16be(subfile_offset, sf) == 0x8000) { /* (type 0=ADX) */
|
||||
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";
|
||||
}
|
||||
else if (is_id32be(subfile_offset,sf, "RIFF")) { /* (type 8=ATRAC3, 11=ATRAC9) */
|
||||
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 &&
|
||||
read_u32be(subfile_offset + 0x50,sf) == 0) { /* (type 13=DSP), probably should call some check function */
|
||||
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
|
||||
case M4A: /* Imperial SaGa Eclipse (Browser) */
|
||||
vgmstream = init_vgmstream_mp4_aac_ffmpeg(temp_sf);
|
||||
if (!vgmstream) goto fail;
|
||||
break;
|
||||
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
|
||||
default:
|
||||
else {
|
||||
vgm_logi("AWB: unknown codec (report)\n");
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
vgmstream->num_streams = total_subsongs;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/* try to load cue names */
|
||||
load_awb_name(sf, sf_acb, vgmstream, waveid);
|
||||
|
@ -1,14 +1,15 @@
|
||||
#include "cri_utf.h"
|
||||
#include "../util/log.h"
|
||||
|
||||
#define UTF_MAX_SCHEMA_SIZE 0x8000 /* arbitrary max */
|
||||
#define COLUMN_BITMASK_FLAG 0xf0
|
||||
#define COLUMN_BITMASK_TYPE 0x0f
|
||||
|
||||
enum columna_flag_t {
|
||||
COLUMN_FLAG_NAME = 0x10,
|
||||
COLUMN_FLAG_DEFAULT = 0x20,
|
||||
COLUMN_FLAG_ROW = 0x40,
|
||||
COLUMN_FLAG_UNDEFINED = 0x80 /* shouldn't exist */
|
||||
COLUMN_FLAG_NAME = 0x10, /* column has name (may be empty) */
|
||||
COLUMN_FLAG_DEFAULT = 0x20, /* data is found relative to schema start (typically constant value for all rows) */
|
||||
COLUMN_FLAG_ROW = 0x40, /* data is found relative to row start */
|
||||
COLUMN_FLAG_UNDEFINED = 0x80 /* shouldn't exist */
|
||||
};
|
||||
|
||||
enum column_type_t {
|
||||
@ -28,34 +29,8 @@ enum column_type_t {
|
||||
COLUMN_TYPE_UNDEFINED = -1
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
int found;
|
||||
enum column_type_t type;
|
||||
union {
|
||||
int8_t value_s8;
|
||||
uint8_t value_u8;
|
||||
int16_t value_s16;
|
||||
uint16_t value_u16;
|
||||
int32_t value_s32;
|
||||
uint32_t value_u32;
|
||||
int64_t value_s64;
|
||||
uint64_t value_u64;
|
||||
float value_float;
|
||||
double value_double;
|
||||
struct utf_data_t {
|
||||
uint32_t offset;
|
||||
uint32_t size;
|
||||
} value_data;
|
||||
//struct utf_u128_t {
|
||||
// uint64_t hi;
|
||||
// uint64_t lo;
|
||||
//} value_u128;
|
||||
const char *value_string;
|
||||
} value;
|
||||
} utf_result_t;
|
||||
|
||||
struct utf_context {
|
||||
STREAMFILE *sf;
|
||||
STREAMFILE* sf;
|
||||
uint32_t table_offset;
|
||||
|
||||
/* header */
|
||||
@ -68,25 +43,31 @@ struct utf_context {
|
||||
uint16_t columns;
|
||||
uint16_t row_width;
|
||||
uint32_t rows;
|
||||
|
||||
uint8_t* schema_buf;
|
||||
struct utf_column_t {
|
||||
uint8_t flag;
|
||||
uint8_t type;
|
||||
const char *name;
|
||||
const char* name;
|
||||
uint32_t offset;
|
||||
} *schema;
|
||||
|
||||
/* derived */
|
||||
uint32_t schema_offset;
|
||||
uint32_t schema_size;
|
||||
uint32_t rows_size;
|
||||
uint32_t data_size;
|
||||
uint32_t strings_size;
|
||||
char *string_table;
|
||||
const char *table_name;
|
||||
char* string_table;
|
||||
const char* table_name;
|
||||
};
|
||||
|
||||
|
||||
/* @UTF table context creation */
|
||||
utf_context* utf_open(STREAMFILE* sf, uint32_t table_offset, int* p_rows, const char** p_row_name) {
|
||||
utf_context* utf = NULL;
|
||||
|
||||
uint8_t buf[0x20];
|
||||
int bytes;
|
||||
|
||||
utf = calloc(1, sizeof(utf_context));
|
||||
if (!utf) goto fail;
|
||||
@ -94,27 +75,33 @@ utf_context* utf_open(STREAMFILE* sf, uint32_t table_offset, int* p_rows, const
|
||||
utf->sf = sf;
|
||||
utf->table_offset = table_offset;
|
||||
|
||||
/* check header */
|
||||
if (read_u32be(table_offset + 0x00, sf) != 0x40555446) /* "@UTF" */
|
||||
goto fail;
|
||||
bytes = read_streamfile(buf, table_offset, sizeof(buf), sf);
|
||||
if (bytes != sizeof(buf)) goto fail;
|
||||
|
||||
/* load table header */
|
||||
utf->table_size = read_u32be(table_offset + 0x04, sf) + 0x08;
|
||||
utf->version = read_u16be(table_offset + 0x08, sf);
|
||||
utf->rows_offset = read_u16be(table_offset + 0x0a, sf) + 0x08;
|
||||
utf->strings_offset = read_u32be(table_offset + 0x0c, sf) + 0x08;
|
||||
utf->data_offset = read_u32be(table_offset + 0x10, sf) + 0x08;
|
||||
utf->name_offset = read_u32be(table_offset + 0x14, sf); /* within string table */
|
||||
utf->columns = read_u16be(table_offset + 0x18, sf);
|
||||
utf->row_width = read_u16be(table_offset + 0x1a, sf);
|
||||
utf->rows = read_u32be(table_offset + 0x1c, sf);
|
||||
if (get_u32be(buf + 0x00) != get_id32be("@UTF"))
|
||||
goto fail;
|
||||
utf->table_size = get_u32be(buf + 0x04) + 0x08;
|
||||
utf->version = get_u16be(buf + 0x08);
|
||||
utf->rows_offset = get_u16be(buf + 0x0a) + 0x08;
|
||||
utf->strings_offset = get_u32be(buf + 0x0c) + 0x08;
|
||||
utf->data_offset = get_u32be(buf + 0x10) + 0x08;
|
||||
utf->name_offset = get_u32be(buf + 0x14); /* within string table */
|
||||
utf->columns = get_u16be(buf + 0x18);
|
||||
utf->row_width = get_u16be(buf + 0x1a);
|
||||
utf->rows = get_u32be(buf + 0x1c);
|
||||
|
||||
utf->schema_offset = 0x20;
|
||||
utf->schema_size = utf->rows_offset - utf->schema_offset;
|
||||
utf->rows_size = utf->strings_offset - utf->rows_offset;
|
||||
utf->strings_size = utf->data_offset - utf->strings_offset;
|
||||
utf->data_size = utf->table_size - utf->data_offset;
|
||||
|
||||
utf->schema_offset = 0x20;
|
||||
utf->strings_size = utf->data_offset - utf->strings_offset;
|
||||
|
||||
/* 00: early (32b rows_offset?), 01: +2017 (no apparent differences) */
|
||||
if (utf->version != 0x00 && utf->version != 0x01) {
|
||||
vgm_logi("@UTF: unknown version\n");
|
||||
goto fail;
|
||||
}
|
||||
if (utf->table_offset + utf->table_size > get_streamfile_size(sf))
|
||||
goto fail;
|
||||
@ -125,39 +112,48 @@ utf_context* utf_open(STREAMFILE* sf, uint32_t table_offset, int* p_rows, const
|
||||
/* no rows is possible for empty tables (have schema and columns names but no data) [PES 2013 (PC)] */
|
||||
if (utf->columns <= 0 /*|| utf->rows <= 0 || utf->rows_width <= 0*/)
|
||||
goto fail;
|
||||
if (utf->schema_size >= UTF_MAX_SCHEMA_SIZE)
|
||||
goto fail;
|
||||
|
||||
|
||||
/* load string table */
|
||||
/* load sections linearly (to optimize stream) */
|
||||
{
|
||||
size_t read;
|
||||
/* schema section: small so keep it around (useful to avoid re-reads on column values) */
|
||||
utf->schema_buf = malloc(utf->schema_size);
|
||||
if (!utf->schema_buf) goto fail;
|
||||
|
||||
bytes = read_streamfile(utf->schema_buf, utf->table_offset + utf->schema_offset, utf->schema_size, sf);
|
||||
if (bytes != utf->schema_size) goto fail;
|
||||
|
||||
/* row section: skip, mid to big (0x10000~0x50000) so not preloaded for now */
|
||||
|
||||
/* string section: low to mid size but used to return c-strings */
|
||||
utf->string_table = calloc(utf->strings_size + 1, sizeof(char));
|
||||
if (!utf->string_table) goto fail;
|
||||
|
||||
utf->table_name = utf->string_table + utf->name_offset;
|
||||
bytes = read_streamfile((unsigned char*)utf->string_table, utf->table_offset + utf->strings_offset, utf->strings_size, sf);
|
||||
if (bytes != utf->strings_size) goto fail;
|
||||
|
||||
read = read_streamfile((unsigned char*)utf->string_table, utf->table_offset + utf->strings_offset, utf->strings_size, sf);
|
||||
if (utf->strings_size != read) goto fail;
|
||||
/* data section: skip (may be big with memory AWB) */
|
||||
}
|
||||
|
||||
|
||||
/* load column schema */
|
||||
{
|
||||
int i;
|
||||
uint32_t value_size, column_offset = 0;
|
||||
uint32_t schema_offset = utf->table_offset + utf->schema_offset;
|
||||
int schema_pos = 0;
|
||||
|
||||
utf->table_name = utf->string_table + utf->name_offset;
|
||||
|
||||
utf->schema = malloc(sizeof(struct utf_column_t) * utf->columns);
|
||||
utf->schema = malloc(utf->columns * sizeof(struct utf_column_t));
|
||||
if (!utf->schema) goto fail;
|
||||
|
||||
for (i = 0; i < utf->columns; i++) {
|
||||
uint8_t info = read_u8(schema_offset + 0x00, sf);
|
||||
uint32_t name_offset = read_u32be(schema_offset + 0x01, sf);
|
||||
uint8_t info = get_u8(utf->schema_buf + schema_pos + 0x00);
|
||||
uint32_t name_offset = get_u32be(utf->schema_buf + schema_pos + 0x01);
|
||||
|
||||
if (name_offset > utf->strings_size)
|
||||
goto fail;
|
||||
schema_offset += 0x01 + 0x04;
|
||||
|
||||
schema_pos += 0x01 + 0x04;
|
||||
|
||||
utf->schema[i].flag = info & COLUMN_BITMASK_FLAG;
|
||||
utf->schema[i].type = info & COLUMN_BITMASK_TYPE;
|
||||
@ -165,7 +161,7 @@ utf_context* utf_open(STREAMFILE* sf, uint32_t table_offset, int* p_rows, const
|
||||
utf->schema[i].offset = 0;
|
||||
|
||||
/* known flags are name+default or name+row, but name+default+row is mentioned in VGMToolbox
|
||||
* even though isn't possible in CRI's craft utils, and no name is apparently possible */
|
||||
* even though isn't possible in CRI's craft utils (meaningless), and no name is apparently possible */
|
||||
if ( (utf->schema[i].flag == 0) ||
|
||||
!(utf->schema[i].flag & COLUMN_FLAG_NAME) ||
|
||||
((utf->schema[i].flag & COLUMN_FLAG_DEFAULT) && (utf->schema[i].flag & COLUMN_FLAG_ROW)) ||
|
||||
@ -207,20 +203,25 @@ utf_context* utf_open(STREAMFILE* sf, uint32_t table_offset, int* p_rows, const
|
||||
}
|
||||
|
||||
if (utf->schema[i].flag & COLUMN_FLAG_DEFAULT) {
|
||||
/* data is found relative to schema start */
|
||||
utf->schema[i].offset = schema_offset - (utf->table_offset + utf->schema_offset);
|
||||
schema_offset += value_size;
|
||||
utf->schema[i].offset = schema_pos;
|
||||
schema_pos += value_size;
|
||||
}
|
||||
|
||||
if (utf->schema[i].flag & COLUMN_FLAG_ROW) {
|
||||
/* data is found relative to row start */
|
||||
utf->schema[i].offset = column_offset;
|
||||
column_offset += value_size;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* next section is row and variable length data (pointed above) then end of table */
|
||||
#if 0
|
||||
VGM_LOG("- %s\n", utf->table_name);
|
||||
VGM_LOG("utf_o=%08x (%x)\n", utf->table_offset, utf->table_size);
|
||||
VGM_LOG(" sch_o=%08x (%x), c=%i\n", utf->table_offset + utf->schema_offset, utf->schema_size, utf->columns);
|
||||
VGM_LOG(" row_o=%08x (%x), r=%i\n", utf->table_offset + utf->rows_offset, utf->rows_size, utf->rows);
|
||||
VGM_LOG(" str_o=%08x (%x)\n", utf->table_offset + utf->strings_offset, utf->strings_size);
|
||||
VGM_LOG(" dat_o=%08x (%x))\n", utf->table_offset + utf->data_offset, utf->data_size);
|
||||
#endif
|
||||
|
||||
/* write info */
|
||||
if (p_rows) *p_rows = utf->rows;
|
||||
@ -237,107 +238,139 @@ void utf_close(utf_context* utf) {
|
||||
if (!utf) return;
|
||||
|
||||
free(utf->string_table);
|
||||
free(utf->schema_buf);
|
||||
free(utf->schema);
|
||||
free(utf);
|
||||
}
|
||||
|
||||
|
||||
static int utf_query(utf_context* utf, int row, const char* column, utf_result_t* result) {
|
||||
int utf_get_column(utf_context* utf, const char* column) {
|
||||
int i;
|
||||
|
||||
|
||||
result->found = 0;
|
||||
|
||||
if (row >= utf->rows || row < 0)
|
||||
goto fail;
|
||||
|
||||
/* find target column */
|
||||
for (i = 0; i < utf->columns; i++) {
|
||||
struct utf_column_t *col = &utf->schema[i];
|
||||
uint32_t data_offset;
|
||||
struct utf_column_t* col = &utf->schema[i];
|
||||
|
||||
if (col->name == NULL || strcmp(col->name, column) != 0)
|
||||
continue;
|
||||
return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
enum column_type_t type;
|
||||
union {
|
||||
int8_t s8;
|
||||
uint8_t u8;
|
||||
int16_t s16;
|
||||
uint16_t u16;
|
||||
int32_t s32;
|
||||
uint32_t u32;
|
||||
int64_t s64;
|
||||
uint64_t u64;
|
||||
float flt;
|
||||
double dbl;
|
||||
struct utf_data_t {
|
||||
uint32_t offset;
|
||||
uint32_t size;
|
||||
} data;
|
||||
#if 0
|
||||
struct utf_u128_t {
|
||||
uint64_t hi;
|
||||
uint64_t lo;
|
||||
} value_u128;
|
||||
#endif
|
||||
const char* str;
|
||||
} value;
|
||||
} utf_result_t;
|
||||
|
||||
static int utf_query(utf_context* utf, int row, int column, utf_result_t* result) {
|
||||
|
||||
if (row >= utf->rows || row < 0)
|
||||
goto fail;
|
||||
if (column >= utf->columns || column < 0)
|
||||
goto fail;
|
||||
|
||||
/* get target column */
|
||||
{
|
||||
struct utf_column_t* col = &utf->schema[column];
|
||||
uint32_t data_offset = 0;
|
||||
uint8_t* buf = NULL;
|
||||
|
||||
result->found = 1;
|
||||
result->type = col->type;
|
||||
|
||||
if (col->flag & COLUMN_FLAG_DEFAULT) {
|
||||
data_offset = utf->table_offset + utf->schema_offset + col->offset;
|
||||
if (utf->schema_buf)
|
||||
buf = utf->schema_buf + col->offset;
|
||||
else
|
||||
data_offset = utf->table_offset + utf->schema_offset + col->offset;
|
||||
}
|
||||
else if (col->flag & COLUMN_FLAG_ROW) {
|
||||
data_offset = utf->table_offset + utf->rows_offset + row * utf->row_width + col->offset;
|
||||
}
|
||||
else {
|
||||
data_offset = 0;
|
||||
/* shouldn't happen */
|
||||
memset(&result->value, 0, sizeof(result->value));
|
||||
return 1; /* ??? */
|
||||
}
|
||||
|
||||
/* ignore zero value */
|
||||
if (data_offset == 0) {
|
||||
memset(&result->value, 0, sizeof(result->value)); /* just in case... */
|
||||
break;
|
||||
}
|
||||
|
||||
/* read row/constant value */
|
||||
/* read row/constant value (use buf if available) */
|
||||
switch (col->type) {
|
||||
case COLUMN_TYPE_UINT8:
|
||||
result->value.value_u8 = read_u8(data_offset, utf->sf);
|
||||
result->value.u8 = buf ? get_u8(buf) : read_u8(data_offset, utf->sf);
|
||||
break;
|
||||
case COLUMN_TYPE_SINT8:
|
||||
result->value.value_s8 = read_s8(data_offset, utf->sf);
|
||||
result->value.s8 = buf ? get_s8(buf) : read_s8(data_offset, utf->sf);
|
||||
break;
|
||||
case COLUMN_TYPE_UINT16:
|
||||
result->value.value_u16 = read_u16be(data_offset, utf->sf);
|
||||
result->value.u16 = buf ? get_u16be(buf) : read_u16be(data_offset, utf->sf);
|
||||
break;
|
||||
case COLUMN_TYPE_SINT16:
|
||||
result->value.value_s16 = read_s16be(data_offset, utf->sf);
|
||||
result->value.s16 = buf ? get_s16be(buf) : read_s16be(data_offset, utf->sf);
|
||||
break;
|
||||
case COLUMN_TYPE_UINT32:
|
||||
result->value.value_u32 = read_u32be(data_offset, utf->sf);
|
||||
result->value.u32 = buf ? get_u32be(buf) : read_u32be(data_offset, utf->sf);
|
||||
break;
|
||||
case COLUMN_TYPE_SINT32:
|
||||
result->value.value_s32 = read_s32be(data_offset, utf->sf);
|
||||
result->value.s32 = buf ? get_s32be(buf) : read_s32be(data_offset, utf->sf);
|
||||
break;
|
||||
case COLUMN_TYPE_UINT64:
|
||||
result->value.value_u64 = read_u64be(data_offset, utf->sf);
|
||||
result->value.u64 = buf ? get_u64be(buf) : read_u64be(data_offset, utf->sf);
|
||||
break;
|
||||
case COLUMN_TYPE_SINT64:
|
||||
result->value.value_s64 = read_s64be(data_offset, utf->sf);
|
||||
result->value.s64 = buf ? get_s64be(buf) : read_s64be(data_offset, utf->sf);
|
||||
break;
|
||||
case COLUMN_TYPE_FLOAT: {
|
||||
result->value.value_float = read_f32be(data_offset, utf->sf);
|
||||
case COLUMN_TYPE_FLOAT:
|
||||
result->value.flt = buf ? get_f32be(buf) : read_f32be(data_offset, utf->sf);
|
||||
break;
|
||||
}
|
||||
#if 0
|
||||
case COLUMN_TYPE_DOUBLE: {
|
||||
result->value.value_double = read_d64be(data_offset, utf->sf);
|
||||
case COLUMN_TYPE_DOUBLE:
|
||||
result->value.dbl = buf ? get_d64be(buf) : read_d64be(data_offset, utf->sf);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
case COLUMN_TYPE_STRING: {
|
||||
uint32_t name_offset = read_u32be(data_offset, utf->sf);
|
||||
uint32_t name_offset = buf ? get_u32be(buf) : read_u32be(data_offset, utf->sf);
|
||||
if (name_offset > utf->strings_size)
|
||||
goto fail;
|
||||
result->value.value_string = utf->string_table + name_offset;
|
||||
result->value.str = utf->string_table + name_offset;
|
||||
break;
|
||||
}
|
||||
|
||||
case COLUMN_TYPE_VLDATA:
|
||||
result->value.value_data.offset = read_u32be(data_offset + 0x00, utf->sf);
|
||||
result->value.value_data.size = read_u32be(data_offset + 0x04, utf->sf);
|
||||
result->value.data.offset = buf ? get_u32be(buf + 0x0) : read_u32be(data_offset + 0x00, utf->sf);
|
||||
result->value.data.size = buf ? get_u32be(buf + 0x4) : read_u32be(data_offset + 0x04, utf->sf);
|
||||
break;
|
||||
#if 0
|
||||
case COLUMN_TYPE_UINT128: {
|
||||
result->value.value_u128.hi = read_u64be(data_offset + 0x00, utf->sf);
|
||||
result->value.value_u128.lo = read_u64be(data_offset + 0x08, utf->sf);
|
||||
case COLUMN_TYPE_UINT128:
|
||||
result->value.value_u128.hi = buf ? get_u32be(buf + 0x0) : read_u64be(data_offset + 0x00, utf->sf);
|
||||
result->value.value_u128.lo = buf ? get_u32be(buf + 0x4) : read_u64be(data_offset + 0x08, utf->sf);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
default:
|
||||
goto fail;
|
||||
}
|
||||
|
||||
break; /* column found and read */
|
||||
}
|
||||
|
||||
return 1;
|
||||
@ -345,24 +378,24 @@ fail:
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int utf_query_value(utf_context* utf, int row, const char* column, void* value, enum column_type_t type) {
|
||||
static int utf_query_value(utf_context* utf, int row, int column, void* value, enum column_type_t type) {
|
||||
utf_result_t result = {0};
|
||||
int valid;
|
||||
|
||||
valid = utf_query(utf, row, column, &result);
|
||||
if (!valid || !result.found || result.type != type)
|
||||
if (!valid || result.type != type)
|
||||
return 0;
|
||||
|
||||
switch(result.type) {
|
||||
case COLUMN_TYPE_UINT8: (*(uint8_t*)value) = result.value.value_u8; break;
|
||||
case COLUMN_TYPE_SINT8: (*(int8_t*)value) = result.value.value_s8; break;
|
||||
case COLUMN_TYPE_UINT16: (*(uint16_t*)value) = result.value.value_u16; break;
|
||||
case COLUMN_TYPE_SINT16: (*(int16_t*)value) = result.value.value_s16; break;
|
||||
case COLUMN_TYPE_UINT32: (*(uint32_t*)value) = result.value.value_u32; break;
|
||||
case COLUMN_TYPE_SINT32: (*(int32_t*)value) = result.value.value_s32; break;
|
||||
case COLUMN_TYPE_UINT64: (*(uint64_t*)value) = result.value.value_u64; break;
|
||||
case COLUMN_TYPE_SINT64: (*(int64_t*)value) = result.value.value_s64; break;
|
||||
case COLUMN_TYPE_STRING: (*(const char**)value) = result.value.value_string; break;
|
||||
case COLUMN_TYPE_UINT8: (*(uint8_t*)value) = result.value.u8; break;
|
||||
case COLUMN_TYPE_SINT8: (*(int8_t*)value) = result.value.s8; break;
|
||||
case COLUMN_TYPE_UINT16: (*(uint16_t*)value) = result.value.u16; break;
|
||||
case COLUMN_TYPE_SINT16: (*(int16_t*)value) = result.value.s16; break;
|
||||
case COLUMN_TYPE_UINT32: (*(uint32_t*)value) = result.value.u32; break;
|
||||
case COLUMN_TYPE_SINT32: (*(int32_t*)value) = result.value.s32; break;
|
||||
case COLUMN_TYPE_UINT64: (*(uint64_t*)value) = result.value.u64; break;
|
||||
case COLUMN_TYPE_SINT64: (*(int64_t*)value) = result.value.s64; break;
|
||||
case COLUMN_TYPE_STRING: (*(const char**)value) = result.value.str; break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
@ -370,43 +403,76 @@ static int utf_query_value(utf_context* utf, int row, const char* column, void*
|
||||
return 1;
|
||||
}
|
||||
|
||||
int utf_query_s8(utf_context* utf, int row, const char* column, int8_t* value) {
|
||||
int utf_query_col_s8(utf_context* utf, int row, int column, int8_t* value) {
|
||||
return utf_query_value(utf, row, column, (void*)value, COLUMN_TYPE_SINT8);
|
||||
}
|
||||
int utf_query_u8(utf_context* utf, int row, const char* column, uint8_t* value) {
|
||||
int utf_query_col_u8(utf_context* utf, int row, int column, uint8_t* value) {
|
||||
return utf_query_value(utf, row, column, (void*)value, COLUMN_TYPE_UINT8);
|
||||
}
|
||||
int utf_query_s16(utf_context* utf, int row, const char* column, int16_t* value) {
|
||||
int utf_query_col_s16(utf_context* utf, int row, int column, int16_t* value) {
|
||||
return utf_query_value(utf, row, column, (void*)value, COLUMN_TYPE_SINT16);
|
||||
}
|
||||
int utf_query_u16(utf_context* utf, int row, const char* column, uint16_t* value) {
|
||||
int utf_query_col_u16(utf_context* utf, int row, int column, uint16_t* value) {
|
||||
return utf_query_value(utf, row, column, (void*)value, COLUMN_TYPE_UINT16);
|
||||
}
|
||||
int utf_query_s32(utf_context* utf, int row, const char* column, int32_t* value) {
|
||||
int utf_query_col_s32(utf_context* utf, int row, int column, int32_t* value) {
|
||||
return utf_query_value(utf, row, column, (void*)value, COLUMN_TYPE_SINT32);
|
||||
}
|
||||
int utf_query_u32(utf_context* utf, int row, const char* column, uint32_t* value) {
|
||||
int utf_query_col_u32(utf_context* utf, int row, int column, uint32_t* value) {
|
||||
return utf_query_value(utf, row, column, (void*)value, COLUMN_TYPE_UINT32);
|
||||
}
|
||||
int utf_query_s64(utf_context* utf, int row, const char* column, int64_t* value) {
|
||||
int utf_query_col_s64(utf_context* utf, int row, int column, int64_t* value) {
|
||||
return utf_query_value(utf, row, column, (void*)value, COLUMN_TYPE_SINT64);
|
||||
}
|
||||
int utf_query_u64(utf_context* utf, int row, const char* column, uint64_t* value) {
|
||||
int utf_query_col_u64(utf_context* utf, int row, int column, uint64_t* value) {
|
||||
return utf_query_value(utf, row, column, (void*)value, COLUMN_TYPE_UINT64);
|
||||
}
|
||||
int utf_query_string(utf_context* utf, int row, const char* column, const char** value) {
|
||||
int utf_query_col_string(utf_context* utf, int row, int column, const char** value) {
|
||||
return utf_query_value(utf, row, column, (void*)value, COLUMN_TYPE_STRING);
|
||||
}
|
||||
|
||||
int utf_query_data(utf_context* utf, int row, const char* column, uint32_t* p_offset, uint32_t* p_size) {
|
||||
int utf_query_col_data(utf_context* utf, int row, int column, uint32_t* p_offset, uint32_t* p_size) {
|
||||
utf_result_t result = {0};
|
||||
int valid;
|
||||
|
||||
valid = utf_query(utf, row, column, &result);
|
||||
if (!valid || !result.found || result.type != COLUMN_TYPE_VLDATA)
|
||||
if (!valid || result.type != COLUMN_TYPE_VLDATA)
|
||||
return 0;
|
||||
|
||||
if (p_offset) *p_offset = utf->table_offset + utf->data_offset + result.value.value_data.offset;
|
||||
if (p_size) *p_size = result.value.value_data.size;
|
||||
if (p_offset) *p_offset = utf->table_offset + utf->data_offset + result.value.data.offset;
|
||||
if (p_size) *p_size = result.value.data.size;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
int utf_query_s8(utf_context* utf, int row, const char* column_name, int8_t* value) {
|
||||
return utf_query_value(utf, row, utf_get_column(utf, column_name), (void*)value, COLUMN_TYPE_SINT8);
|
||||
}
|
||||
int utf_query_u8(utf_context* utf, int row, const char* column_name, uint8_t* value) {
|
||||
return utf_query_value(utf, row, utf_get_column(utf, column_name), (void*)value, COLUMN_TYPE_UINT8);
|
||||
}
|
||||
int utf_query_s16(utf_context* utf, int row, const char* column_name, int16_t* value) {
|
||||
return utf_query_value(utf, row, utf_get_column(utf, column_name), (void*)value, COLUMN_TYPE_SINT16);
|
||||
}
|
||||
int utf_query_u16(utf_context* utf, int row, const char* column_name, uint16_t* value) {
|
||||
return utf_query_value(utf, row, utf_get_column(utf, column_name), (void*)value, COLUMN_TYPE_UINT16);
|
||||
}
|
||||
int utf_query_s32(utf_context* utf, int row, const char* column_name, int32_t* value) {
|
||||
return utf_query_value(utf, row, utf_get_column(utf, column_name), (void*)value, COLUMN_TYPE_SINT32);
|
||||
}
|
||||
int utf_query_u32(utf_context* utf, int row, const char* column_name, uint32_t* value) {
|
||||
return utf_query_value(utf, row, utf_get_column(utf, column_name), (void*)value, COLUMN_TYPE_UINT32);
|
||||
}
|
||||
int utf_query_s64(utf_context* utf, int row, const char* column_name, int64_t* value) {
|
||||
return utf_query_value(utf, row, utf_get_column(utf, column_name), (void*)value, COLUMN_TYPE_SINT64);
|
||||
}
|
||||
int utf_query_u64(utf_context* utf, int row, const char* column_name, uint64_t* value) {
|
||||
return utf_query_value(utf, row, utf_get_column(utf, column_name), (void*)value, COLUMN_TYPE_UINT64);
|
||||
}
|
||||
int utf_query_string(utf_context* utf, int row, const char* column_name, const char** value) {
|
||||
return utf_query_value(utf, row, utf_get_column(utf, column_name), (void*)value, COLUMN_TYPE_STRING);
|
||||
}
|
||||
|
||||
int utf_query_data(utf_context* utf, int row, const char* column_name, uint32_t* p_offset, uint32_t* p_size) {
|
||||
return utf_query_col_data(utf, row, utf_get_column(utf, column_name), p_offset, p_size);
|
||||
}
|
||||
|
@ -23,16 +23,30 @@ typedef struct utf_context utf_context;
|
||||
/* open a CRI UTF table at offset, returning table name and rows. Passed streamfile is used internally for next calls */
|
||||
utf_context* utf_open(STREAMFILE* sf, uint32_t table_offset, int* p_rows, const char** p_row_name);
|
||||
void utf_close(utf_context* utf);
|
||||
/* query calls */
|
||||
int utf_query_s8(utf_context* utf, int row, const char* column, int8_t* value);
|
||||
int utf_query_u8(utf_context* utf, int row, const char* column, uint8_t* value);
|
||||
int utf_query_s16(utf_context* utf, int row, const char* column, int16_t* value);
|
||||
int utf_query_u16(utf_context* utf, int row, const char* column, uint16_t* value);
|
||||
int utf_query_s32(utf_context* utf, int row, const char* column, int32_t* value);
|
||||
int utf_query_u32(utf_context* utf, int row, const char* column, uint32_t* value);
|
||||
int utf_query_s64(utf_context* utf, int row, const char* column, int64_t* value);
|
||||
int utf_query_u64(utf_context* utf, int row, const char* column, uint64_t* value);
|
||||
int utf_query_string(utf_context* utf, int row, const char* column, const char** value);
|
||||
int utf_query_data(utf_context* utf, int row, const char* column, uint32_t* offset, uint32_t* size);
|
||||
|
||||
int utf_get_column(utf_context* utf, const char* column);
|
||||
|
||||
/* query calls (passing column index is faster, when you have to read lots of rows) */
|
||||
int utf_query_col_s8(utf_context* utf, int row, int column, int8_t* value);
|
||||
int utf_query_col_u8(utf_context* utf, int row, int column, uint8_t* value);
|
||||
int utf_query_col_s16(utf_context* utf, int row, int column, int16_t* value);
|
||||
int utf_query_col_u16(utf_context* utf, int row, int column, uint16_t* value);
|
||||
int utf_query_col_s32(utf_context* utf, int row, int column, int32_t* value);
|
||||
int utf_query_col_u32(utf_context* utf, int row, int column, uint32_t* value);
|
||||
int utf_query_col_s64(utf_context* utf, int row, int column, int64_t* value);
|
||||
int utf_query_col_u64(utf_context* utf, int row, int column, uint64_t* value);
|
||||
int utf_query_col_string(utf_context* utf, int row, int column, const char** value);
|
||||
int utf_query_col_data(utf_context* utf, int row, int column, uint32_t* offset, uint32_t* size);
|
||||
|
||||
int utf_query_s8(utf_context* utf, int row, const char* column_name, int8_t* value);
|
||||
int utf_query_u8(utf_context* utf, int row, const char* column_name, uint8_t* value);
|
||||
int utf_query_s16(utf_context* utf, int row, const char* column_name, int16_t* value);
|
||||
int utf_query_u16(utf_context* utf, int row, const char* column_name, uint16_t* value);
|
||||
int utf_query_s32(utf_context* utf, int row, const char* column_name, int32_t* value);
|
||||
int utf_query_u32(utf_context* utf, int row, const char* column_name, uint32_t* value);
|
||||
int utf_query_s64(utf_context* utf, int row, const char* column_name, int64_t* value);
|
||||
int utf_query_u64(utf_context* utf, int row, const char* column_name, uint64_t* value);
|
||||
int utf_query_string(utf_context* utf, int row, const char* column_name, const char** value);
|
||||
int utf_query_data(utf_context* utf, int row, const char* column_name, uint32_t* offset, uint32_t* size);
|
||||
|
||||
#endif /* _CRI_UTF_H_ */
|
||||
|
@ -19,15 +19,15 @@ typedef struct {
|
||||
int32_t num_samples;
|
||||
int32_t loop_start;
|
||||
int loop_flag;
|
||||
off_t extra_offset;
|
||||
uint32_t extra_offset;
|
||||
uint32_t channel_layout;
|
||||
|
||||
int is_external;
|
||||
uint32_t stream_offsets[MAX_CHANNELS];
|
||||
uint32_t stream_sizes[MAX_CHANNELS];
|
||||
|
||||
off_t sound_name_offset;
|
||||
off_t config_name_offset;
|
||||
uint32_t sound_name_offset;
|
||||
uint32_t config_name_offset;
|
||||
char name[255+1];
|
||||
} ktsr_header;
|
||||
|
||||
@ -266,12 +266,12 @@ static int parse_codec(ktsr_header* ktsr) {
|
||||
|
||||
return 1;
|
||||
fail:
|
||||
VGM_LOG("KTSR: unknown codec combo: ext=%x, fmt=%x, ptf=%x\n", ktsr->is_external, ktsr->format, ktsr->platform);
|
||||
VGM_LOG("ktsr: unknown codec combo: ext=%x, fmt=%x, ptf=%x\n", ktsr->is_external, ktsr->format, ktsr->platform);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_ktsr_subfile(ktsr_header* ktsr, STREAMFILE* sf, off_t offset) {
|
||||
off_t suboffset, starts_offset, sizes_offset;
|
||||
static int parse_ktsr_subfile(ktsr_header* ktsr, STREAMFILE* sf, uint32_t offset) {
|
||||
uint32_t suboffset, starts_offset, sizes_offset;
|
||||
int i;
|
||||
uint32_t type;
|
||||
|
||||
@ -318,7 +318,7 @@ static int parse_ktsr_subfile(ktsr_header* ktsr, STREAMFILE* sf, off_t offset) {
|
||||
ktsr->is_external = 1;
|
||||
|
||||
if (ktsr->format != 0x05) {
|
||||
VGM_LOG("KTSR: unknown subcodec at %lx\n", offset);
|
||||
VGM_LOG("ktsr: unknown subcodec at %x\n", offset);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
@ -362,7 +362,7 @@ static int parse_ktsr_subfile(ktsr_header* ktsr, STREAMFILE* sf, off_t offset) {
|
||||
suboffset = offset + 0x30;
|
||||
|
||||
if (ktsr->channels > MAX_CHANNELS) {
|
||||
VGM_LOG("KTSR: max channels found\n");
|
||||
VGM_LOG("ktsr: max channels found\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
@ -379,7 +379,7 @@ static int parse_ktsr_subfile(ktsr_header* ktsr, STREAMFILE* sf, off_t offset) {
|
||||
|
||||
default:
|
||||
/* streams also have their own chunks like 0x09D4F415, not needed here */
|
||||
VGM_LOG("KTSR: unknown subheader at %lx\n", offset);
|
||||
VGM_LOG("ktsr: unknown subheader at %x\n", offset);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
@ -388,7 +388,7 @@ static int parse_ktsr_subfile(ktsr_header* ktsr, STREAMFILE* sf, off_t offset) {
|
||||
|
||||
return 1;
|
||||
fail:
|
||||
VGM_LOG("KTSR: error parsing subheader\n");
|
||||
VGM_LOG("ktsr: error parsing subheader\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -419,7 +419,7 @@ static void build_name(ktsr_header* ktsr, STREAMFILE* sf) {
|
||||
|
||||
static void parse_longname(ktsr_header* ktsr, STREAMFILE* sf, uint32_t target_id) {
|
||||
/* more configs than sounds is possible so we need target_id first */
|
||||
off_t offset, end, name_offset;
|
||||
uint32_t offset, end, name_offset;
|
||||
uint32_t stream_id;
|
||||
|
||||
offset = 0x40;
|
||||
@ -447,7 +447,7 @@ static void parse_longname(ktsr_header* ktsr, STREAMFILE* sf, uint32_t target_id
|
||||
}
|
||||
|
||||
static int parse_ktsr(ktsr_header* ktsr, STREAMFILE* sf) {
|
||||
off_t offset, end, header_offset, name_offset;
|
||||
uint32_t offset, end, header_offset, name_offset;
|
||||
uint32_t stream_id = 0, stream_count;
|
||||
|
||||
/* 00: KTSR
|
||||
@ -486,7 +486,6 @@ static int parse_ktsr(ktsr_header* ktsr, STREAMFILE* sf) {
|
||||
break;
|
||||
|
||||
case 0xC5CCCB70: /* sound (internal data or external stream) */
|
||||
//VGM_LOG("info at %lx\n", offset);
|
||||
ktsr->total_subsongs++;
|
||||
|
||||
/* sound table:
|
||||
@ -503,13 +502,12 @@ static int parse_ktsr(ktsr_header* ktsr, STREAMFILE* sf) {
|
||||
|
||||
|
||||
if (ktsr->total_subsongs == ktsr->target_subsong) {
|
||||
//;VGM_LOG("KTSR: target at %lx\n", offset);
|
||||
|
||||
stream_id = read_u32be(offset + 0x08,sf);
|
||||
//ktsr->is_external = read_u16le(offset + 0x0e,sf);
|
||||
stream_count = read_u32le(offset + 0x10,sf);
|
||||
if (stream_count != 1) {
|
||||
VGM_LOG("KTSR: unknown stream count\n");
|
||||
VGM_LOG("ktsr: unknown stream count\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
@ -527,7 +525,7 @@ static int parse_ktsr(ktsr_header* ktsr, STREAMFILE* sf) {
|
||||
|
||||
default:
|
||||
/* streams also have their own chunks like 0x09D4F415, not needed here */
|
||||
VGM_LOG("KTSR: unknown chunk at %lx\n", offset);
|
||||
VGM_LOG("ktsr: unknown chunk at %x\n", offset);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
@ -542,5 +540,6 @@ static int parse_ktsr(ktsr_header* ktsr, STREAMFILE* sf) {
|
||||
|
||||
return 1;
|
||||
fail:
|
||||
vgm_logi("KTSR: unknown variation (report)\n");
|
||||
return 0;
|
||||
}
|
||||
|
218
src/meta/psb.c
218
src/meta/psb.c
@ -1,10 +1,10 @@
|
||||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
#include "../util/m2_psb.h"
|
||||
#include "../layout/layout.h"
|
||||
|
||||
|
||||
//todo prepare multichannel
|
||||
#define PSB_MAX_LAYERS 1
|
||||
#define PSB_MAX_LAYERS 2
|
||||
|
||||
typedef enum { PCM, RIFF_AT3, XMA2, MSADPCM, XWMA, DSP, OPUSNX, RIFF_AT9, VAG } psb_codec_t;
|
||||
typedef struct {
|
||||
@ -25,8 +25,10 @@ typedef struct {
|
||||
int target_subsong;
|
||||
|
||||
/* chunks references */
|
||||
uint32_t stream_offset;
|
||||
uint32_t stream_size;
|
||||
uint32_t stream_offset[PSB_MAX_LAYERS];
|
||||
uint32_t stream_size[PSB_MAX_LAYERS];
|
||||
uint32_t body_offset;
|
||||
uint32_t body_size;
|
||||
uint32_t intro_offset;
|
||||
uint32_t intro_size;
|
||||
uint32_t fmt_offset;
|
||||
@ -43,6 +45,7 @@ typedef struct {
|
||||
int bps;
|
||||
|
||||
int32_t num_samples;
|
||||
int32_t body_samples;
|
||||
int32_t intro_samples;
|
||||
int32_t skip_samples;
|
||||
int loop_flag;
|
||||
@ -55,6 +58,10 @@ typedef struct {
|
||||
static int parse_psb(STREAMFILE* sf, psb_header_t* psb);
|
||||
|
||||
|
||||
static segmented_layout_data* build_segmented_psb_opus(STREAMFILE* sf, psb_header_t* psb);
|
||||
static layered_layout_data* build_layered_psb(STREAMFILE* sf, psb_header_t* psb);
|
||||
|
||||
|
||||
/* PSB - M2 container [Sega Vintage Collection (multi), Legend of Mana (multi)] */
|
||||
VGMSTREAM* init_vgmstream_psb(STREAMFILE* sf) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
@ -82,7 +89,7 @@ VGMSTREAM* init_vgmstream_psb(STREAMFILE* sf) {
|
||||
init_vgmstream = init_vgmstream_riff;
|
||||
break;
|
||||
|
||||
case VAG: /* Plastic Memories (Vita) */
|
||||
case VAG: /* Plastic Memories (Vita), Judgment (PS4) */
|
||||
ext = "vag";
|
||||
init_vgmstream = init_vgmstream_vag;
|
||||
break;
|
||||
@ -97,7 +104,7 @@ VGMSTREAM* init_vgmstream_psb(STREAMFILE* sf) {
|
||||
}
|
||||
|
||||
if (init_vgmstream != NULL) {
|
||||
STREAMFILE* temp_sf = setup_subfile_streamfile(sf, psb.stream_offset, psb.stream_size, ext);
|
||||
STREAMFILE* temp_sf = setup_subfile_streamfile(sf, psb.stream_offset[0], psb.stream_size[0], ext);
|
||||
if (!temp_sf) goto fail;
|
||||
|
||||
vgmstream = init_vgmstream(temp_sf);
|
||||
@ -121,21 +128,19 @@ VGMSTREAM* init_vgmstream_psb(STREAMFILE* sf) {
|
||||
vgmstream->loop_start_sample = psb.loop_start;
|
||||
vgmstream->loop_end_sample = psb.loop_end;
|
||||
vgmstream->num_streams = psb.total_subsongs;
|
||||
vgmstream->stream_size = psb.stream_size;
|
||||
vgmstream->stream_size = psb.stream_size[0];
|
||||
|
||||
switch(psb.codec) {
|
||||
case PCM:
|
||||
switch(psb.bps) {
|
||||
case 16: vgmstream->coding_type = coding_PCM16LE; break; /* Legend of Mana (PC), Namco Museum Archives Vol.1 (PC) */
|
||||
case 24: vgmstream->coding_type = coding_PCM24LE; break; /* Legend of Mana (PC) */
|
||||
default:
|
||||
vgm_logi("PSB: unknown bps %i (report)\n", psb.bps);
|
||||
goto fail;
|
||||
default: goto fail;
|
||||
}
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = psb.block_size / psb.channels;
|
||||
if (!vgmstream->num_samples)
|
||||
vgmstream->num_samples = pcm_bytes_to_samples(psb.stream_size, psb.channels, psb.bps);
|
||||
vgmstream->num_samples = pcm_bytes_to_samples(psb.stream_size[0], psb.channels, psb.bps);
|
||||
break;
|
||||
|
||||
case MSADPCM: /* [Senxin Aleste (AC)] */
|
||||
@ -143,12 +148,12 @@ VGMSTREAM* init_vgmstream_psb(STREAMFILE* sf) {
|
||||
vgmstream->layout_type = layout_none;
|
||||
vgmstream->frame_size = psb.block_size;
|
||||
if (!vgmstream->num_samples)
|
||||
vgmstream->num_samples = msadpcm_bytes_to_samples(psb.stream_size, psb.block_size, psb.channels);
|
||||
vgmstream->num_samples = msadpcm_bytes_to_samples(psb.stream_size[0], psb.block_size, psb.channels);
|
||||
break;
|
||||
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
case XWMA: { /* [Senxin Aleste (AC)] */
|
||||
vgmstream->codec_data = init_ffmpeg_xwma(sf, psb.stream_offset, psb.stream_size, psb.format, psb.channels, psb.sample_rate, psb.avg_bitrate, psb.block_size);
|
||||
vgmstream->codec_data = init_ffmpeg_xwma(sf, psb.stream_offset[0], psb.stream_size[0], psb.format, psb.channels, psb.sample_rate, psb.avg_bitrate, psb.block_size);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->coding_type = coding_FFmpeg;
|
||||
vgmstream->layout_type = layout_none;
|
||||
@ -164,27 +169,52 @@ VGMSTREAM* init_vgmstream_psb(STREAMFILE* sf) {
|
||||
uint8_t buf[0x100];
|
||||
size_t bytes;
|
||||
|
||||
bytes = ffmpeg_make_riff_xma_from_fmt_chunk(buf, sizeof(buf), psb.fmt_offset, psb.fmt_size, psb.stream_size, sf, 1);
|
||||
vgmstream->codec_data = init_ffmpeg_header_offset(sf, buf, bytes, psb.stream_offset, psb.stream_size);
|
||||
bytes = ffmpeg_make_riff_xma_from_fmt_chunk(buf, sizeof(buf), psb.fmt_offset, psb.fmt_size, psb.stream_size[0], sf, 1);
|
||||
vgmstream->codec_data = init_ffmpeg_header_offset(sf, buf, bytes, psb.stream_offset[0], psb.stream_size[0]);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->coding_type = coding_FFmpeg;
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
||||
xma_fix_raw_samples(vgmstream, sf, psb.stream_offset, psb.stream_size, psb.fmt_offset, 1,1);
|
||||
xma_fix_raw_samples(vgmstream, sf, psb.stream_offset[0], psb.stream_size[0], psb.fmt_offset, 1,1);
|
||||
break;
|
||||
}
|
||||
|
||||
case OPUSNX: { /* Legend of Mana (Switch) */
|
||||
vgmstream->layout_data = build_segmented_psb_opus(sf, &psb);
|
||||
if (!vgmstream->layout_data) goto fail;
|
||||
vgmstream->coding_type = coding_FFmpeg;
|
||||
vgmstream->layout_type = layout_segmented;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
case DSP: /* Legend of Mana (Switch) */
|
||||
case OPUSNX: /* Legend of Mana (Switch) */
|
||||
/* standard DSP resources */
|
||||
if (psb.layers > 1) {
|
||||
/* somehow R offset can go before L, use layered */
|
||||
vgmstream->layout_data = build_layered_psb(sf, &psb);
|
||||
if (!vgmstream->layout_data) goto fail;
|
||||
vgmstream->coding_type = coding_NGC_DSP;
|
||||
vgmstream->layout_type = layout_layered;
|
||||
}
|
||||
else {
|
||||
vgmstream->coding_type = coding_NGC_DSP;
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
||||
dsp_read_coefs_le(vgmstream,sf, psb.stream_offset[0] + 0x1c, 0);
|
||||
dsp_read_hist_le(vgmstream,sf, psb.stream_offset[0] + 0x1c + 0x20, 0);
|
||||
}
|
||||
|
||||
vgmstream->num_samples = read_u32le(psb.stream_offset[0] + 0x00, sf);
|
||||
break;
|
||||
|
||||
default:
|
||||
vgm_logi("PSB: not implemented (ignore)\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
strncpy(vgmstream->stream_name, psb.readable_name, STREAM_NAME_SIZE);
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream, sf, psb.stream_offset))
|
||||
if (!vgmstream_open_stream(vgmstream, sf, psb.stream_offset[0]))
|
||||
goto fail;
|
||||
return vgmstream;
|
||||
|
||||
@ -193,6 +223,103 @@ fail:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static segmented_layout_data* build_segmented_psb_opus(STREAMFILE* sf, psb_header_t* psb) {
|
||||
segmented_layout_data* data = NULL;
|
||||
int i, pos = 0, segment_count = 0, max_count = 2;
|
||||
|
||||
//TODO improve
|
||||
//TODO these use standard switch opus (VBR), could sub-file? but skip_samples becomes more complex
|
||||
|
||||
uint32_t offsets[] = {psb->intro_offset, psb->body_offset};
|
||||
uint32_t sizes[] = {psb->intro_size, psb->body_size};
|
||||
uint32_t samples[] = {psb->intro_samples, psb->body_samples};
|
||||
uint32_t skips[] = {0, psb->skip_samples};
|
||||
|
||||
/* intro + body (looped songs) or just body (standard songs)
|
||||
in full loops intro is 0 samples with a micro 1-frame opus [Nekopara (Switch)] */
|
||||
if (offsets[0] && samples[0])
|
||||
segment_count++;
|
||||
if (offsets[1] && samples[1])
|
||||
segment_count++;
|
||||
|
||||
/* init layout */
|
||||
data = init_layout_segmented(segment_count);
|
||||
if (!data) goto fail;
|
||||
|
||||
for (i = 0; i < max_count; i++) {
|
||||
if (!offsets[i] || !samples[i])
|
||||
continue;
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
{
|
||||
int start = read_u32le(offsets[i] + 0x10, sf) + 0x08;
|
||||
int skip = read_s16le(offsets[i] + 0x1c, sf);
|
||||
|
||||
VGMSTREAM* v = allocate_vgmstream(psb->channels, 0);
|
||||
if (!v) goto fail;
|
||||
|
||||
data->segments[pos++] = v;
|
||||
v->sample_rate = psb->sample_rate;
|
||||
v->num_samples = samples[i];
|
||||
v->codec_data = init_ffmpeg_switch_opus(sf, offsets[i] + start, sizes[i] - start, psb->channels, skips[i] + skip, psb->sample_rate);
|
||||
if (!v->codec_data) goto fail;
|
||||
v->coding_type = coding_FFmpeg;
|
||||
v->layout_type = layout_none;
|
||||
}
|
||||
#else
|
||||
goto fail;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (!setup_layout_segmented(data))
|
||||
goto fail;
|
||||
|
||||
return data;
|
||||
fail:
|
||||
free_layout_segmented(data);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static layered_layout_data* build_layered_psb(STREAMFILE* sf, psb_header_t* psb) {
|
||||
layered_layout_data* data = NULL;
|
||||
int i;
|
||||
|
||||
|
||||
/* init layout */
|
||||
data = init_layout_layered(psb->layers);
|
||||
if (!data) goto fail;
|
||||
|
||||
for (i = 0; i < psb->layers; i++) {
|
||||
STREAMFILE* temp_sf = NULL;
|
||||
VGMSTREAM* (*init_vgmstream)(STREAMFILE* sf) = NULL;
|
||||
const char* extension = NULL;
|
||||
|
||||
switch (psb->codec) {
|
||||
case DSP:
|
||||
extension = "adpcm";
|
||||
init_vgmstream = init_vgmstream_ngc_dsp_std_le;
|
||||
break;
|
||||
default:
|
||||
goto fail;
|
||||
}
|
||||
|
||||
temp_sf = setup_subfile_streamfile(sf, psb->stream_offset[i], psb->stream_size[i], extension);
|
||||
if (!temp_sf) goto fail;
|
||||
|
||||
data->layers[i] = init_vgmstream(temp_sf);
|
||||
close_streamfile(temp_sf);
|
||||
if (!data->layers[i]) goto fail;
|
||||
}
|
||||
|
||||
/* setup layered VGMSTREAMs */
|
||||
if (!setup_layout_layered(data))
|
||||
goto fail;
|
||||
return data;
|
||||
fail:
|
||||
free_layout_layered(data);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
static int prepare_fmt(STREAMFILE* sf, psb_header_t* psb) {
|
||||
@ -272,11 +399,20 @@ static int prepare_codec(STREAMFILE* sf, psb_header_t* psb) {
|
||||
|
||||
if (strcmp(ext, ".opus") == 0) {
|
||||
psb->codec = OPUSNX;
|
||||
|
||||
psb->body_samples -= psb->skip_samples;
|
||||
if (!psb->loop_flag)
|
||||
psb->loop_flag = psb->intro_samples > 0;
|
||||
psb->loop_start = psb->intro_samples;
|
||||
psb->loop_end = psb->body_samples + psb->intro_samples;
|
||||
psb->num_samples = psb->intro_samples + psb->body_samples;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (strcmp(ext, ".adpcm") == 0) {
|
||||
psb->codec = DSP;
|
||||
|
||||
psb->channels = psb->layers;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
@ -286,8 +422,8 @@ static int prepare_codec(STREAMFILE* sf, psb_header_t* psb) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (strcmp(spec, "vita") == 0) {
|
||||
if (is_id32be(psb->stream_offset, sf, "RIFF"))
|
||||
if (strcmp(spec, "vita") == 0 || strcmp(spec, "ps4") == 0) {
|
||||
if (is_id32be(psb->stream_offset[0], sf, "RIFF"))
|
||||
psb->codec = RIFF_AT9;
|
||||
else
|
||||
psb->codec = VAG;
|
||||
@ -374,8 +510,8 @@ static int parse_psb_channels(psb_header_t* psb, psb_node_t* nchans) {
|
||||
switch (type) {
|
||||
case PSB_TYPE_DATA: /* Sega Vintage Collection (PS3) */
|
||||
data = psb_node_get_result(&narch).data;
|
||||
psb->stream_offset = data.offset;
|
||||
psb->stream_size = data.size;
|
||||
psb->stream_offset[i] = data.offset;
|
||||
psb->stream_size[i] = data.size;
|
||||
break;
|
||||
|
||||
case PSB_TYPE_OBJECT: /* rest */
|
||||
@ -386,8 +522,8 @@ static int parse_psb_channels(psb_header_t* psb, psb_node_t* nchans) {
|
||||
|
||||
data = psb_node_get_data(&narch, "data");
|
||||
if (data.offset) {
|
||||
psb->stream_offset = data.offset;
|
||||
psb->stream_size = data.size;
|
||||
psb->stream_offset[i] = data.offset;
|
||||
psb->stream_size[i] = data.size;
|
||||
}
|
||||
|
||||
data = psb_node_get_data(&narch, "fmt");
|
||||
@ -408,23 +544,20 @@ static int parse_psb_channels(psb_header_t* psb, psb_node_t* nchans) {
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
if (psb_node_by_key(&narch, "body", &node)) {
|
||||
data = psb_node_get_data(&node, "data");
|
||||
psb->stream_offset = data.offset;
|
||||
psb->stream_size = data.size;
|
||||
psb->num_samples = psb_node_get_integer(&node, "sampleCount");
|
||||
psb->body_offset = data.offset;
|
||||
psb->body_size = data.size;
|
||||
psb->body_samples = psb_node_get_integer(&node, "sampleCount");
|
||||
psb->skip_samples = psb_node_get_integer(&node, "skipSampleCount");
|
||||
}
|
||||
|
||||
if (psb_node_by_key(&narch, "intro", &node)) {
|
||||
data = psb_node_get_data(&node, "data");
|
||||
psb->stream_offset = data.offset;
|
||||
psb->stream_size = data.size;
|
||||
psb->num_samples = psb_node_get_integer(&node, "sampleCount");
|
||||
psb->skip_samples = psb_node_get_integer(&node, "skipSampleCount");
|
||||
psb->intro_offset = data.offset;
|
||||
psb->intro_size = data.size;
|
||||
psb->intro_samples = psb_node_get_integer(&node, "sampleCount");
|
||||
}
|
||||
#endif
|
||||
|
||||
data = psb_node_get_data(&narch, "dpds");
|
||||
if (data.offset) {
|
||||
@ -432,10 +565,25 @@ static int parse_psb_channels(psb_header_t* psb, psb_node_t* nchans) {
|
||||
psb->dpds_size = data.size;
|
||||
}
|
||||
|
||||
psb->sample_rate = (int)psb_node_get_float(&narch, "samprate");
|
||||
psb->channels = psb_node_get_integer(&narch, "channelCount");
|
||||
|
||||
psb->sample_rate = (int)psb_node_get_float(&narch, "samprate"); /* seen in DSP */
|
||||
if (!psb->sample_rate)
|
||||
psb->sample_rate = psb_node_get_integer(&narch, "samprate"); /* seen in OpusNX */
|
||||
|
||||
psb->tmp->ext = psb_node_get_string(&narch, "ext"); /* appears for all channels, assumed to be the same */
|
||||
|
||||
psb->tmp->wav = psb_node_get_string(&narch, "wav");
|
||||
|
||||
/* DSP has a "pan" array like: [1.0, 0.0]=L, [0.0, 1.0 ]=R */
|
||||
if (psb_node_by_key(&narch, "pan", &node)) {
|
||||
|
||||
psb_node_by_index(&node, i, &nsub);
|
||||
if (psb_node_get_result(&nsub).flt != 1.0f) {
|
||||
vgm_logi("PSB: unexpected pan (report)\n");
|
||||
};
|
||||
}
|
||||
|
||||
/* background: false?
|
||||
*/
|
||||
break;
|
||||
|
@ -16,22 +16,13 @@
|
||||
* - Clang: seems only defined on Linux/GNU environments, somehow emscripten is out
|
||||
* (unsure about Clang Win since apparently they define _MSC_VER)
|
||||
* - Android: API +24 if not using __USE_FILE_OFFSET64
|
||||
* Not sure if fopen64 is needed in some cases. May be work adding some compiler flag to control this manually.
|
||||
* Not sure if fopen64 is needed in some cases. May be worth adding some compiler flag to enable 64 versions manually.
|
||||
*/
|
||||
|
||||
/* MSVC fixes (though mingw uses MSVCRT but not MSC_VER, maybe use AND?) */
|
||||
#if defined(__MSVCRT__) || defined(_MSC_VER)
|
||||
#include <io.h>
|
||||
|
||||
/*
|
||||
#ifndef fseeko
|
||||
#define fseeko fseek
|
||||
#endif
|
||||
#ifndef ftello
|
||||
#define ftello ftell
|
||||
#endif
|
||||
*/
|
||||
|
||||
#define fopen_v fopen
|
||||
#if (_MSC_VER >= 1400)
|
||||
#define fseek_v _fseeki64
|
||||
@ -48,9 +39,9 @@
|
||||
#define fdopen _fdopen
|
||||
#define dup _dup
|
||||
|
||||
#ifndef off64_t
|
||||
#define off_t __int64
|
||||
#endif
|
||||
//#ifndef off64_t
|
||||
// #define off_t/off64_t __int64
|
||||
//#endif
|
||||
|
||||
#elif defined(XBMC) || defined(__EMSCRIPTEN__) || defined (__ANDROID__)
|
||||
#define fopen_v fopen
|
||||
@ -70,6 +61,7 @@ typedef struct {
|
||||
|
||||
FILE* infile; /* actual FILE */
|
||||
char name[PATH_LIMIT]; /* FILE filename */
|
||||
int name_len; /* cache */
|
||||
offv_t offset; /* last read offset (info) */
|
||||
offv_t buf_offset; /* current buffer data start */
|
||||
uint8_t* buf; /* data buffer */
|
||||
@ -87,7 +79,7 @@ static size_t stdio_read(STDIO_STREAMFILE* sf, uint8_t* dst, offv_t offset, size
|
||||
if (!sf->infile || !dst || length <= 0 || offset < 0)
|
||||
return 0;
|
||||
|
||||
//;VGM_LOG("STDIO: read %lx + %x (buf %lx + %x)\n", offset, length, sf->buf_offset, sf->valid_size);
|
||||
//;VGM_LOG("stdio: read %lx + %x (buf %lx + %x)\n", offset, length, sf->buf_offset, sf->valid_size);
|
||||
|
||||
/* is the part of the requested length in the buffer? */
|
||||
if (offset >= sf->buf_offset && offset < sf->buf_offset + sf->valid_size) {
|
||||
@ -98,7 +90,7 @@ static size_t stdio_read(STDIO_STREAMFILE* sf, uint8_t* dst, offv_t offset, size
|
||||
if (buf_limit > length)
|
||||
buf_limit = length;
|
||||
|
||||
//;VGM_LOG("STDIO: copy buf %lx + %x (+ %x) (buf %lx + %x)\n", offset, length_to_read, (length - length_to_read), sf->buf_offset, sf->valid_size);
|
||||
//;VGM_LOG("stdio: copy buf %lx + %x (+ %x) (buf %lx + %x)\n", offset, length_to_read, (length - length_to_read), sf->buf_offset, sf->valid_size);
|
||||
|
||||
memcpy(dst, sf->buf + buf_into, buf_limit);
|
||||
read_total += buf_limit;
|
||||
@ -109,7 +101,7 @@ static size_t stdio_read(STDIO_STREAMFILE* sf, uint8_t* dst, offv_t offset, size
|
||||
|
||||
#ifdef VGM_DEBUG_OUTPUT
|
||||
if (offset < sf->buf_offset && length > 0) {
|
||||
VGM_LOG("STDIO: rebuffer, requested %lx vs %lx (sf %x)\n", offset, sf->buf_offset, (uint32_t)sf);
|
||||
VGM_LOG("stdio: rebuffer, requested %x vs %x (sf %x)\n", (uint32_t)offset, (uint32_t)sf->buf_offset, (uint32_t)sf);
|
||||
//sf->rebuffer++;
|
||||
//if (rebuffer > N) ...
|
||||
}
|
||||
@ -143,7 +135,7 @@ static size_t stdio_read(STDIO_STREAMFILE* sf, uint8_t* dst, offv_t offset, size
|
||||
/* fill the buffer (offset now is beyond buf_offset) */
|
||||
sf->buf_offset = offset;
|
||||
sf->valid_size = fread(sf->buf, sizeof(uint8_t), sf->buf_size, sf->infile);
|
||||
//;VGM_LOG("STDIO: read buf %lx + %x\n", sf->buf_offset, sf->valid_size);
|
||||
//;VGM_LOG("stdio: read buf %lx + %x\n", sf->buf_offset, sf->valid_size);
|
||||
|
||||
/* decide how much must be read this time */
|
||||
if (length > sf->buf_size)
|
||||
@ -177,8 +169,12 @@ static offv_t stdio_get_offset(STDIO_STREAMFILE* sf) {
|
||||
return sf->offset;
|
||||
}
|
||||
static void stdio_get_name(STDIO_STREAMFILE* sf, char* name, size_t name_size) {
|
||||
strncpy(name, sf->name, name_size);
|
||||
name[name_size - 1] = '\0';
|
||||
int copy_size = sf->name_len + 1;
|
||||
if (copy_size > name_size)
|
||||
copy_size = name_size;
|
||||
|
||||
memcpy(name, sf->name, copy_size);
|
||||
name[copy_size - 1] = '\0';
|
||||
}
|
||||
|
||||
static STREAMFILE* stdio_open(STDIO_STREAMFILE* sf, const char* const filename, size_t buf_size) {
|
||||
@ -241,8 +237,11 @@ static STREAMFILE* open_stdio_streamfile_buffer_by_file(FILE* infile, const char
|
||||
this_sf->buf_size = buf_size;
|
||||
this_sf->buf = buf;
|
||||
|
||||
strncpy(this_sf->name, filename, sizeof(this_sf->name));
|
||||
this_sf->name[sizeof(this_sf->name)-1] = '\0';
|
||||
this_sf->name_len = strlen(filename);
|
||||
if (this_sf->name_len >= sizeof(this_sf->name))
|
||||
goto fail;
|
||||
memcpy(this_sf->name, filename, this_sf->name_len);
|
||||
this_sf->name[this_sf->name_len] = '\0';
|
||||
|
||||
/* cache file_size */
|
||||
if (infile) {
|
||||
@ -335,7 +334,7 @@ static size_t buffer_read(BUFFER_STREAMFILE* sf, uint8_t* dst, offv_t offset, si
|
||||
|
||||
#ifdef VGM_DEBUG_OUTPUT
|
||||
if (offset < sf->buf_offset) {
|
||||
VGM_LOG("BUFFER: rebuffer, requested %lx vs %lx (sf %x)\n", offset, sf->buf_offset, (uint32_t)sf);
|
||||
VGM_LOG("buffer: rebuffer, requested %x vs %x (sf %x)\n", (uint32_t)offset, (uint32_t)sf->buf_offset, (uint32_t)sf);
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -346,7 +345,7 @@ static size_t buffer_read(BUFFER_STREAMFILE* sf, uint8_t* dst, offv_t offset, si
|
||||
/* ignore requests at EOF */
|
||||
if (offset >= sf->file_size) {
|
||||
//offset = sf->file_size; /* seems fseek doesn't clamp offset */
|
||||
VGM_ASSERT_ONCE(offset > sf->file_size, "BUFFER: reading over file_size 0x%x @ 0x%x + 0x%x\n", sf->file_size, (uint32_t)offset, length);
|
||||
VGM_ASSERT_ONCE(offset > sf->file_size, "buffer: reading over file_size 0x%x @ 0x%x + 0x%x\n", sf->file_size, (uint32_t)offset, length);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -598,14 +597,15 @@ typedef struct {
|
||||
STREAMFILE* inner_sf;
|
||||
void* data; /* state for custom reads, malloc'ed + copied on open (to re-open streamfiles cleanly) */
|
||||
size_t data_size;
|
||||
size_t (*read_callback)(STREAMFILE*, uint8_t*, offv_t, size_t, void*); /* custom read to modify data before copying into buffer */
|
||||
size_t (*read_callback)(STREAMFILE*, uint8_t*, off_t, size_t, void*); /* custom read to modify data before copying into buffer */
|
||||
size_t (*size_callback)(STREAMFILE*, void*); /* size when custom reads make data smaller/bigger than underlying streamfile */
|
||||
int (*init_callback)(STREAMFILE*, void*); /* init the data struct members somehow, return >= 0 if ok */
|
||||
void (*close_callback)(STREAMFILE*, void*); /* close the data struct members somehow */
|
||||
/* read doesn't use offv_t since callbacks would need to be modified */
|
||||
} IO_STREAMFILE;
|
||||
|
||||
static size_t io_read(IO_STREAMFILE* sf, uint8_t* dst, offv_t offset, size_t length) {
|
||||
return sf->read_callback(sf->inner_sf, dst, offset, length, sf->data);
|
||||
return sf->read_callback(sf->inner_sf, dst, (off_t)offset, length, sf->data);
|
||||
}
|
||||
static size_t io_get_size(IO_STREAMFILE* sf) {
|
||||
if (sf->size_callback)
|
||||
@ -697,6 +697,7 @@ typedef struct {
|
||||
|
||||
STREAMFILE* inner_sf;
|
||||
char fakename[PATH_LIMIT];
|
||||
int fakename_len;
|
||||
} FAKENAME_STREAMFILE;
|
||||
|
||||
static size_t fakename_read(FAKENAME_STREAMFILE* sf, uint8_t* dst, offv_t offset, size_t length) {
|
||||
@ -709,8 +710,11 @@ static offv_t fakename_get_offset(FAKENAME_STREAMFILE* sf) {
|
||||
return sf->inner_sf->get_offset(sf->inner_sf); /* default */
|
||||
}
|
||||
static void fakename_get_name(FAKENAME_STREAMFILE* sf, char* name, size_t name_size) {
|
||||
strncpy(name,sf->fakename, name_size);
|
||||
name[name_size - 1] = '\0';
|
||||
int copy_size = sf->fakename_len + 1;
|
||||
if (copy_size > name_size)
|
||||
copy_size = name_size;
|
||||
memcpy(name, sf->fakename, copy_size);
|
||||
name[copy_size - 1] = '\0';
|
||||
}
|
||||
|
||||
static STREAMFILE* fakename_open(FAKENAME_STREAMFILE* sf, const char* const filename, size_t buf_size) {
|
||||
@ -753,7 +757,7 @@ STREAMFILE* open_fakename_streamfile(STREAMFILE* sf, const char* fakename, const
|
||||
|
||||
/* copy passed name or retain current, and swap extension if expected */
|
||||
if (fakename) {
|
||||
strcpy(this_sf->fakename,fakename);
|
||||
strcpy(this_sf->fakename, fakename);
|
||||
} else {
|
||||
sf->get_name(sf, this_sf->fakename, PATH_LIMIT);
|
||||
}
|
||||
@ -768,6 +772,8 @@ STREAMFILE* open_fakename_streamfile(STREAMFILE* sf, const char* fakename, const
|
||||
strcat(this_sf->fakename, fakeext);
|
||||
}
|
||||
|
||||
this_sf->fakename_len = strlen(this_sf->fakename);
|
||||
|
||||
return &this_sf->vt;
|
||||
}
|
||||
STREAMFILE* open_fakename_streamfile_f(STREAMFILE* sf, const char* fakename, const char* fakeext) {
|
||||
@ -1365,7 +1371,6 @@ static int find_chunk_internal(STREAMFILE* sf, uint32_t chunk_id, off_t start_of
|
||||
while (offset < max_offset) {
|
||||
uint32_t chunk_type = read_32bit_type(offset + 0x00,sf);
|
||||
uint32_t chunk_size = read_32bit_size(offset + 0x04,sf);
|
||||
//;VGM_LOG("CHUNK: type=%x, size=%x at %lx\n", chunk_type, chunk_size, offset);
|
||||
|
||||
if (chunk_type == 0xFFFFFFFF || chunk_size == 0xFFFFFFFF)
|
||||
return 0;
|
||||
@ -1503,7 +1508,7 @@ void dump_streamfile(STREAMFILE* sf, int num) {
|
||||
|
||||
bytes = read_streamfile(buf, offset, sizeof(buf), sf);
|
||||
if(!bytes) {
|
||||
VGM_LOG("dump streamfile: can't read at %lx\n", offset);
|
||||
VGM_LOG("dump streamfile: can't read at %x\n", (uint32_t)offset);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -20,10 +20,6 @@
|
||||
/* MSVC fixes (though mingw uses MSVCRT but not MSC_VER, maybe use AND?) */
|
||||
#if defined(__MSVCRT__) || defined(_MSC_VER)
|
||||
#include <io.h>
|
||||
|
||||
#ifndef off64_t
|
||||
#define off_t __int64
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef DIR_SEPARATOR
|
||||
|
10
src/util.h
10
src/util.h
@ -70,7 +70,15 @@ static inline float get_f32le(const uint8_t* p) {
|
||||
temp.u32 = get_u32le(p);
|
||||
return temp.f32;
|
||||
}
|
||||
static inline float get_d64le(const uint8_t* p) {
|
||||
static inline double get_d64be(const uint8_t* p) {
|
||||
union {
|
||||
uint64_t u64;
|
||||
double d64;
|
||||
} temp;
|
||||
temp.u64 = get_u64be(p);
|
||||
return temp.d64;
|
||||
}
|
||||
static inline double get_d64le(const uint8_t* p) {
|
||||
union {
|
||||
uint64_t u64;
|
||||
double d64;
|
||||
|
@ -15,12 +15,22 @@
|
||||
* - still WIP, some stuff not working ATM or may change
|
||||
*/
|
||||
|
||||
/* compiler hints to force printf-style checks, butt-ugly but so useful... */
|
||||
/* supposedly MSCV has _Printf_format_string_ with /analyze but I can't get it to work */
|
||||
#if defined(__GNUC__) /* clang too */
|
||||
#define GNUC_LOG_ATRIB __attribute__ ((format(printf, 1, 2))) /* only with -Wformat (1=format param, 2=other params) */
|
||||
#define GNUC_ASR_ATRIB __attribute__ ((format(printf, 2, 3)))
|
||||
#else
|
||||
#define GNUC_LOG_ATRIB /* none */
|
||||
#define GNUC_ASR_ATRIB /* none */
|
||||
#endif
|
||||
|
||||
// void (*callback)(int level, const char* str);
|
||||
void vgm_log_set_callback(void* ctx_p, int level, int type, void* callback);
|
||||
|
||||
#if defined(VGM_LOG_OUTPUT) || defined(VGM_DEBUG_OUTPUT)
|
||||
void vgm_logi(/*void* ctx,*/ const char* fmt, ...);
|
||||
void vgm_asserti(/*void* ctx,*/ int condition, const char* fmt, ...);
|
||||
void vgm_logi(/*void* ctx,*/ const char* fmt, ...) GNUC_LOG_ATRIB;
|
||||
void vgm_asserti(/*void* ctx,*/ int condition, const char* fmt, ...) GNUC_ASR_ATRIB;
|
||||
//void vgm_logi_once(/*void* ctx, int* once_flag, */ const char* fmt, ...);
|
||||
#else
|
||||
#define vgm_logi(...) /* nothing */
|
||||
@ -28,7 +38,7 @@ void vgm_log_set_callback(void* ctx_p, int level, int type, void* callback);
|
||||
#endif
|
||||
|
||||
#ifdef VGM_DEBUG_OUTPUT
|
||||
void vgm_logd(/*void* ctx,*/ const char* fmt, ...);
|
||||
void vgm_logd(/*void* ctx,*/ const char* fmt, ...) GNUC_LOG_ATRIB;
|
||||
#define VGM_LOG(...) do { vgm_logd(__VA_ARGS__); } while (0)
|
||||
#define VGM_ASSERT(condition, ...) do { if (condition) {vgm_logd(__VA_ARGS__);} } while (0)
|
||||
#else
|
||||
|
Loading…
Reference in New Issue
Block a user