2019-11-23 20:53:59 +01:00
|
|
|
#include "meta.h"
|
|
|
|
#include "../coding/coding.h"
|
|
|
|
|
|
|
|
|
2021-01-31 19:57:12 +01:00
|
|
|
static void load_name(char* name, size_t name_size, STREAMFILE* sf, int big_endian, int total_subsongs, int target_subsong);
|
|
|
|
static STREAMFILE* setup_nub_streamfile(STREAMFILE *sf, off_t header_offset, size_t header_size, off_t stream_offset, size_t stream_size, const char* fake_ext);
|
2019-11-23 20:53:59 +01:00
|
|
|
|
2019-11-24 10:47:54 +01:00
|
|
|
/* .nub - Namco's nuSound2 audio container */
|
2021-01-31 19:57:12 +01:00
|
|
|
VGMSTREAM* init_vgmstream_nub(STREAMFILE* sf) {
|
|
|
|
VGMSTREAM* vgmstream = NULL;
|
|
|
|
STREAMFILE* temp_sf = NULL;
|
2019-11-24 10:47:54 +01:00
|
|
|
int big_endian;
|
2021-01-31 19:57:12 +01:00
|
|
|
int total_subsongs, target_subsong = sf->stream_index;
|
2019-11-23 20:53:59 +01:00
|
|
|
uint32_t version, codec;
|
|
|
|
const char* fake_ext;
|
2021-01-31 19:57:12 +01:00
|
|
|
VGMSTREAM*(*init_vgmstream_function)(STREAMFILE*) = NULL;
|
2019-11-23 20:53:59 +01:00
|
|
|
char name[STREAM_NAME_SIZE] = {0};
|
|
|
|
int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL;
|
|
|
|
|
|
|
|
|
|
|
|
/* checks */
|
2020-02-22 20:41:53 +01:00
|
|
|
/* .nub: standard
|
|
|
|
* .nub2: rare [iDOLM@STER - Gravure For You (PS3)] */
|
2021-01-31 19:57:12 +01:00
|
|
|
if (!check_extensions(sf, "nub,nub2"))
|
2019-11-23 20:53:59 +01:00
|
|
|
goto fail;
|
|
|
|
|
2021-01-31 19:57:12 +01:00
|
|
|
version = read_32bitBE(0x00,sf);
|
2019-11-23 20:53:59 +01:00
|
|
|
if (version != 0x00020000 && /* v2.0 (rare, ex. Ridge Race 6 (X360)) */
|
|
|
|
version != 0x00020100 && /* v2.1 (common) */
|
2019-11-24 10:47:54 +01:00
|
|
|
version != 0x01020100) /* same but LE (seen in PSP/PC games, except PS4) */
|
2019-11-23 20:53:59 +01:00
|
|
|
goto fail;
|
2021-01-31 19:57:12 +01:00
|
|
|
if (read_32bitBE(0x04,sf) != 0x00000000) /* null */
|
2019-11-23 20:53:59 +01:00
|
|
|
goto fail;
|
|
|
|
|
|
|
|
/* sometimes LE [Soul Calibur: Broken Destiny (PSP), Tales of Vesperia (PS4) */
|
2021-01-31 19:57:12 +01:00
|
|
|
big_endian = guess_endianness32bit(0x18, sf);
|
2019-11-24 10:47:54 +01:00
|
|
|
if (big_endian) {
|
2019-11-23 20:53:59 +01:00
|
|
|
read_32bit = read_32bitBE;
|
|
|
|
} else{
|
|
|
|
read_32bit = read_32bitLE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* parse TOC */
|
|
|
|
{
|
|
|
|
off_t offset, data_start, header_start;
|
|
|
|
off_t header_offset, subheader_start, stream_offset;
|
|
|
|
size_t header_size, subheader_size, stream_size;
|
|
|
|
|
|
|
|
/* - base header */
|
2019-11-24 10:47:54 +01:00
|
|
|
/* 0x08: file id (0 = first) */
|
2021-01-31 19:57:12 +01:00
|
|
|
total_subsongs = read_32bit(0x0c, sf); /* .nub with 0 files do exist, and with 1 song only too */
|
|
|
|
data_start = read_32bit(0x10, sf); /* exists even with 0 files */
|
2019-11-23 20:53:59 +01:00
|
|
|
/* 0x14: data end (may have padding) */
|
2021-01-31 19:57:12 +01:00
|
|
|
header_start = read_32bit(0x18, sf); /* exists even with 0 files */
|
2019-11-23 20:53:59 +01:00
|
|
|
/* 0x1c: header end */
|
|
|
|
|
2021-01-31 19:57:12 +01:00
|
|
|
|
2019-11-23 20:53:59 +01:00
|
|
|
/* probably means "header end" in v2.0 */
|
|
|
|
if (version == 0x00020000) {
|
|
|
|
data_start = align_size_to_block(data_start, 0x800);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (target_subsong == 0) target_subsong = 1;
|
|
|
|
if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail;
|
|
|
|
|
2021-01-31 19:57:12 +01:00
|
|
|
offset = read_32bit(header_start + (target_subsong-1)*0x04, sf);
|
2019-11-23 20:53:59 +01:00
|
|
|
|
|
|
|
/* .nus have all headers first then all data, but extractors often just paste them together,
|
|
|
|
* so we'll combine header+data on the fly to make them playable with existing parsers.
|
2019-11-24 10:47:54 +01:00
|
|
|
* Formats inside .nub normally don't exist as external files, so they could be extracted in various
|
|
|
|
* ways that we'll try to match (though IDSP/BNSF can be found as header+data in some bigfiles).
|
|
|
|
* Headers seem to be called "tones" and data "streams" in debug strings. */
|
2019-11-23 20:53:59 +01:00
|
|
|
|
|
|
|
header_offset = offset;
|
|
|
|
|
|
|
|
/* - extension (as referenced in companion files with internal filenames, ex. "BGM_MovingDemo1.is14" > "is14") */
|
|
|
|
if (version != 0x00020000)
|
2019-11-24 10:47:54 +01:00
|
|
|
offset += 0x04; /* skip, not found in v2.0 */
|
2019-11-23 20:53:59 +01:00
|
|
|
|
2019-11-24 10:47:54 +01:00
|
|
|
/* - tone header */
|
2019-11-23 20:53:59 +01:00
|
|
|
/* 0x00: config? */
|
|
|
|
/* 0x04: header id/number */
|
2021-01-31 19:57:12 +01:00
|
|
|
codec = (uint32_t)read_32bit(offset + 0x08, sf);
|
2019-11-23 20:53:59 +01:00
|
|
|
/* 0x0c: null */
|
2021-01-31 19:57:12 +01:00
|
|
|
stream_size = read_32bit(offset + 0x10, sf); /* 0x10 aligned */
|
|
|
|
stream_offset = read_32bit(offset + 0x14, sf) + data_start;
|
|
|
|
subheader_size = read_32bit(offset + 0x18, sf);
|
2019-11-24 10:47:54 +01:00
|
|
|
/* 0x1c: extra info, size 0x10 (meaning varies but usually loop points) */
|
|
|
|
/* rest until sub-header start looks like config/volumes/pan/etc in various floats */
|
2019-11-23 20:53:59 +01:00
|
|
|
|
|
|
|
if (version == 0x00020000)
|
|
|
|
subheader_start = 0xAC;
|
|
|
|
else
|
|
|
|
subheader_start = 0xBC;
|
|
|
|
header_size = align_size_to_block(subheader_start + subheader_size, 0x10);
|
|
|
|
|
|
|
|
switch(codec) {
|
|
|
|
case 0x00: /* (none) (xma1) */
|
|
|
|
fake_ext = "xma";
|
|
|
|
init_vgmstream_function = init_vgmstream_nub_xma;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x01: /* "wav\0" */
|
|
|
|
fake_ext = "wav";
|
|
|
|
init_vgmstream_function = init_vgmstream_nub_wav;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x02: /* "vag\0" */
|
|
|
|
fake_ext = "vag";
|
|
|
|
init_vgmstream_function = init_vgmstream_nub_vag;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x03: /* "at3\0" */
|
|
|
|
fake_ext = "at3";
|
|
|
|
init_vgmstream_function = init_vgmstream_nub_at3;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x04: /* "xma\0" (xma2 old) */
|
|
|
|
case 0x08: /* "xma\0" (xma2 new) */
|
|
|
|
fake_ext = "xma";
|
|
|
|
init_vgmstream_function = init_vgmstream_nub_xma;
|
|
|
|
break;
|
|
|
|
|
2020-03-01 12:14:06 +01:00
|
|
|
case 0x05: /* "dsp\0" */
|
|
|
|
fake_ext = "dsp";
|
|
|
|
init_vgmstream_function = init_vgmstream_nub_dsp;
|
|
|
|
break;
|
|
|
|
|
2019-11-23 20:53:59 +01:00
|
|
|
case 0x06: /* "idsp" */
|
|
|
|
fake_ext = "idsp";
|
|
|
|
init_vgmstream_function = init_vgmstream_nub_idsp;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x07: /* "is14" */
|
|
|
|
fake_ext = "is14";
|
|
|
|
init_vgmstream_function = init_vgmstream_nub_is14;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
VGM_LOG("NUB: unknown codec %x\n", codec);
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
//;VGM_LOG("NUB: subfile header=%lx + %x, offset=%lx + %x\n", header_offset, header_size, stream_offset, stream_size);
|
|
|
|
|
2021-01-31 19:57:12 +01:00
|
|
|
temp_sf = setup_nub_streamfile(sf, header_offset, header_size, stream_offset, stream_size, fake_ext);
|
2019-11-23 20:53:59 +01:00
|
|
|
if (!temp_sf) goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-11-24 10:47:54 +01:00
|
|
|
/* get names from companion file, rarely [Noby Noby Boy (PS3), Wangan Midnight Maximum Tune (AC)] */
|
2021-01-31 19:57:12 +01:00
|
|
|
load_name(name, sizeof(name), sf, big_endian, total_subsongs, target_subsong);
|
2019-11-23 20:53:59 +01:00
|
|
|
|
|
|
|
|
|
|
|
/* init the VGMSTREAM */
|
|
|
|
vgmstream = init_vgmstream_function(temp_sf);
|
|
|
|
if (!vgmstream) goto fail;
|
|
|
|
|
|
|
|
vgmstream->stream_size = get_streamfile_size(temp_sf);
|
|
|
|
vgmstream->num_streams = total_subsongs;
|
|
|
|
if (name[0] != '\0')
|
|
|
|
strcpy(vgmstream->stream_name, name);
|
|
|
|
|
|
|
|
close_streamfile(temp_sf);
|
|
|
|
return vgmstream;
|
|
|
|
|
|
|
|
fail:
|
|
|
|
close_streamfile(temp_sf);
|
|
|
|
close_vgmstream(vgmstream);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* *********************************************************** */
|
|
|
|
|
2021-01-31 19:57:12 +01:00
|
|
|
static void load_name(char* name, size_t name_size, STREAMFILE* sf, int big_endian, int total_subsongs, int target_subsong) {
|
2019-11-24 10:47:54 +01:00
|
|
|
STREAMFILE *sf_names = NULL;
|
|
|
|
char filename[PATH_LIMIT];
|
|
|
|
char basename[255];
|
|
|
|
char name1[0x40+1] = {0};
|
|
|
|
char name2[0x40+1] = {0};
|
|
|
|
int count;
|
|
|
|
off_t offset;
|
|
|
|
size_t name1_size, name2_size;
|
|
|
|
uint32_t (*read_u32)(off_t,STREAMFILE*) = big_endian ? read_u32be : read_u32le;
|
|
|
|
|
|
|
|
get_streamfile_basename(sf, basename, sizeof(basename));
|
|
|
|
snprintf(filename, sizeof(filename), "nuSound2ToneStr%s.bin", basename);
|
|
|
|
|
|
|
|
sf_names = open_streamfile_by_filename(sf, filename);
|
|
|
|
if (!sf_names) goto done;
|
|
|
|
|
|
|
|
/* 0x00: version/endianness? (0=NNB, 1=WMMT5) */
|
|
|
|
/* 0x04: version/endianness? (1=NNB, 0=WMMT5) */
|
|
|
|
count = read_u32(0x08, sf_names);
|
|
|
|
/* 0x0c: file size */
|
|
|
|
name1_size = read_u32(0x10, sf_names);
|
|
|
|
name2_size = read_u32(0x14, sf_names);
|
|
|
|
/* 0x18/1c: null */
|
|
|
|
/* 0x20: bank name (size 0x20) */
|
|
|
|
|
|
|
|
if (count != total_subsongs)
|
|
|
|
goto done;
|
|
|
|
if (name1_size >= sizeof(name1) || name2_size >= sizeof(name2))
|
|
|
|
goto done;
|
|
|
|
|
|
|
|
offset = 0x40 + (target_subsong - 1) * (name1_size + name2_size);
|
|
|
|
|
|
|
|
read_string(name1, name1_size, offset + 0x00, sf_names); /* internal name */
|
|
|
|
read_string(name2, name2_size, offset + name1_size, sf_names); /* file name */
|
|
|
|
//todo some filenames use shift-jis, not sure what to do
|
|
|
|
|
|
|
|
snprintf(name, name_size, "%s/%s", name1, name2);
|
|
|
|
|
|
|
|
done:
|
|
|
|
close_streamfile(sf_names);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-01-31 19:57:12 +01:00
|
|
|
static STREAMFILE* setup_nub_streamfile(STREAMFILE* sf, off_t header_offset, size_t header_size, off_t stream_offset, size_t stream_size, const char* fake_ext) {
|
|
|
|
STREAMFILE* new_sf = NULL;
|
|
|
|
STREAMFILE* multi_sf[2] = {0};
|
2019-11-23 20:53:59 +01:00
|
|
|
|
|
|
|
multi_sf[0] = open_wrap_streamfile(sf);
|
|
|
|
multi_sf[0] = open_clamp_streamfile_f(multi_sf[0], header_offset, header_size);
|
|
|
|
multi_sf[1] = open_wrap_streamfile(sf);
|
|
|
|
multi_sf[1] = open_clamp_streamfile_f(multi_sf[1], stream_offset, stream_size);
|
|
|
|
new_sf = open_multifile_streamfile_f(multi_sf, 2);
|
|
|
|
new_sf = open_fakename_streamfile_f(new_sf, NULL, fake_ext);
|
|
|
|
return new_sf;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* *********************************************************** */
|
|
|
|
|
|
|
|
//todo could be simplified
|
|
|
|
|
|
|
|
/* .nub wav - from Namco NUB archives [Ridge Racer 7 (PS3)] */
|
2021-01-31 19:57:12 +01:00
|
|
|
VGMSTREAM* init_vgmstream_nub_wav(STREAMFILE* sf) {
|
|
|
|
VGMSTREAM* vgmstream = NULL;
|
2019-11-23 20:53:59 +01:00
|
|
|
off_t start_offset;
|
2019-11-24 10:47:54 +01:00
|
|
|
int loop_flag, channel_count, sample_rate;
|
|
|
|
size_t data_size, loop_start, loop_length;
|
|
|
|
int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL;
|
|
|
|
int16_t (*read_16bit)(off_t,STREAMFILE*) = NULL;
|
2019-11-23 20:53:59 +01:00
|
|
|
|
|
|
|
|
|
|
|
/* checks */
|
2021-01-31 19:57:12 +01:00
|
|
|
if (!check_extensions(sf, "wav,lwav"))
|
2019-11-23 20:53:59 +01:00
|
|
|
goto fail;
|
2021-01-31 19:57:12 +01:00
|
|
|
if (read_32bitBE(0x00,sf) != 0x77617600) /* "wav\0" "*/
|
2019-11-23 20:53:59 +01:00
|
|
|
goto fail;
|
|
|
|
|
2021-01-31 19:57:12 +01:00
|
|
|
if (guess_endianness32bit(0x1c, sf)) {
|
2019-11-24 10:47:54 +01:00
|
|
|
read_32bit = read_32bitBE;
|
|
|
|
read_16bit = read_16bitBE;
|
|
|
|
} else {
|
|
|
|
read_32bit = read_32bitLE;
|
|
|
|
read_16bit = read_16bitLE;
|
|
|
|
}
|
|
|
|
|
2021-01-31 19:57:12 +01:00
|
|
|
data_size = read_32bit(0x14,sf);
|
2019-11-24 10:47:54 +01:00
|
|
|
/* info header */
|
2021-01-31 19:57:12 +01:00
|
|
|
loop_start = read_32bit(0x20,sf);
|
|
|
|
loop_length = read_32bit(0x24,sf);
|
|
|
|
loop_flag = read_32bit(0x28,sf);
|
2019-11-24 10:47:54 +01:00
|
|
|
/* 0x2c: null */
|
|
|
|
|
|
|
|
/* format header: mini "fmt" chunk */
|
2021-01-31 19:57:12 +01:00
|
|
|
if (read_16bit(0xBC + 0x00, sf) != 0x0001)
|
2019-11-23 20:53:59 +01:00
|
|
|
goto fail;
|
2021-01-31 19:57:12 +01:00
|
|
|
channel_count = read_16bit(0xBC + 0x02,sf);
|
|
|
|
sample_rate = read_32bit(0xBC + 0x04,sf);
|
2019-11-24 10:47:54 +01:00
|
|
|
/* 0x08: bitrate */
|
|
|
|
/* 0x0c: block align/bps */
|
2019-11-23 20:53:59 +01:00
|
|
|
|
|
|
|
start_offset = 0xD0;
|
|
|
|
|
2019-11-24 10:47:54 +01:00
|
|
|
|
2019-11-23 20:53:59 +01:00
|
|
|
/* build the VGMSTREAM */
|
|
|
|
vgmstream = allocate_vgmstream(channel_count,loop_flag);
|
|
|
|
if (!vgmstream) goto fail;
|
|
|
|
|
|
|
|
vgmstream->meta_type = meta_NUB;
|
2019-11-24 10:47:54 +01:00
|
|
|
vgmstream->sample_rate = sample_rate;
|
|
|
|
vgmstream->num_samples = pcm_bytes_to_samples(data_size, channel_count, 16);
|
|
|
|
vgmstream->loop_start_sample = pcm_bytes_to_samples(loop_start, channel_count, 16);
|
|
|
|
vgmstream->loop_end_sample = pcm_bytes_to_samples(loop_start + loop_length, channel_count, 16);
|
2019-11-23 20:53:59 +01:00
|
|
|
|
2019-11-24 10:47:54 +01:00
|
|
|
vgmstream->coding_type = coding_PCM16BE; /* always BE */
|
2019-11-23 20:53:59 +01:00
|
|
|
vgmstream->layout_type = layout_interleave;
|
|
|
|
vgmstream->interleave_block_size = 0x02;
|
|
|
|
|
2021-01-31 19:57:12 +01:00
|
|
|
if (!vgmstream_open_stream(vgmstream, sf, start_offset))
|
2019-11-23 20:53:59 +01:00
|
|
|
goto fail;
|
|
|
|
return vgmstream;
|
|
|
|
|
|
|
|
fail:
|
|
|
|
close_vgmstream(vgmstream);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* .nub vag - from Namco NUB archives [Ridge Racer 7 (PS3), Noby Noby Boy (PS3)] */
|
2021-01-31 19:57:12 +01:00
|
|
|
VGMSTREAM* init_vgmstream_nub_vag(STREAMFILE* sf) {
|
2019-11-23 20:53:59 +01:00
|
|
|
VGMSTREAM * vgmstream = NULL;
|
|
|
|
off_t start_offset;
|
2019-11-24 10:47:54 +01:00
|
|
|
int loop_flag, channel_count, sample_rate;
|
|
|
|
size_t data_size, loop_start, loop_length;
|
|
|
|
int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL;
|
2019-11-23 20:53:59 +01:00
|
|
|
|
|
|
|
|
|
|
|
/* checks */
|
2021-01-31 19:57:12 +01:00
|
|
|
if ( !check_extensions(sf, "vag"))
|
2019-11-23 20:53:59 +01:00
|
|
|
goto fail;
|
2021-01-31 19:57:12 +01:00
|
|
|
if (read_32bitBE(0x00,sf) != 0x76616700) /* "vag\0" */
|
2019-11-23 20:53:59 +01:00
|
|
|
goto fail;
|
|
|
|
|
2021-01-31 19:57:12 +01:00
|
|
|
if (guess_endianness32bit(0x1c, sf)) {
|
2019-11-24 10:47:54 +01:00
|
|
|
read_32bit = read_32bitBE;
|
|
|
|
} else {
|
|
|
|
read_32bit = read_32bitLE;
|
|
|
|
}
|
|
|
|
|
2021-01-31 19:57:12 +01:00
|
|
|
data_size = read_32bit(0x14,sf);
|
2019-11-24 10:47:54 +01:00
|
|
|
/* info header */
|
2021-01-31 19:57:12 +01:00
|
|
|
loop_start = read_32bit(0x20,sf);
|
|
|
|
loop_length = read_32bit(0x24,sf);
|
|
|
|
loop_flag = read_32bit(0x28,sf);
|
2019-11-24 10:47:54 +01:00
|
|
|
/* 0x2c: null */
|
|
|
|
|
|
|
|
/* format header */
|
2021-01-31 19:57:12 +01:00
|
|
|
sample_rate = read_32bit(0xBC + 0x00,sf);
|
2019-11-24 10:47:54 +01:00
|
|
|
|
|
|
|
channel_count = 1;
|
2019-11-23 20:53:59 +01:00
|
|
|
start_offset = 0xC0;
|
|
|
|
|
2019-11-24 10:47:54 +01:00
|
|
|
|
2019-11-23 20:53:59 +01:00
|
|
|
/* build the VGMSTREAM */
|
|
|
|
vgmstream = allocate_vgmstream(channel_count,loop_flag);
|
|
|
|
if (!vgmstream) goto fail;
|
|
|
|
|
|
|
|
vgmstream->meta_type = meta_NUB;
|
2019-11-24 10:47:54 +01:00
|
|
|
vgmstream->sample_rate = sample_rate;
|
|
|
|
vgmstream->num_samples = ps_bytes_to_samples(data_size, channel_count);
|
|
|
|
vgmstream->loop_start_sample = ps_bytes_to_samples(loop_start, channel_count);
|
|
|
|
vgmstream->loop_end_sample = ps_bytes_to_samples(loop_start + loop_length, channel_count);
|
2019-11-23 20:53:59 +01:00
|
|
|
|
|
|
|
vgmstream->coding_type = coding_PSX;
|
|
|
|
vgmstream->layout_type = layout_none;
|
|
|
|
vgmstream->allow_dual_stereo = 1;
|
|
|
|
|
2021-01-31 19:57:12 +01:00
|
|
|
if (!vgmstream_open_stream(vgmstream, sf, start_offset))
|
2019-11-23 20:53:59 +01:00
|
|
|
goto fail;
|
|
|
|
return vgmstream;
|
|
|
|
|
|
|
|
fail:
|
|
|
|
close_vgmstream(vgmstream);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* .nub at3 - from Namco NUB archives [Ridge Racer 7 (PS3), Katamari Forever (PS3)] */
|
2021-01-31 19:57:12 +01:00
|
|
|
VGMSTREAM* init_vgmstream_nub_at3(STREAMFILE* sf) {
|
|
|
|
VGMSTREAM* vgmstream = NULL;
|
|
|
|
STREAMFILE* temp_sf = NULL;
|
2019-11-23 20:53:59 +01:00
|
|
|
off_t subfile_offset = 0;
|
|
|
|
size_t subfile_size = 0;
|
|
|
|
|
|
|
|
|
|
|
|
/* checks */
|
2021-01-31 19:57:12 +01:00
|
|
|
if (!check_extensions(sf,"at3"))
|
2019-11-23 20:53:59 +01:00
|
|
|
goto fail;
|
2021-01-31 19:57:12 +01:00
|
|
|
if (read_32bitBE(0x00,sf) != 0x61743300) /* "at3\0" */
|
2019-11-23 20:53:59 +01:00
|
|
|
goto fail;
|
|
|
|
|
2019-11-24 10:47:54 +01:00
|
|
|
/* info header */
|
|
|
|
/* 0x20: loop start (in samples) */
|
|
|
|
/* 0x24: loop length (in samples) */
|
|
|
|
/* 0x28: loop flag */
|
|
|
|
/* 0x2c: null */
|
|
|
|
|
|
|
|
/* format header: mini fmt (WAVEFORMATEX) + fact chunks LE (clone of RIFF's) */
|
|
|
|
/* we can just ignore and use RIFF at data start since it has the same info */
|
|
|
|
|
2019-11-23 20:53:59 +01:00
|
|
|
subfile_offset = 0x100;
|
2021-01-31 19:57:12 +01:00
|
|
|
subfile_size = read_32bitLE(subfile_offset + 0x04, sf) + 0x08; /* RIFF size */
|
2019-11-23 20:53:59 +01:00
|
|
|
|
2021-01-31 19:57:12 +01:00
|
|
|
temp_sf = setup_subfile_streamfile(sf, subfile_offset,subfile_size, NULL);
|
2019-11-23 20:53:59 +01:00
|
|
|
if (!temp_sf) goto fail;
|
|
|
|
|
|
|
|
vgmstream = init_vgmstream_riff(temp_sf);
|
|
|
|
if (!vgmstream) goto fail;
|
|
|
|
|
|
|
|
close_streamfile(temp_sf);
|
|
|
|
return vgmstream;
|
|
|
|
fail:
|
|
|
|
close_streamfile(temp_sf);
|
|
|
|
close_vgmstream(vgmstream);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* .nub xma - from Namco NUB archives [Ridge Racer 6 (X360), Tekken 6 (X360), Galaga Legions DX (X360)] */
|
2021-01-31 19:57:12 +01:00
|
|
|
VGMSTREAM* init_vgmstream_nub_xma(STREAMFILE* sf) {
|
|
|
|
VGMSTREAM* vgmstream = NULL;
|
2019-11-23 20:53:59 +01:00
|
|
|
off_t start_offset, chunk_offset;
|
|
|
|
size_t data_size, chunk_size, header_size;
|
|
|
|
int loop_flag, channel_count, sample_rate, nus_codec;
|
|
|
|
int num_samples, loop_start_sample, loop_end_sample;
|
|
|
|
|
|
|
|
|
|
|
|
/* checks */
|
2021-01-31 19:57:12 +01:00
|
|
|
if (!check_extensions(sf,"xma"))
|
2019-11-23 20:53:59 +01:00
|
|
|
goto fail;
|
|
|
|
|
2021-01-31 19:57:12 +01:00
|
|
|
if (read_32bitBE(0x00,sf) == 0x786D6100) { /* "xma\0" */
|
2019-11-23 20:53:59 +01:00
|
|
|
/* nub v2.1 */
|
2021-01-31 19:57:12 +01:00
|
|
|
nus_codec = read_32bitBE(0x0C,sf);
|
|
|
|
data_size = read_32bitBE(0x14,sf);
|
|
|
|
header_size = read_32bitBE(0x1c,sf);
|
2019-11-23 20:53:59 +01:00
|
|
|
chunk_offset = 0xBC;
|
2019-11-24 10:47:54 +01:00
|
|
|
|
|
|
|
/* info header */
|
|
|
|
/* 0x20: null */
|
2021-01-31 19:57:12 +01:00
|
|
|
chunk_size = read_32bitBE(0x24,sf);
|
2019-11-24 10:47:54 +01:00
|
|
|
/* 0x24: loop flag */
|
|
|
|
/* 0x20: null */
|
2019-11-23 20:53:59 +01:00
|
|
|
}
|
2021-01-31 19:57:12 +01:00
|
|
|
else if (read_32bitBE(0x08,sf) == 0 && read_32bitBE(0x0c,sf) == 0) {
|
2019-11-23 20:53:59 +01:00
|
|
|
/* nub v2.0 from Ridge Racer 6 */
|
2021-01-31 19:57:12 +01:00
|
|
|
nus_codec = read_32bitBE(0x08,sf);
|
|
|
|
data_size = read_32bitBE(0x10,sf);
|
|
|
|
header_size = read_32bitBE(0x18,sf);
|
2019-11-23 20:53:59 +01:00
|
|
|
chunk_offset = 0xAC;
|
2019-11-24 10:47:54 +01:00
|
|
|
|
2019-11-23 20:53:59 +01:00
|
|
|
chunk_size = header_size;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
start_offset = align_size_to_block(chunk_offset + header_size, 0x10);
|
|
|
|
|
|
|
|
if (nus_codec == 0x00) { /* XMA1 "fmt " */
|
|
|
|
int loop_start_b, loop_end_b, loop_subframe;
|
|
|
|
|
2021-01-31 19:57:12 +01:00
|
|
|
xma1_parse_fmt_chunk(sf, chunk_offset, &channel_count,&sample_rate, &loop_flag, &loop_start_b, &loop_end_b, &loop_subframe, 1);
|
2019-11-23 20:53:59 +01:00
|
|
|
|
|
|
|
{
|
|
|
|
ms_sample_data msd = {0};
|
|
|
|
|
|
|
|
msd.xma_version = 1;
|
|
|
|
msd.channels = channel_count;
|
|
|
|
msd.data_offset = start_offset;
|
|
|
|
msd.data_size = data_size;
|
|
|
|
msd.loop_flag = loop_flag;
|
|
|
|
msd.loop_start_b= loop_start_b;
|
|
|
|
msd.loop_end_b = loop_end_b;
|
|
|
|
msd.loop_start_subframe = loop_subframe & 0xF; /* lower 4b: subframe where the loop starts, 0..4 */
|
|
|
|
msd.loop_end_subframe = loop_subframe >> 4; /* upper 4b: subframe where the loop ends, 0..3 */
|
|
|
|
msd.chunk_offset= chunk_offset;
|
|
|
|
|
2021-01-31 19:57:12 +01:00
|
|
|
xma_get_samples(&msd, sf);
|
2019-11-23 20:53:59 +01:00
|
|
|
|
|
|
|
num_samples = msd.num_samples;
|
|
|
|
loop_start_sample = msd.loop_start_sample;
|
|
|
|
loop_end_sample = msd.loop_end_sample;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (nus_codec == 0x04) { /* "XMA2" */
|
2021-01-31 19:57:12 +01:00
|
|
|
xma2_parse_xma2_chunk(sf, chunk_offset, &channel_count,&sample_rate, &loop_flag, &num_samples, &loop_start_sample, &loop_end_sample);
|
2019-11-23 20:53:59 +01:00
|
|
|
}
|
|
|
|
else if (nus_codec == 0x08) { /* XMA2 "fmt " */
|
2021-01-31 19:57:12 +01:00
|
|
|
channel_count = read_16bitBE(chunk_offset+0x02,sf);
|
|
|
|
sample_rate = read_32bitBE(chunk_offset+0x04,sf);
|
|
|
|
xma2_parse_fmt_chunk_extra(sf, chunk_offset, &loop_flag, &num_samples, &loop_start_sample, &loop_end_sample, 1);
|
2019-11-23 20:53:59 +01:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* build the VGMSTREAM */
|
|
|
|
vgmstream = allocate_vgmstream(channel_count,loop_flag);
|
|
|
|
if (!vgmstream) goto fail;
|
|
|
|
|
|
|
|
vgmstream->meta_type = meta_NUB;
|
|
|
|
vgmstream->sample_rate = sample_rate;
|
|
|
|
vgmstream->num_samples = num_samples;
|
|
|
|
vgmstream->loop_start_sample = loop_start_sample;
|
|
|
|
vgmstream->loop_end_sample = loop_end_sample;
|
|
|
|
|
|
|
|
#ifdef VGM_USE_FFMPEG
|
|
|
|
{
|
|
|
|
uint8_t buf[0x100];
|
|
|
|
size_t bytes;
|
|
|
|
|
|
|
|
if (nus_codec == 0x04) {
|
2021-01-31 19:57:12 +01:00
|
|
|
bytes = ffmpeg_make_riff_xma2_from_xma2_chunk(buf,0x100, chunk_offset,chunk_size, data_size, sf);
|
2019-11-23 20:53:59 +01:00
|
|
|
} else {
|
2021-01-31 19:57:12 +01:00
|
|
|
bytes = ffmpeg_make_riff_xma_from_fmt_chunk(buf,0x100, chunk_offset,chunk_size, data_size, sf, 1);
|
2019-11-23 20:53:59 +01:00
|
|
|
}
|
2021-01-31 19:57:12 +01:00
|
|
|
vgmstream->codec_data = init_ffmpeg_header_offset(sf, buf,bytes, start_offset,data_size);
|
2019-11-23 20:53:59 +01:00
|
|
|
if ( !vgmstream->codec_data ) goto fail;
|
|
|
|
vgmstream->coding_type = coding_FFmpeg;
|
|
|
|
vgmstream->layout_type = layout_none;
|
|
|
|
|
2021-01-31 19:57:12 +01:00
|
|
|
xma_fix_raw_samples(vgmstream, sf, start_offset, data_size, chunk_offset, 1,1); /* samples needs adjustment */
|
2019-11-23 20:53:59 +01:00
|
|
|
}
|
|
|
|
#else
|
|
|
|
goto fail;
|
|
|
|
#endif
|
|
|
|
|
2021-01-31 19:57:12 +01:00
|
|
|
if ( !vgmstream_open_stream(vgmstream, sf, start_offset) )
|
2019-11-23 20:53:59 +01:00
|
|
|
goto fail;
|
|
|
|
return vgmstream;
|
|
|
|
|
|
|
|
fail:
|
|
|
|
close_vgmstream(vgmstream);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2020-03-01 12:14:06 +01:00
|
|
|
/* .nub dsp - from Namco NUB archives [Taiko no Tatsujin Wii Chou Goukanban (Wii)] */
|
2021-01-31 19:57:12 +01:00
|
|
|
VGMSTREAM* init_vgmstream_nub_dsp(STREAMFILE* sf) {
|
|
|
|
VGMSTREAM* vgmstream = NULL;
|
|
|
|
STREAMFILE* temp_sf = NULL;
|
2020-03-01 12:14:06 +01:00
|
|
|
off_t header_offset, stream_offset;
|
|
|
|
size_t header_size, stream_size;
|
|
|
|
|
|
|
|
|
|
|
|
/* checks */
|
2021-01-31 19:57:12 +01:00
|
|
|
if (!check_extensions(sf,"dsp"))
|
2020-03-01 12:14:06 +01:00
|
|
|
goto fail;
|
2021-01-31 19:57:12 +01:00
|
|
|
if (read_32bitBE(0x00,sf) != 0x64737000) /* "dsp\0" */
|
2020-03-01 12:14:06 +01:00
|
|
|
goto fail;
|
|
|
|
|
|
|
|
/* paste header+data together and pass to meta, which has loop info too */
|
|
|
|
header_offset = 0xBC;
|
2021-01-31 19:57:12 +01:00
|
|
|
stream_size = read_32bitBE(0x14, sf);
|
|
|
|
header_size = read_32bitBE(0x1c, sf);
|
2020-03-01 12:14:06 +01:00
|
|
|
stream_offset = align_size_to_block(header_offset + header_size, 0x10);
|
|
|
|
|
2021-01-31 19:57:12 +01:00
|
|
|
temp_sf = setup_nub_streamfile(sf, header_offset, header_size, stream_offset, stream_size, "dsp");
|
2020-03-01 12:14:06 +01:00
|
|
|
if (!temp_sf) goto fail;
|
|
|
|
|
|
|
|
vgmstream = init_vgmstream_ngc_dsp_std(temp_sf);
|
|
|
|
if (!vgmstream) goto fail;
|
|
|
|
|
|
|
|
close_streamfile(temp_sf);
|
|
|
|
return vgmstream;
|
|
|
|
fail:
|
|
|
|
close_streamfile(temp_sf);
|
|
|
|
close_vgmstream(vgmstream);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2019-11-23 20:53:59 +01:00
|
|
|
/* .nub idsp - from Namco NUB archives [Soul Calibur Legends (Wii), Sky Crawlers: Innocent Aces (Wii)] */
|
2021-01-31 19:57:12 +01:00
|
|
|
VGMSTREAM* init_vgmstream_nub_idsp(STREAMFILE* sf) {
|
|
|
|
VGMSTREAM* vgmstream = NULL;
|
|
|
|
STREAMFILE* temp_sf = NULL;
|
2019-11-24 10:47:54 +01:00
|
|
|
off_t header_offset, stream_offset;
|
|
|
|
size_t header_size, stream_size;
|
|
|
|
|
2019-11-23 20:53:59 +01:00
|
|
|
|
|
|
|
/* checks */
|
2021-01-31 19:57:12 +01:00
|
|
|
if (!check_extensions(sf,"idsp"))
|
2019-11-23 20:53:59 +01:00
|
|
|
goto fail;
|
2021-01-31 19:57:12 +01:00
|
|
|
if (read_32bitBE(0x00,sf) != 0x69647370) /* "idsp" */
|
2019-11-23 20:53:59 +01:00
|
|
|
goto fail;
|
|
|
|
|
2019-11-24 10:47:54 +01:00
|
|
|
/* info header */
|
|
|
|
/* 0x20: loop start (in samples) */
|
|
|
|
/* 0x24: loop length (in samples) */
|
|
|
|
/* 0x28: loop flag */
|
|
|
|
/* 0x2c: null */
|
2019-11-23 20:53:59 +01:00
|
|
|
|
2019-11-24 10:47:54 +01:00
|
|
|
/* paste header+data together and pass to meta, which has loop info too */
|
|
|
|
header_offset = 0xBC;
|
2021-01-31 19:57:12 +01:00
|
|
|
stream_size = read_32bitBE(0x14, sf);
|
|
|
|
header_size = read_32bitBE(0x1c, sf);
|
2019-11-24 10:47:54 +01:00
|
|
|
stream_offset = align_size_to_block(header_offset + header_size, 0x10);
|
2019-11-23 20:53:59 +01:00
|
|
|
|
2021-01-31 19:57:12 +01:00
|
|
|
temp_sf = setup_nub_streamfile(sf, header_offset, header_size, stream_offset, stream_size, "idsp");
|
2019-11-24 10:47:54 +01:00
|
|
|
if (!temp_sf) goto fail;
|
2019-11-23 20:53:59 +01:00
|
|
|
|
2019-11-24 10:47:54 +01:00
|
|
|
vgmstream = init_vgmstream_idsp_namco(temp_sf);
|
|
|
|
if (!vgmstream) goto fail;
|
2019-11-23 20:53:59 +01:00
|
|
|
|
2019-11-24 10:47:54 +01:00
|
|
|
close_streamfile(temp_sf);
|
2019-11-23 20:53:59 +01:00
|
|
|
return vgmstream;
|
|
|
|
fail:
|
2019-11-24 10:47:54 +01:00
|
|
|
close_streamfile(temp_sf);
|
2019-11-23 20:53:59 +01:00
|
|
|
close_vgmstream(vgmstream);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2019-11-24 10:47:54 +01:00
|
|
|
/* .nub is14 - from Namco NUB archives [Tales of Vesperia (PS3), Mojipittan (Wii)] */
|
2021-01-31 19:57:12 +01:00
|
|
|
VGMSTREAM* init_vgmstream_nub_is14(STREAMFILE* sf) {
|
|
|
|
VGMSTREAM* vgmstream = NULL;
|
|
|
|
STREAMFILE* temp_sf = NULL;
|
2019-11-23 20:53:59 +01:00
|
|
|
off_t header_offset, stream_offset;
|
|
|
|
size_t header_size, stream_size, sdat_size;
|
|
|
|
int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL;
|
|
|
|
|
|
|
|
|
|
|
|
/* checks */
|
2021-01-31 19:57:12 +01:00
|
|
|
if (!check_extensions(sf,"is14"))
|
2019-11-23 20:53:59 +01:00
|
|
|
goto fail;
|
2021-01-31 19:57:12 +01:00
|
|
|
if (read_32bitBE(0x00,sf) != 0x69733134) /* "is14" */
|
2019-11-23 20:53:59 +01:00
|
|
|
goto fail;
|
|
|
|
|
2021-01-31 19:57:12 +01:00
|
|
|
if (guess_endianness32bit(0x1c, sf)) {
|
2019-11-23 20:53:59 +01:00
|
|
|
read_32bit = read_32bitBE;
|
|
|
|
} else{
|
|
|
|
read_32bit = read_32bitLE;
|
|
|
|
}
|
|
|
|
|
2019-11-24 10:47:54 +01:00
|
|
|
/* info header: null (even when BSNF loops) */
|
2019-11-23 20:53:59 +01:00
|
|
|
|
|
|
|
/* paste header+data together and pass to meta */
|
|
|
|
header_offset = 0xBC;
|
2021-01-31 19:57:12 +01:00
|
|
|
header_size = read_32bit(0x1c, sf);
|
2019-11-23 20:53:59 +01:00
|
|
|
|
|
|
|
/* size at 0x14 is padded, find "sdat" size BE (may move around) */
|
2021-01-31 19:57:12 +01:00
|
|
|
if (!find_chunk_riff_be(sf, 0x73646174, 0xbc+0x0c, header_size - 0x0c, NULL, &sdat_size))
|
2019-11-23 20:53:59 +01:00
|
|
|
goto fail;
|
|
|
|
stream_offset = align_size_to_block(header_offset + header_size, 0x10);
|
|
|
|
stream_size = sdat_size;
|
|
|
|
|
|
|
|
|
2021-01-31 19:57:12 +01:00
|
|
|
temp_sf = setup_nub_streamfile(sf, header_offset, header_size, stream_offset, stream_size, "bnsf");
|
2019-11-23 20:53:59 +01:00
|
|
|
if (!temp_sf) goto fail;
|
|
|
|
|
|
|
|
vgmstream = init_vgmstream_bnsf(temp_sf);
|
|
|
|
if (!vgmstream) goto fail;
|
|
|
|
|
|
|
|
close_streamfile(temp_sf);
|
|
|
|
return vgmstream;
|
|
|
|
fail:
|
|
|
|
close_streamfile(temp_sf);
|
|
|
|
close_vgmstream(vgmstream);
|
|
|
|
return NULL;
|
|
|
|
}
|