From 34d4500e54db3c1431ff5c6900eb608da8da4d39 Mon Sep 17 00:00:00 2001 From: bnnm Date: Sat, 15 Dec 2018 11:17:47 +0100 Subject: [PATCH 1/3] Add .sps EAMP3 [Need for Speed (PS4)] --- src/coding/mpeg_custom_utils_eamp3.c | 175 +++++++++++++++++++++++++++ src/coding/mpeg_decoder.c | 2 + src/coding/mpeg_decoder.h | 2 + src/libvgmstream.vcproj | 4 + src/libvgmstream.vcxproj | 1 + src/libvgmstream.vcxproj.filters | 3 + src/meta/ea_eaac.c | 22 +++- src/meta/ea_eaac_streamfile.h | 2 + src/vgmstream.h | 3 +- 9 files changed, 212 insertions(+), 2 deletions(-) create mode 100644 src/coding/mpeg_custom_utils_eamp3.c diff --git a/src/coding/mpeg_custom_utils_eamp3.c b/src/coding/mpeg_custom_utils_eamp3.c new file mode 100644 index 00000000..5925ccc1 --- /dev/null +++ b/src/coding/mpeg_custom_utils_eamp3.c @@ -0,0 +1,175 @@ +#include "mpeg_decoder.h" + +#ifdef VGM_USE_MPEG + +/* parsed info from a single EAMP3 frame */ +typedef struct { + uint32_t extended_flag; + uint32_t stereo_flag; /* assumed */ + uint32_t unknown_flag; /* unused? */ + uint32_t frame_size; /* full size including headers and pcm block */ + uint32_t pcm_number; /* samples in the PCM block (typically 1 MPEG frame, 1152) */ + + uint32_t pre_size; /* size of the header part */ + uint32_t mpeg_size; /* size of the MPEG part */ + uint32_t pcm_size; /* size of the PCM block */ +} eamp3_frame_info; + +static int eamp3_parse_frame(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data, eamp3_frame_info * eaf); +static int eamp3_write_pcm_block(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data, int num_stream, eamp3_frame_info * eaf); +static int eamp3_skip_data(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data, int num_stream, int at_start); + +/* init config and validate */ +int mpeg_custom_setup_init_eamp3(STREAMFILE *streamFile, off_t start_offset, mpeg_codec_data *data, coding_t *coding_type) { + mpeg_frame_info info; + uint16_t frame_header; + size_t header_size; + + + /* test unknown stuff */ + frame_header = (uint16_t)read_16bitLE(start_offset+0x00, streamFile); + if (frame_header & 0x2000) { + VGM_LOG("EAMP3: found unknown bit 13\n"); + goto fail; + } + if ((frame_header & 0x8000) && (uint32_t)read_32bitLE(start_offset+0x02, streamFile) > 0xFFFF) { + VGM_LOG("EAMP3: found big PCM block\n"); + goto fail; + } + + /* get frame info at offset */ + header_size = (frame_header & 0x8000) ? 0x06 : 0x02; + if (!mpeg_get_frame_info(streamFile, start_offset+header_size, &info)) + goto fail; + switch(info.layer) { + case 1: *coding_type = coding_MPEG_layer1; break; + case 2: *coding_type = coding_MPEG_layer2; break; + case 3: *coding_type = coding_MPEG_layer3; break; + default: goto fail; + } + data->channels_per_frame = info.channels; + data->samples_per_frame = info.frame_samples; + data->bitrate_per_frame = info.bit_rate; + data->sample_rate_per_frame = info.sample_rate; + + + return 1; +fail: + return 0; +} + +/* reads custom frame header + MPEG data + (optional) PCM block */ +int mpeg_custom_parse_frame_eamp3(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data, int num_stream) { + mpeg_custom_stream *ms = data->streams[num_stream]; + eamp3_frame_info eaf; + int ok; + + + if (!eamp3_skip_data(stream, data, num_stream, 1)) + goto fail; + + ok = eamp3_parse_frame(stream, data, &eaf); + if (!ok) goto fail; + + ok = eamp3_write_pcm_block(stream, data, num_stream, &eaf); + if (!ok) goto fail; + + ms->bytes_in_buffer = read_streamfile(ms->buffer,stream->offset + eaf.pre_size, eaf.mpeg_size, stream->streamfile); + stream->offset += eaf.frame_size; + + if (!eamp3_skip_data(stream, data, num_stream, 0)) + goto fail; + + return 1; +fail: + return 0; +} + + +static int eamp3_parse_frame(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data, eamp3_frame_info * eaf) { + uint16_t current_header = (uint16_t)read_16bitLE(stream->offset+0x00, stream->streamfile); + + eaf->extended_flag = (current_header & 0x8000); + eaf->stereo_flag = (current_header & 0x4000); + eaf->unknown_flag = (current_header & 0x2000); + eaf->frame_size = (current_header & 0x1FFF); /* full size including PCM block */ + eaf->pcm_number = 0; + if (eaf->extended_flag > 0) { + eaf->pcm_number = (uint32_t)read_32bitLE(stream->offset+0x02, stream->streamfile); + eaf->pcm_size = sizeof(sample) * eaf->pcm_number * data->channels_per_frame; + eaf->pre_size = 0x06; + eaf->mpeg_size = eaf->frame_size - eaf->pre_size - eaf->pcm_size; + if (eaf->frame_size < eaf->pre_size + eaf->pcm_size) { + VGM_LOG("EAMP3: bad pcm size at %x\n", (uint32_t)stream->offset); + goto fail; + } + } + else { + eaf->pcm_size = 0; + eaf->pre_size = 0x02; + eaf->mpeg_size = eaf->frame_size - eaf->pre_size; + } + + return 1; +fail: + return 0; +} + +/* write PCM block directly to sample buffer and setup decode discard (see EALayer3). */ +static int eamp3_write_pcm_block(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data, int num_stream, eamp3_frame_info * eaf) { + mpeg_custom_stream *ms = data->streams[num_stream]; + size_t bytes_filled; + int i; + + + bytes_filled = sizeof(sample) * ms->samples_filled * data->channels_per_frame; + if (bytes_filled + eaf->pcm_size > ms->output_buffer_size) { + VGM_LOG("EAMP3: can't fill the sample buffer with 0x%x\n", eaf->pcm_size); + goto fail; + } + + + if (eaf->pcm_number) { + + /* read + write PCM block samples (always LE) */ + for (i = 0; i < eaf->pcm_number * data->channels_per_frame; i++) { + off_t pcm_offset = stream->offset + eaf->pre_size + eaf->mpeg_size + sizeof(sample)*i; + int16_t pcm_sample = read_16bitLE(pcm_offset,stream->streamfile); + put_16bitLE(ms->output_buffer + bytes_filled + sizeof(sample)*i, pcm_sample); + } + ms->samples_filled += eaf->pcm_number; + + /* modify decoded samples */ + { + size_t decode_to_discard = eaf->pcm_number; //todo guessed + ms->decode_to_discard += decode_to_discard; + } + } + + return 1; +fail: + return 0; +} + +/* Skip EA-frames from other streams for .sns/sps multichannel (see EALayer3). */ +static int eamp3_skip_data(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data, int num_stream, int at_start) { + int ok, i; + eamp3_frame_info eaf; + int skips = at_start ? num_stream : data->streams_size - 1 - num_stream; + + + for (i = 0; i < skips; i++) { + ok = eamp3_parse_frame(stream, data, &eaf); + if (!ok) goto fail; + + //;VGM_LOG("s%i: skipping %x, now at %lx\n", num_stream,eaf.frame_size,stream->offset); + stream->offset += eaf.frame_size; + } + //;VGM_LOG("s%i: skipped %i frames, now at %lx\n", num_stream,skips,stream->offset); + + return 1; +fail: + return 0; +} + +#endif diff --git a/src/coding/mpeg_decoder.c b/src/coding/mpeg_decoder.c index fd9c5f2e..604e0fa0 100644 --- a/src/coding/mpeg_decoder.c +++ b/src/coding/mpeg_decoder.c @@ -140,6 +140,7 @@ mpeg_codec_data *init_mpeg_custom(STREAMFILE *streamFile, off_t start_offset, co case MPEG_EAL32P: case MPEG_EAL32S: ok = mpeg_custom_setup_init_ealayer3(streamFile, start_offset, data, coding_type); break; case MPEG_AWC: ok = mpeg_custom_setup_init_awc(streamFile, start_offset, data, coding_type); break; + case MPEG_EAMP3: ok = mpeg_custom_setup_init_eamp3(streamFile, start_offset, data, coding_type); break; default: ok = mpeg_custom_setup_init_default(streamFile, start_offset, data, coding_type); break; } if (!ok) @@ -399,6 +400,7 @@ static void decode_mpeg_custom_stream(VGMSTREAMCHANNEL *stream, mpeg_codec_data case MPEG_EAL32S: ok = mpeg_custom_parse_frame_ealayer3(stream, data, num_stream); break; case MPEG_AHX: ok = mpeg_custom_parse_frame_ahx(stream, data, num_stream); break; case MPEG_AWC: ok = mpeg_custom_parse_frame_awc(stream, data, num_stream); break; + case MPEG_EAMP3: ok = mpeg_custom_parse_frame_eamp3(stream, data, num_stream); break; default: ok = mpeg_custom_parse_frame_default(stream, data, num_stream); break; } if (!ok) { diff --git a/src/coding/mpeg_decoder.h b/src/coding/mpeg_decoder.h index 94e31b13..a8f11733 100644 --- a/src/coding/mpeg_decoder.h +++ b/src/coding/mpeg_decoder.h @@ -21,11 +21,13 @@ int mpeg_get_frame_info(STREAMFILE *streamfile, off_t offset, mpeg_frame_info * int mpeg_custom_setup_init_default(STREAMFILE *streamFile, off_t start_offset, mpeg_codec_data *data, coding_t *coding_type); int mpeg_custom_setup_init_ealayer3(STREAMFILE *streamFile, off_t start_offset, mpeg_codec_data *data, coding_t *coding_type); int mpeg_custom_setup_init_awc(STREAMFILE *streamFile, off_t start_offset, mpeg_codec_data *data, coding_t *coding_type); +int mpeg_custom_setup_init_eamp3(STREAMFILE *streamFile, off_t start_offset, mpeg_codec_data *data, coding_t *coding_type); int mpeg_custom_parse_frame_default(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data, int num_stream); int mpeg_custom_parse_frame_ahx(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data, int num_stream); int mpeg_custom_parse_frame_ealayer3(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data, int num_stream); int mpeg_custom_parse_frame_awc(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data, int num_stream); +int mpeg_custom_parse_frame_eamp3(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data, int num_stream); #endif/* VGM_USE_MPEG */ diff --git a/src/libvgmstream.vcproj b/src/libvgmstream.vcproj index cb731253..85614108 100644 --- a/src/libvgmstream.vcproj +++ b/src/libvgmstream.vcproj @@ -1837,6 +1837,10 @@ + + + diff --git a/src/libvgmstream.vcxproj.filters b/src/libvgmstream.vcxproj.filters index dfd7938a..f8ab0189 100644 --- a/src/libvgmstream.vcxproj.filters +++ b/src/libvgmstream.vcxproj.filters @@ -1090,6 +1090,9 @@ coding\Source Files + + coding\Source Files + coding\Source Files diff --git a/src/meta/ea_eaac.c b/src/meta/ea_eaac.c index 2b5ff700..80f3f370 100644 --- a/src/meta/ea_eaac.c +++ b/src/meta/ea_eaac.c @@ -19,6 +19,7 @@ #define EAAC_CODEC_DSP 0x08 #define EAAC_CODEC_EASPEEX 0x09 #define EAAC_CODEC_EATRAX 0x0a +#define EAAC_CODEC_EAMP3 0x0b #define EAAC_CODEC_EAOPUS 0x0c #define EAAC_FLAG_NONE 0x00 @@ -680,6 +681,25 @@ static VGMSTREAM * init_vgmstream_eaaudiocore_header(STREAMFILE * streamHead, ST } #endif + +#ifdef VGM_USE_MPEG + case EAAC_CODEC_EAMP3: { /* "EM30"?: EAMP3 [Need for Speed 2015 (PS4)] */ + mpeg_custom_config cfg = {0}; + + start_offset = 0x00; /* must point to the custom streamfile's beginning */ + + temp_streamFile = setup_eaac_streamfile(streamData, eaac.version, eaac.codec, eaac.streamed,0,0, eaac.stream_offset); + if (!temp_streamFile) goto fail; + + vgmstream->codec_data = init_mpeg_custom(temp_streamFile, start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_EAMP3, &cfg); + if (!vgmstream->codec_data) goto fail; + vgmstream->layout_type = layout_none; + + break; + } + +#endif + #ifdef VGM_USE_FFMPEG case EAAC_CODEC_EAOPUS: { /* EAOpus (unknown FourCC) [FIFA 17 (PC), FIFA 19 (Switch)]*/ int skip = 0; @@ -701,7 +721,7 @@ static VGMSTREAM * init_vgmstream_eaaudiocore_header(STREAMFILE * streamHead, ST } #endif - case EAAC_CODEC_EASPEEX: /* EASpeex (libspeex variant, base versions vary: 1.0.5, 1.2beta3) */ //todo + case EAAC_CODEC_EASPEEX: /* "Esp0"?: EASpeex (libspeex variant, base versions vary: 1.0.5, 1.2beta3) */ //todo default: VGM_LOG("EA EAAC: unknown codec 0x%02x\n", eaac.codec); goto fail; diff --git a/src/meta/ea_eaac_streamfile.h b/src/meta/ea_eaac_streamfile.h index 8c6d3ce9..8ece6be2 100644 --- a/src/meta/ea_eaac_streamfile.h +++ b/src/meta/ea_eaac_streamfile.h @@ -85,6 +85,7 @@ static size_t eaac_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, case 0x05: /* EALayer3 v1 */ case 0x06: /* EALayer3 v2 "PCM" */ case 0x07: /* EALayer3 v2 "Spike" */ + case 0x0b: /* EAMP3 */ case 0x0c: /* EAOpus */ data->skip_size = 0x08; data->data_size = data->block_size - data->skip_size; @@ -205,6 +206,7 @@ static size_t eaac_io_size(STREAMFILE *streamfile, eaac_io_data* data) { case 0x05: /* EALayer3 v1 */ case 0x06: /* EALayer3 v2 "PCM" */ case 0x07: /* EALayer3 v2 "Spike" */ + case 0x0b: /* EAMP3 */ case 0x0c: /* EAOpus */ data_size = block_size - 0x08; break; diff --git a/src/vgmstream.h b/src/vgmstream.h index 7ce02dea..1e169121 100644 --- a/src/vgmstream.h +++ b/src/vgmstream.h @@ -952,7 +952,8 @@ typedef enum { MPEG_EAL32P, /* EALayer3 v2 "PCM", custom frames with v2 header + bigger PCM blocks? */ MPEG_EAL32S, /* EALayer3 v2 "Spike", custom frames with v2 header + smaller PCM blocks? */ MPEG_LYN, /* N streams of fixed interleave */ - MPEG_AWC /* N streams in block layout (music) or absolute offsets (sfx) */ + MPEG_AWC, /* N streams in block layout (music) or absolute offsets (sfx) */ + MPEG_EAMP3 /* custom frame header + MPEG frame + PCM blocks */ } mpeg_custom_t; /* config for the above modes */ From a0ece34e936a749cbb739cb81529e0591024dd2c Mon Sep 17 00:00:00 2001 From: bnnm Date: Sat, 15 Dec 2018 12:36:48 +0100 Subject: [PATCH 2/3] Update doc --- doc/BUILD.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/doc/BUILD.md b/doc/BUILD.md index 308e0d33..d839f3a1 100644 --- a/doc/BUILD.md +++ b/doc/BUILD.md @@ -144,6 +144,7 @@ Should be buildable with Autotools, much like the Audacious plugin, though requi Windows builds are possible with libao.dll and includes, but some features are disabled. +libao is licensed under the GPL v2 or later. ## External libraries Support for some codecs is done with external libs, instead of copying their code in vgmstream. There are various reasons for this: @@ -164,6 +165,7 @@ Below is a quick explanation of each library and how to compile binaries from th Adds support for Vorbis (inside Ogg and custom containers). - Source: http://downloads.xiph.org/releases/vorbis/libvorbis-1.3.6.zip - DLL: `libvorbis.dll` +- licensed under the 3-clause BSD license. Should be buildable with MSVC (in /win32 dir are .sln files) or autotools (use `autogen.sh`). @@ -173,6 +175,7 @@ Adds support for MPEG (MP1/MP2/MP3). - Source: https://sourceforge.net/projects/mpg123/files/mpg123/1.25.10/ - Builds: http://www.mpg123.de/download/win32/1.25.10/ - DLL: `libmpg123-0.dll` +- licensed under the LGPL v2.1 Must use autotools (sh configure, make, make install), though some scripts simplify the process: `makedll.sh`, `windows-builds.sh`. @@ -181,7 +184,9 @@ Must use autotools (sh configure, make, make install), though some scripts simpl Adds support for ITU-T G.722.1 annex C (standardization of Polycom Siren 14). - Source: https://github.com/bnnm/vgmstream-g7221 - Alt lib (has volume problems): https://github.com/kode54/libg7221_decode + - licensed under the LGPL v3 (possibly invalid and Polycom's) - DLL: `libg7221_decode.dll` +- unknown license (possibly invalid and Polycom's) Use make `libg7221_decode.dll`. @@ -189,6 +194,7 @@ Use make `libg7221_decode.dll`. Adds support for ITU-T G.719 (standardization of Polycom Siren 22). - Source: https://github.com/kode54/libg719_decode - DLL: `libg719_decode.dll` +- unknown license (possibly invalid and Polycom's) Requires MSVC (use `g719.sln`). @@ -197,6 +203,7 @@ Requires MSVC (use `g719.sln`). Adds support for multiple codecs: ATRAC3, ATRAC3plus, XMA1/2, WMA v1, WMA v2, WMAPro, AAC, Bink, AC3/SPDIF, Opus, Musepack, FLAC, etc (also Vorbis and MPEG for certain cases). - Source: https://github.com/FFmpeg/FFmpeg/ - DLLs: `avcodec-vgmstream-58.dll`, `avformat-vgmstream-58.dll`, `avutil-vgmstream-56.dll`, `swresample-vgmstream-3.dll` +- primarily licensed under the LGPL v2.1 or later, with portions licensed under the GPL v2 vgmstream's FFmpeg builds remove many unnecessary parts of FFmpeg to trim down its gigantic size, and are also built with the "vgmstream-" preffix (to avoid clashing with other plugins). Current options can be seen in `ffmpeg_options.txt`. @@ -211,6 +218,7 @@ Both may need yasm somewhere in PATH to properly compile: https://yasm.tortall.n Adds support for ATRAC9. - Source: https://github.com/Thealexbarney/LibAtrac9 - DLL: `libatrac9.dll` +- licensed under the MIT license Use MSCV and `libatrac9.sln`, or GCC and the Makefile included. @@ -220,6 +228,7 @@ Adds support for FSB CELT versions 0.6.1 and 0.11.0. - Source (0.6.1): http://downloads.us.xiph.org/releases/celt/celt-0.6.1.tar.gz - Source (0.11.0): http://downloads.xiph.org/releases/celt/celt-0.11.0.tar.gz - DLL: `libcelt-0061.dll`, `libcelt-0110.dll` +- licensed under the MIT license FSB uses two incompatible, older libcelt versions. Both libraries export the same symbols so normally can't coexist together. To get them working we need to make sure symbols are renamed first. This may be solved in various ways: - using dynamic loading (LoadLibrary) but for portability it isn't an option From 770343b25579a38a2bae4790d08d2b2c6161ddf9 Mon Sep 17 00:00:00 2001 From: bnnm Date: Sat, 15 Dec 2018 12:37:01 +0100 Subject: [PATCH 3/3] Add .nus3audio [Super Smash Bros. Ultimate (Switch)] --- src/formats.c | 1 + src/libvgmstream.vcproj | 4 ++ src/libvgmstream.vcxproj | 1 + src/libvgmstream.vcxproj.filters | 3 + src/meta/meta.h | 2 + src/meta/nus3audio.c | 119 +++++++++++++++++++++++++++++++ src/vgmstream.c | 1 + 7 files changed, 131 insertions(+) create mode 100644 src/meta/nus3audio.c diff --git a/src/formats.c b/src/formats.c index 9688ab3b..9db2151d 100644 --- a/src/formats.c +++ b/src/formats.c @@ -263,6 +263,7 @@ static const char* extension_list[] = { "nop", "nps", "npsf", //fake extension/header id for .nps (in bigfiles) + "nus3audio", "nus3bank", "nwa", "nwav", diff --git a/src/libvgmstream.vcproj b/src/libvgmstream.vcproj index 85614108..e43284f2 100644 --- a/src/libvgmstream.vcproj +++ b/src/libvgmstream.vcproj @@ -846,6 +846,10 @@ RelativePath=".\meta\nub_xma.c" > + + diff --git a/src/libvgmstream.vcxproj b/src/libvgmstream.vcxproj index f3f99bbe..ca301847 100644 --- a/src/libvgmstream.vcxproj +++ b/src/libvgmstream.vcxproj @@ -319,6 +319,7 @@ + diff --git a/src/libvgmstream.vcxproj.filters b/src/libvgmstream.vcxproj.filters index f8ab0189..f36e72ff 100644 --- a/src/libvgmstream.vcxproj.filters +++ b/src/libvgmstream.vcxproj.filters @@ -520,6 +520,9 @@ meta\Source Files + + meta\Source Files + meta\Source Files diff --git a/src/meta/meta.h b/src/meta/meta.h index 287af92a..a068fc96 100644 --- a/src/meta/meta.h +++ b/src/meta/meta.h @@ -819,4 +819,6 @@ VGMSTREAM * init_vgmstream_zsnd(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_ogg_opus(STREAMFILE * streamFile); +VGMSTREAM * init_vgmstream_nus3audio(STREAMFILE * streamFile); + #endif /*_META_H*/ diff --git a/src/meta/nus3audio.c b/src/meta/nus3audio.c new file mode 100644 index 00000000..79826bc3 --- /dev/null +++ b/src/meta/nus3audio.c @@ -0,0 +1,119 @@ +#include "meta.h" +#include "../coding/coding.h" + +typedef enum { IDSP, OPUS, } nus3audio_codec; + +/* .nus3audio - Namco's newest newest audio container [Super Smash Bros. Ultimate (Switch)] */ +VGMSTREAM * init_vgmstream_nus3audio(STREAMFILE *streamFile) { + VGMSTREAM *vgmstream = NULL; + STREAMFILE *temp_streamFile = NULL; + off_t subfile_offset = 0, name_offset = 0; + size_t subfile_size = 0; + nus3audio_codec codec; + const char* fake_ext = NULL; + int total_subsongs, target_subsong = streamFile->stream_index; + + + /* checks */ + if (!check_extensions(streamFile, "nus3audio")) + goto fail; + if (read_32bitBE(0x00,streamFile) != 0x4E555333) /* "NUS3" */ + goto fail; + if (read_32bitLE(0x04,streamFile) + 0x08 != get_streamfile_size(streamFile)) + goto fail; + if (read_32bitBE(0x08,streamFile) != 0x41554449) /* "AUDI" */ + goto fail; + + + /* parse existing chunks */ + { + off_t offset = 0x0c; + size_t file_size = get_streamfile_size(streamFile); + uint32_t codec_id = 0; + + total_subsongs = 0; + + while (offset < file_size) { + uint32_t chunk_id = (uint32_t)read_32bitBE(offset+0x00, streamFile); + size_t chunk_size = (size_t)read_32bitLE(offset+0x04, streamFile); + + switch(chunk_id) { + case 0x494E4458: /* "INDX": audio index */ + total_subsongs = read_32bitLE(offset+0x08 + 0x00,streamFile); + if (target_subsong == 0) target_subsong = 1; + if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail; + break; + + case 0x4E4D4F46: /* "NMOF": name offsets (absolute, inside TNNM) */ + name_offset = read_32bitLE(offset+0x08 + 0x04*(target_subsong-1),streamFile); + break; + + case 0x41444F46: /* "ADOF": audio offsets (absolute, inside PACK) */ + subfile_offset = read_32bitLE(offset+0x08 + 0x08*(target_subsong-1) + 0x00,streamFile); + subfile_size = read_32bitLE(offset+0x08 + 0x08*(target_subsong-1) + 0x04,streamFile); + break; + + case 0x544E4944: /* "TNID": tone ids? */ + case 0x544E4E4D: /* "TNNM": tone names */ + case 0x4A554E4B: /* "JUNK": padding */ + case 0x5041434B: /* "PACK": main data */ + default: + break; + } + + offset += 0x08 + chunk_size; + } + + if (total_subsongs == 0 || subfile_offset == 0 || subfile_size == 0) { + VGM_LOG("NUS3AUDIO: subfile not found\n"); + goto fail; + } + + codec_id = read_32bitBE(subfile_offset, streamFile); + switch(codec_id) { + case 0x49445350: /* "IDSP" */ + codec = IDSP; + fake_ext = "idsp"; + break; + case 0x4F505553: /* "OPUS" */ + codec = OPUS; + fake_ext = "opus"; + break; + default: + VGM_LOG("NUS3AUDIO: unknown codec %x\n", codec_id); + goto fail; + } + } + + + temp_streamFile = setup_subfile_streamfile(streamFile, subfile_offset,subfile_size, fake_ext); + if (!temp_streamFile) goto fail; + + /* init the VGMSTREAM */ + switch(codec) { + case IDSP: + vgmstream = init_vgmstream_idsp_nus3(temp_streamFile); + if (!vgmstream) goto fail; + break; + case OPUS: + vgmstream = init_vgmstream_opus_nus3(temp_streamFile); + if (!vgmstream) goto fail; + break; + default: + goto fail; + } + + vgmstream->num_streams = total_subsongs; + if (name_offset) /* null-terminated */ + read_string(vgmstream->stream_name,STREAM_NAME_SIZE, name_offset,streamFile); + + close_streamfile(temp_streamFile); + return vgmstream; + +fail: + close_streamfile(temp_streamFile); + close_vgmstream(vgmstream); + return NULL; +} + + diff --git a/src/vgmstream.c b/src/vgmstream.c index 1023e894..47aba049 100644 --- a/src/vgmstream.c +++ b/src/vgmstream.c @@ -456,6 +456,7 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_opus_opusx, init_vgmstream_dsp_adpcmx, init_vgmstream_ogg_opus, + init_vgmstream_nus3audio, /* lowest priority metas (should go after all metas, and TXTH should go before raw formats) */ init_vgmstream_txth, /* proper parsers should supersede TXTH, once added */