Merge pull request #192 from bnnm/sxd-xnb-etc

SXD, XNB, etc
This commit is contained in:
Christopher Snowhill 2018-02-10 15:46:57 -08:00 committed by GitHub
commit d438b1e299
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 609 additions and 391 deletions

View File

@ -94,7 +94,7 @@ export CFLAGS LDFLAGS
### targets
vgmstream_cli: libvgmstream.a $(TARGET_EXT_LIBS)
$(CC) $(CFLAGS) "-DVERSION=\"`../version.sh`\"" test.c $(LDFLAGS) -o $(OUTPUT_CLI)
$(CC) $(CFLAGS) "-DVERSION=\"`../version.sh`\"" vgmstream_cli.c $(LDFLAGS) -o $(OUTPUT_CLI)
$(STRIP) $(OUTPUT_CLI)
vgmstream123: libvgmstream.a $(TARGET_EXT_LIBS)

View File

@ -9,7 +9,7 @@ endif
AM_CFLAGS = -I$(top_builddir) -I$(top_srcdir) -I$(top_srcdir)/ext_includes/ $(AO_CFLAGS)
AM_MAKEFLAGS = -f Makefile.autotools
vgmstream_cli_SOURCES = test.c
vgmstream_cli_SOURCES = vgmstream_cli.c
vgmstream_cli_LDADD = ../src/libvgmstream.la
vgmstream123_SOURCES = vgmstream123.c

View File

@ -182,7 +182,7 @@
UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
>
<File
RelativePath=".\test.c"
RelativePath=".\vgmstream_cli.c"
>
</File>
</Filter>

View File

@ -121,7 +121,7 @@
</PreBuildEvent>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="test.c" />
<ClCompile Include="vgmstream_cli.c" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\dependencies\fdk-aac\msvc\fdk-aac.vcxproj">

View File

@ -15,7 +15,7 @@
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="test.c">
<ClCompile Include="vgmstream_cli.c">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>

View File

@ -530,8 +530,8 @@ static int ealayer3_rebuild_mpeg_frame(vgm_bitstream* is_0, ealayer3_frame_info*
VGM_LOG("MPEG EAL3: written 0x%lx but expected less than 0x%x at 0x%lx\n", os->b_off/8, expected_frame_size, os->info_offset);
}
else {
/* fill ancillary data (ignored) */
memset(os->buf + os->b_off/8, 0x77, expected_frame_size - os->b_off/8);
/* fill ancillary data (should be ignored, but 0x00 seems to improve mpg123's free bitrate detection) */
memset(os->buf + os->b_off/8, 0x00, expected_frame_size - os->b_off/8);
}
os->b_off = expected_frame_size*8;

View File

@ -620,7 +620,11 @@ VGMSTREAM * init_vgmstream_ngc_dsp_std_int(STREAMFILE *streamFile) {
ch0_header.loop_flag != ch1_header.loop_flag ||
ch0_header.loop_start_offset != ch1_header.loop_start_offset ||
ch0_header.loop_end_offset != ch1_header.loop_end_offset
) goto fail;
) {
/* Timesplitters 2 GC's ts2_atom_smasher_44_fx.mss differs slightly in samples but plays ok */
if (meta_type != meta_DSP_MSS)
goto fail;
}
if (ch0_header.loop_flag) {
off_t loop_off;

View File

@ -5,12 +5,12 @@
/* SXD - Sony/SCE's SNDX lib format (cousin of SGXD) [Gravity Rush, Freedom Wars, Soul Sacrifice PSV] */
VGMSTREAM * init_vgmstream_sxd(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
STREAMFILE * streamHeader = NULL;
STREAMFILE * streamHeader = NULL, *streamData = NULL;
off_t start_offset, chunk_offset, first_offset = 0x60, name_offset = 0;
size_t chunk_size, stream_size = 0;
int is_separate;
int loop_flag, channels, codec;
int is_dual, is_external;
int loop_flag, channels, codec, location;
int sample_rate, num_samples, loop_start_sample, loop_end_sample;
uint32_t at9_config_data = 0;
int total_subsongs, target_subsong = streamFile->stream_index;
@ -19,10 +19,10 @@ VGMSTREAM * init_vgmstream_sxd(STREAMFILE *streamFile) {
/* check extension, case insensitive */
/* .sxd: header+data (SXDF), .sxd1: header (SXDF) + .sxd2 = data (SXDS) */
if (!check_extensions(streamFile,"sxd,sxd2")) goto fail;
is_separate = !check_extensions(streamFile,"sxd");
is_dual = !check_extensions(streamFile,"sxd");
/* sxd1+sxd2: use sxd1 as header; otherwise use the current file as header */
if (is_separate) {
if (is_dual) {
if (read_32bitBE(0x00,streamFile) != 0x53584453) /* "SXDS" */
goto fail;
streamHeader = open_stream_ext(streamFile, "sxd1");
@ -50,10 +50,10 @@ VGMSTREAM * init_vgmstream_sxd(STREAMFILE *streamFile) {
table_offset = chunk_offset + 0x08 + 4*(target_subsong-1);
header_offset = table_offset + read_32bitLE(table_offset,streamHeader);
/* 0x00(4): type/location? (00/01=sxd/RAM?, 02/03=sxd2/stream?) */
codec = read_8bit (header_offset+0x04,streamHeader);
channels = read_8bit (header_offset+0x05,streamHeader);
sample_rate = read_32bitLE(header_offset+0x08,streamHeader);
location = read_32bitLE(header_offset+0x00,streamHeader);
codec = read_8bit (header_offset+0x04,streamHeader);
channels = read_8bit (header_offset+0x05,streamHeader);
sample_rate = read_32bitLE(header_offset+0x08,streamHeader);
/* 0x0c(4): unknown size? (0x4000/0x3999/0x3333/etc, not related to extra data) */
/* 0x10(4): ? + volume? + pan? (can be 0 for music) */
num_samples = read_32bitLE(header_offset+0x14,streamHeader);
@ -69,7 +69,7 @@ VGMSTREAM * init_vgmstream_sxd(STREAMFILE *streamFile) {
off_t max_offset = chunk_offset + chunk_size;
/* manually try to find certain tag, no idea about the actual format
* (most variable in Soul Sacrifice; extra size isn't found in the SXD AFAIK) */
* (most variable in Soul Sacrifice; extra data size isn't found in the header AFAIK) */
while (extra_offset < max_offset) {
uint32_t tag = read_32bitBE(extra_offset, streamHeader);
if (tag == 0x0A010000 || tag == 0x0A010600) {
@ -85,16 +85,33 @@ VGMSTREAM * init_vgmstream_sxd(STREAMFILE *streamFile) {
loop_flag = loop_start_sample != -1 && loop_end_sample != -1;
/* from current offset in sxd, absolute in sxd2 */
if (is_separate) {
start_offset = stream_offset;
/* usually sxd=header+data and sxd1=header + sxd2=data, but rarely sxd1 contain data [The Last Guardian (PS4)] */
switch(location) { /* might not be exact but seems the only difference in TLG */
case 0x00: /* some Chaos Rings 2 sfx */
case 0x01: /* most common */
case 0x05: /* some Gradity Rush 2 sfx */
is_external = 0; /* RAM asset? */
break;
case 0x02: /* some Chaos Rings 3 sfx */
case 0x03: /* most common */
is_external = 1; /* stream asset? */
break;
default:
VGM_LOG("SXD: unknown location 0x%x\n", location);
goto fail;
}
if (is_external) {
start_offset = stream_offset; /* absolute if external */
} else {
start_offset = header_offset+0x24 + stream_offset;
start_offset = header_offset+0x24 + stream_offset; /* from current entry offset if internal */
}
}
/* get stream name (NAME is tied to REQD/cues, and SFX cues repeat WAVEs, but should work ok for streams) */
if (is_separate && find_chunk_le(streamHeader, 0x4E414D45,first_offset,0, &chunk_offset,NULL)) { /* "NAME" */
if (is_dual && find_chunk_le(streamHeader, 0x4E414D45,first_offset,0, &chunk_offset,NULL)) { /* "NAME" */
/* table: relative offset (32b) + hash? (32b) + cue index (32b) */
int i;
int num_entries = read_16bitLE(chunk_offset+0x04,streamHeader); /* can be bigger than streams */
@ -107,6 +124,22 @@ VGMSTREAM * init_vgmstream_sxd(STREAMFILE *streamFile) {
}
}
if (is_external && !is_dual) {
VGM_LOG("SXD: found single sxd with external data\n");
goto fail;
}
if (is_external) {
streamData = streamFile;
} else {
streamData = streamHeader;
}
if (start_offset > get_streamfile_size(streamData)) {
VGM_LOG("SXD: wrong location?\n");
goto fail;
}
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channels,loop_flag);
@ -136,7 +169,7 @@ VGMSTREAM * init_vgmstream_sxd(STREAMFILE *streamFile) {
break;
#ifdef VGM_USE_ATRAC9
case 0x42: { /* ATRAC9 [Soul Sacrifice (Vita), Freedom Wars (Vita)] */
case 0x42: { /* ATRAC9 [Soul Sacrifice (Vita), Freedom Wars (Vita), Gravity Rush 2 (PS4)] */
atrac9_config cfg = {0};
cfg.channels = vgmstream->channels;
@ -157,14 +190,14 @@ VGMSTREAM * init_vgmstream_sxd(STREAMFILE *streamFile) {
/* open the file for reading */
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
if (!vgmstream_open_stream(vgmstream,streamData,start_offset))
goto fail;
if (is_separate && streamHeader) close_streamfile(streamHeader);
if (is_dual) close_streamfile(streamHeader);
return vgmstream;
fail:
if (is_separate && streamHeader) close_streamfile(streamHeader);
if (is_dual) close_streamfile(streamHeader);
close_vgmstream(vgmstream);
return NULL;
}

View File

@ -474,7 +474,6 @@ static int parse_keyval(STREAMFILE * streamFile, STREAMFILE * streamText, txth_h
else if (0==strcmp(key,"interleave")) {
if (0==strcmp(val,"half_size")) {
txth->interleave = txth->data_size / txth->channels;
VGM_LOG("int=%x, ds=%x\n", txth->interleave, txth->data_size);
}
else {
if (!parse_num(streamFile,val, &txth->interleave)) goto fail;

View File

@ -5,9 +5,10 @@
VGMSTREAM * init_vgmstream_xnb(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
off_t start_offset;
int loop_flag = 0, channel_count;
int flags, codec, sample_rate, block_size, bps;
size_t xnb_size, data_size;
int loop_flag = 0, channel_count, num_samples = 0, loop_start = 0, loop_end = 0;
int big_endian, flags, codec, sample_rate, block_size, bps;
size_t data_size;
char platform;
/* check extension, case insensitive */
@ -17,65 +18,77 @@ VGMSTREAM * init_vgmstream_xnb(STREAMFILE *streamFile) {
/* check header */
if ((read_32bitBE(0,streamFile) & 0xFFFFFF00) != 0x584E4200) /* "XNB" */
goto fail;
/* 0x04: platform: w = Microsoft Windows, m = Windows Phone 7, x = Xbox 360, 'a' = Android */
/* 0x04: platform: w = Windows, m = Windows Phone 7, x = X360, 'a' = Android, 'P': PS4 */
platform = read_8bit(0x03,streamFile);
big_endian = (platform == 'x');
if (read_8bit(0x04,streamFile) != 0x05) /* XNA 4.0 version only */
goto fail;
flags = read_8bit(0x05,streamFile);
if (flags & 0x80) goto fail; /* compressed with XMemCompress */
//if (flags & 0x01) goto fail; /* XMA/big endian flag? */
//if (flags & 0x01) goto fail; /* "HiDef profile" content (no actual difference) */
if (flags & 0x80) goto fail; /* compressed with XMemCompress (at 0x0a is decompressed size) */
if (flags & 0x40) goto fail; /* custom compression? seen in Square Heroes (PS4) */
/* full size */
if (read_32bitLE(0x06,streamFile) != get_streamfile_size(streamFile))
goto fail;
/* "check for truncated XNB" (???) */
xnb_size = read_32bitLE(0x06,streamFile);
if (get_streamfile_size(streamFile) < xnb_size) goto fail;
/* XNB contains "type reader" class references to parse "shared resource" data (can be any implemented filetype) */
{
char reader_name[255+1];
off_t current_chunk = 0xa;
off_t current_offset = 0x0a;
int reader_string_len;
uint32_t fmt_chunk_size;
const char * type_sound = "Microsoft.Xna.Framework.Content.SoundEffectReader"; /* partial "fmt" chunk or XMA */
//const char * type_song = "Microsoft.Xna.Framework.Content.SongReader"; /* just references a companion .wma */
/* type reader count, accept only one for now */
if (read_8bit(current_chunk++, streamFile) != 1)
if (read_8bit(current_offset++, streamFile) != 1)
goto fail;
reader_string_len = read_8bit(current_chunk++, streamFile); /* doesn't count null */
reader_string_len = read_8bit(current_offset++, streamFile); /* doesn't count null */
if (reader_string_len > 255) goto fail;
/* check SoundEffect type string */
if (read_string(reader_name,reader_string_len+1,current_chunk,streamFile) != reader_string_len)
if (read_string(reader_name,reader_string_len+1,current_offset,streamFile) != reader_string_len)
goto fail;
if ( strcmp(reader_name, type_sound) != 0 )
goto fail;
current_chunk += reader_string_len + 1;
current_chunk += 4; /* reader version */
current_offset += reader_string_len + 1;
current_offset += 4; /* reader version */
/* shared resource count */
if (read_8bit(current_chunk++, streamFile) != 1)
if (read_8bit(current_offset++, streamFile) != 1)
goto fail;
/* shared resource: partial "fmt" chunk */
fmt_chunk_size = read_32bitLE(current_chunk, streamFile);
current_chunk += 4;
fmt_chunk_size = read_32bitLE(current_offset, streamFile);
current_offset += 4;
{
codec = read_16bitLE(current_chunk+0x00, streamFile);
channel_count = read_16bitLE(current_chunk+0x02, streamFile);
sample_rate = read_32bitLE(current_chunk+0x04, streamFile);
block_size = read_16bitLE(current_chunk+0x0c, streamFile);
bps = read_16bitLE(current_chunk+0x0e, streamFile);
int32_t (*read_32bit)(off_t,STREAMFILE*) = big_endian ? read_32bitBE : read_32bitLE;
int16_t (*read_16bit)(off_t,STREAMFILE*) = big_endian ? read_16bitBE : read_16bitLE;
codec = read_16bit(current_offset+0x00, streamFile);
channel_count = read_16bit(current_offset+0x02, streamFile);
sample_rate = read_32bit(current_offset+0x04, streamFile);
block_size = read_16bit(current_offset+0x0c, streamFile);
bps = read_16bit(current_offset+0x0e, streamFile);
if (codec == 0x166) {
xma2_parse_fmt_chunk_extra(streamFile, current_offset, &loop_flag, &num_samples, &loop_start, &loop_end, big_endian);
}
}
current_chunk += fmt_chunk_size;
current_offset += fmt_chunk_size;
data_size = read_32bitLE(current_chunk, streamFile);
current_chunk += 4;
data_size = read_32bitLE(current_offset, streamFile);
current_offset += 4;
start_offset = current_chunk;
start_offset = current_offset;
}
@ -87,14 +100,14 @@ VGMSTREAM * init_vgmstream_xnb(STREAMFILE *streamFile) {
vgmstream->meta_type = meta_XNB;
switch (codec) {
case 0x01:
case 0x01: /* Dragon's Blade (Android) */
vgmstream->coding_type = bps == 8 ? coding_PCM8_U_int : coding_PCM16LE;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = block_size / channel_count;
vgmstream->num_samples = pcm_bytes_to_samples(data_size, channel_count, bps);
break;
case 0x02:
case 0x02: /* White Noise Online (PC) */
vgmstream->coding_type = coding_MSADPCM;
vgmstream->layout_type = layout_none;
vgmstream->interleave_block_size = block_size;
@ -108,6 +121,29 @@ VGMSTREAM * init_vgmstream_xnb(STREAMFILE *streamFile) {
vgmstream->num_samples = ms_ima_bytes_to_samples(data_size, block_size, channel_count);
break;
#ifdef VGM_USE_FFMPEG
case 0x166: { /* Terraria (X360) */
uint8_t buf[0x100];
int32_t bytes, block_size, block_count;
block_size = 0x10000; /* guessed */
block_count = data_size / block_size + (data_size % block_size ? 1 : 0);
bytes = ffmpeg_make_riff_xma2(buf,0x100, num_samples, data_size, vgmstream->channels, vgmstream->sample_rate, block_count, block_size);
if (bytes <= 0) goto fail;
vgmstream->codec_data = init_ffmpeg_header_offset(streamFile, buf,bytes, start_offset,data_size);
if (!vgmstream->codec_data) goto fail;
vgmstream->coding_type = coding_FFmpeg;
vgmstream->layout_type = layout_none;
vgmstream->num_samples = num_samples;
vgmstream->loop_start_sample = loop_start;
vgmstream->loop_end_sample = loop_end;
break;
}
#endif
default:
VGM_LOG("XNB: unknown codec 0x%x\n", codec);
goto fail;

File diff suppressed because it is too large Load Diff

View File

@ -9,3 +9,4 @@
#define IDC_THREAD_PRIORITY_TEXT 1007
#define IDC_DEFAULT_BUTTON 1008
#define IDC_DISABLE_SUBSONGS 1009
#define IDC_DOWNMIX 1010

View File

@ -3,6 +3,7 @@
#include <windows.h>
#define IDC_STATIC -1
//elements: text, id, x, y, width, height [, style [, extended-style]]
IDD_CONFIG DIALOGEX 0, 0, 187, 164
STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "in_vgmstream configuration"
@ -23,8 +24,9 @@ BEGIN
CONTROL "Loop forever",IDC_LOOP_FOREVER,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,7,70,77,10
CONTROL "Ignore looping",IDC_IGNORE_LOOP,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,7,83,77,10
CONTROL "Disable subsongs",IDC_DISABLE_SUBSONGS,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,99,87,10
LTEXT "Thread Priority",IDC_STATIC,21,119,46,8
CONTROL "Slider1",IDC_THREAD_PRIORITY_SLIDER,"msctls_trackbar32",TBS_AUTOTICKS | WS_TABSTOP,7,127,77,10
CTEXT "DATARIFIC",IDC_THREAD_PRIORITY_TEXT,7,140,77,18
CONTROL "Downmix",IDC_DOWNMIX,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,112,87,10
LTEXT "Thread Priority",IDC_STATIC,21,132,46,8
CONTROL "Slider1",IDC_THREAD_PRIORITY_SLIDER,"msctls_trackbar32",TBS_AUTOTICKS | WS_TABSTOP,7,140,77,10
CTEXT "DATARIFIC",IDC_THREAD_PRIORITY_TEXT,7,153,77,18
END