Merge pull request #217 from bnnm/opus-sps-etc

Opus, sps, etc
This commit is contained in:
Christopher Snowhill 2018-04-21 19:04:13 -07:00 committed by GitHub
commit 4ea0d1a9d7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
44 changed files with 2050 additions and 1883 deletions

View File

@ -11,8 +11,8 @@ typedef struct _VFSSTREAMFILE {
STREAMFILE sf;
VFSFile *vfsFile;
off_t offset;
char name[260];
char realname[260];
char name[32768];
//char realname[32768];
} VFSSTREAMFILE;
static STREAMFILE *open_vfs_by_VFSFILE(VFSFile *file, const char *path);
@ -53,12 +53,6 @@ static void get_name_vfs(VFSSTREAMFILE *streamfile, char *buffer,
buffer[length - 1] = '\0';
}
static void get_realname_vfs(VFSSTREAMFILE *streamfile, char *buffer,
size_t length) {
strncpy(buffer, streamfile->realname, length);
buffer[length - 1] = '\0';
}
static STREAMFILE *open_vfs_impl(VFSSTREAMFILE *streamfile,
const char *const filename,
size_t buffersize) {
@ -80,7 +74,6 @@ STREAMFILE *open_vfs_by_VFSFILE(VFSFile *file, const char *path) {
streamfile->sf.get_size = get_size_vfs;
streamfile->sf.get_offset = get_offset_vfs;
streamfile->sf.get_name = get_name_vfs;
streamfile->sf.get_realname = get_realname_vfs;
streamfile->sf.open = open_vfs_impl;
streamfile->sf.close = close_vfs;
@ -88,12 +81,16 @@ STREAMFILE *open_vfs_by_VFSFILE(VFSFile *file, const char *path) {
streamfile->offset = 0;
strncpy(streamfile->name, path, sizeof(streamfile->name));
streamfile->name[sizeof(streamfile->name) - 1] = '\0';
{
gchar *realname = g_filename_from_uri(path, NULL, NULL);
strncpy(streamfile->realname, realname, sizeof(streamfile->realname));
streamfile->realname[sizeof(streamfile->realname) - 1] = '\0';
g_free(realname);
}
// for reference, actual file path ("name" has protocol path, file://...).
// name should work for all situations but in case it's needed again maybe
// get_name should always return realname, as it's used to open companion VFSFiles
//{
// gchar *realname = g_filename_from_uri(path, NULL, NULL);
// strncpy(streamfile->realname, realname, sizeof(streamfile->realname));
// streamfile->realname[sizeof(streamfile->realname) - 1] = '\0';
// g_free(realname);
//}
return &streamfile->sf;
}

View File

@ -230,7 +230,6 @@ static STREAMFILE * open_foo_streamfile_buffer_by_file(service_ptr_t<file> m_fil
streamfile->sf.get_size = (size_t (__cdecl *)(_STREAMFILE *)) get_size_foo;
streamfile->sf.get_offset = (off_t (__cdecl *)(_STREAMFILE *)) get_offset_foo;
streamfile->sf.get_name = (void (__cdecl *)(_STREAMFILE *,char *,size_t)) get_name_foo;
streamfile->sf.get_realname = (void (__cdecl *)(_STREAMFILE *,char *,size_t)) get_name_foo;
streamfile->sf.open = (_STREAMFILE *(__cdecl *)(_STREAMFILE *,const char *const ,size_t)) open_foo;
streamfile->sf.close = (void (__cdecl *)(_STREAMFILE *)) close_foo;

View File

@ -147,10 +147,12 @@ int mpeg_custom_parse_frame_ealayer3(VGMSTREAMCHANNEL *stream, mpeg_codec_data *
if (!ealayer3_skip_data(stream, data, num_stream, 0))
goto fail;
}
// todo rarely there is a block between granules (ex. EAL3 v2P in FIFA 2016)
/* In EAL3 V2P sometimes there is a new SNS/SPS block between granules. Instead of trying to fix it here
* or in blocked layout (too complex/patchy), SNS/SPS uses a custom streamfile that simply removes all
* block headers, so this parser only sees raw EALayer3 data. It also discards samples, which confuses
* blocked layout calculations */
/* get second frame/granule (MPEG1 only) if first granule was found */
granule_found = 0;
@ -298,7 +300,7 @@ static int ealayer3_parse_frame_v2(vgm_bitstream *is, ealayer3_frame_info * eaf)
if (!ok) goto fail;
}
VGM_ASSERT(eaf->v2_extended_flag && eaf->v2_common_size == 0, "EA EAL3: v2 empty frame\n"); /* seen in V2S */
VGM_ASSERT(eaf->v2_extended_flag && eaf->v2_mode_value > 0, "EA EAL3: v2_mode=%x with 0x%x\n", eaf->v2_mode, eaf->v2_mode_value);
VGM_ASSERT(eaf->v2_extended_flag && eaf->v2_mode_value > 0, "EA EAL3: v2_mode=%x with value=0x%x\n", eaf->v2_mode, eaf->v2_mode_value);
//VGM_ASSERT(eaf->v2_pcm_number > 0, "EA EAL3: v2_pcm_number 0x%x\n", eaf->v2_pcm_number);
eaf->pcm_size = (2*eaf->v2_pcm_number * eaf->channels);
@ -550,8 +552,6 @@ static int ealayer3_write_pcm_block(VGMSTREAMCHANNEL *stream, mpeg_codec_data *d
size_t bytes_filled;
int i;
if (!eaf->pcm_size)
return 1;
bytes_filled = sizeof(sample)*ms->samples_filled*data->channels_per_frame;
if (bytes_filled + eaf->pcm_size > ms->output_buffer_size) {
@ -561,6 +561,9 @@ static int ealayer3_write_pcm_block(VGMSTREAMCHANNEL *stream, mpeg_codec_data *d
if (eaf->v1_pcm_number) {
if (!eaf->pcm_size)
return 1;
VGM_ASSERT(eaf->v1_pcm_decode_discard > 576, "MPEG EAL3: big discard %i at 0x%lx\n", eaf->v1_pcm_decode_discard, stream->offset);
VGM_ASSERT(eaf->v1_pcm_number > 0x100, "MPEG EAL3: big samples %i at 0x%lx\n", eaf->v1_pcm_number, stream->offset);
@ -583,6 +586,7 @@ static int ealayer3_write_pcm_block(VGMSTREAMCHANNEL *stream, mpeg_codec_data *d
decode_to_discard = data->samples_per_frame;//+ eaf->v1_pcm_number;
}
else {
// todo also discard
if (decode_to_discard == 0) /* seems ok? */
decode_to_discard += data->samples_per_frame;//+ eaf->v1_pcm_number;
else if (decode_to_discard == 576) /* untested */
@ -594,17 +598,6 @@ static int ealayer3_write_pcm_block(VGMSTREAMCHANNEL *stream, mpeg_codec_data *d
if (eaf->v2_extended_flag) {
if (eaf->v2_pcm_number) {
/* read + write PCM block samples (always BE) */
for (i = 0; i < eaf->v2_pcm_number * data->channels_per_frame; i++) {
off_t pcm_offset = stream->offset + eaf->pre_size + eaf->common_size + sizeof(sample)*i;
int16_t pcm_sample = read_16bitBE(pcm_offset,stream->streamfile);
put_16bitLE(ms->output_buffer + bytes_filled + sizeof(sample)*i, pcm_sample);
}
ms->samples_filled += eaf->v2_pcm_number;
}
#if 0
/* todo supposed skip modes (only seen 0x00):
*
* AB00CCCC CCCCCCCC if A is set: DDEEEEEE EEEEFFFF FFFFFFGG GGGGGGGG
@ -620,16 +613,27 @@ static int ealayer3_write_pcm_block(VGMSTREAMCHANNEL *stream, mpeg_codec_data *d
* if 3: 576
*/
//;VGM_LOG("EA EAL3 v2: off=%lx, mode=%x, value=%x, pcm=%x, size=%x\n", stream->offset, eaf->v2_mode, eaf->v2_mode_value, eaf->v2_pcm_number, eaf->v2_common_size);
if (eaf->v2_pcm_number) {
/* read + write PCM block samples (always BE) */
for (i = 0; i < eaf->v2_pcm_number * data->channels_per_frame; i++) {
off_t pcm_offset = stream->offset + eaf->pre_size + eaf->common_size + sizeof(sample)*i;
int16_t pcm_sample = read_16bitBE(pcm_offset,stream->streamfile);
put_16bitLE(ms->output_buffer + bytes_filled + sizeof(sample)*i, pcm_sample);
}
ms->samples_filled += eaf->v2_pcm_number;
}
/* modify decoded samples depending on flag */
if (eaf->v2_mode == 0x00) {
size_t decode_to_discard = eaf->v2_mode_value;
if (decode_to_discard == 576)
decode_to_discard = data->samples_per_frame;//+ eaf->v2_pcm_number;
size_t decode_to_discard = eaf->v2_mode_value; /* (usually 0 in V2P, varies in V2S) */
if (decode_to_discard == 0)
decode_to_discard = 576;
//todo output seems correct-ish but reaches file end and tries to parse more frames
ms->decode_to_discard += decode_to_discard;
}
#endif
}
return 1;

View File

@ -19,6 +19,7 @@ static const char* extension_list[] = {
//"ac3", //FFmpeg, not parsed //common?
"ace", //fake, for tri-Ace's formats (to be removed)
"acm",
"ad", //txth/reserved [Xenosaga Freaks (PS2)]
"adm",
"adp",
"adpcm",
@ -957,7 +958,7 @@ static const meta_info meta_info_list[] = {
{meta_BINK, "RAD Game Tools Bink header"},
{meta_EA_SNU, "Electronic Arts SNU header"},
{meta_AWC, "Rockstar AWC header"},
{meta_NSW_OPUS, "Nintendo Switch OPUS header"},
{meta_OPUS, "Nintendo Switch OPUS header"},
{meta_PC_AL2, "Illwinter Game Design AL2 raw header"},
{meta_PC_AST, "Capcom AST (PC) header"},
{meta_UBI_SB, "Ubisoft SBx header"},
@ -1000,6 +1001,9 @@ static const meta_info meta_info_list[] = {
{meta_OGG_ENO, "Ogg Vorbis (ENO header)"},
{meta_TXTP, "TXTP generic header"},
{meta_SMC_SMH, "Genki SMC+SMH header"},
{meta_OGG_YS8, "Ogg Vorbis (Ys VIII header)"},
{meta_PPST, "Parappa PPST header"},
{meta_OPUS_PPP, "AT9 OPUS header"},
#ifdef VGM_USE_FFMPEG
{meta_FFmpeg, "FFmpeg supported file format"},

View File

@ -1,34 +1,42 @@
#include "layout.h"
#include "../vgmstream.h"
/* TODO: currently only properly handles mono substreams */
/* TODO: there must be a reasonable way to respect the loop settings, as
the substreams are in their own little world.
Currently the VGMSTREAMs layers loop internally and the external/base VGMSTREAM
doesn't actually loop, and would ignore any altered values/loop_flag. */
#define INTERLEAVE_BUF_SIZE 512
#define LAYER_BUF_SIZE 512
#define LAYER_MAX_CHANNELS 6 /* at least 2, but let's be generous */
void render_vgmstream_layered(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstream) {
sample interleave_buf[INTERLEAVE_BUF_SIZE];
sample interleave_buf[LAYER_BUF_SIZE*LAYER_MAX_CHANNELS];
int32_t samples_done = 0;
layered_layout_data *data = vgmstream->layout_data;
while (samples_done < sample_count) {
int32_t samples_to_do = INTERLEAVE_BUF_SIZE;
int c;
int32_t samples_to_do = LAYER_BUF_SIZE;
int layer;
if (samples_to_do > sample_count - samples_done)
samples_to_do = sample_count - samples_done;
for (c=0; c < data->layer_count; c++) {
int32_t i;
for (layer = 0; layer < data->layer_count; layer++) {
int s,l_ch;
int layer_channels = data->layers[layer]->channels;
render_vgmstream(interleave_buf, samples_to_do, data->layers[c]);
render_vgmstream(interleave_buf, samples_to_do, data->layers[layer]);
for (i=0; i < samples_to_do; i++) {
buffer[(samples_done+i)*data->layer_count + c] = interleave_buf[i];
for (l_ch = 0; l_ch < layer_channels; l_ch++) {
for (s = 0; s < samples_to_do; s++) {
size_t layer_sample = s*layer_channels + l_ch;
size_t buffer_sample = (samples_done+s)*vgmstream->channels + (layer*layer_channels+l_ch);
buffer[buffer_sample] = interleave_buf[layer_sample];
}
}
}
samples_done += samples_to_do;
@ -67,8 +75,7 @@ int setup_layout_layered(layered_layout_data* data) {
if (data->layers[i]->num_samples <= 0)
goto fail;
//todo only mono at the moment
if (data->layers[i]->channels != 1)
if (data->layers[i]->channels > LAYER_MAX_CHANNELS)
goto fail;
if (i > 0) {

View File

@ -225,7 +225,15 @@
>
</File>
<File
RelativePath=".\meta\ea_eaac_eatrax_streamfile.h"
RelativePath=".\meta\ea_eaac_streamfile.h"
>
</File>
<File
RelativePath=".\meta\opus_interleave_streamfile.h"
>
</File>
<File
RelativePath=".\meta\ppst_streamfile.h"
>
</File>
<File
@ -703,7 +711,7 @@
>
</File>
<File
RelativePath=".\meta\nsw_opus.c"
RelativePath=".\meta\opus.c"
>
</File>
<File
@ -733,6 +741,10 @@
<File
RelativePath=".\meta\omu.c"
>
</File>
<File
RelativePath=".\meta\opus_ppp.c"
>
</File>
<File
RelativePath=".\meta\otm.c"
@ -794,6 +806,10 @@
RelativePath=".\meta\pos.c"
>
</File>
<File
RelativePath=".\meta\ppst.c"
>
</File>
<File
RelativePath=".\meta\ps2_vds_vdm.c"
>

View File

@ -98,7 +98,9 @@
<ClInclude Include="meta\aix_streamfile.h" />
<ClInclude Include="meta\awc_xma_streamfile.h" />
<ClInclude Include="meta\bar_streamfile.h" />
<ClInclude Include="meta\ea_eaac_eatrax_streamfile.h" />
<ClInclude Include="meta\ea_eaac_streamfile.h" />
<ClInclude Include="meta\ppst_streamfile.h" />
<ClInclude Include="meta\opus_interleave_streamfile.h" />
<ClInclude Include="meta\sqex_scd_streamfile.h" />
<ClInclude Include="meta\ubi_lyn_ogg_streamfile.h" />
<ClInclude Include="meta\meta.h" />
@ -146,7 +148,7 @@
<ClCompile Include="meta\mp4.c" />
<ClCompile Include="meta\msb_msh.c" />
<ClCompile Include="meta\ngca.c" />
<ClCompile Include="meta\nsw_opus.c" />
<ClCompile Include="meta\opus.c" />
<ClCompile Include="meta\nub_vag.c" />
<ClCompile Include="meta\pc_adp.c" />
<ClCompile Include="meta\pc_adp_otns.c" />
@ -289,6 +291,7 @@
<ClCompile Include="meta\ogg_vorbis.c" />
<ClCompile Include="meta\ogl.c" />
<ClCompile Include="meta\omu.c" />
<ClCompile Include="meta\opus_ppp.c" />
<ClCompile Include="meta\otm.c" />
<ClCompile Include="meta\p3d.c" />
<ClCompile Include="meta\pc_al2.c" />
@ -300,6 +303,7 @@
<ClCompile Include="meta\scd_pcm.c" />
<ClCompile Include="meta\pona.c" />
<ClCompile Include="meta\pos.c" />
<ClCompile Include="meta\ppst.c" />
<ClCompile Include="meta\ps2_vds_vdm.c" />
<ClCompile Include="meta\ps2_adm.c" />
<ClCompile Include="meta\ps2_ads.c" />

View File

@ -74,7 +74,13 @@
<ClInclude Include="meta\bar_streamfile.h">
<Filter>meta\Header Files</Filter>
</ClInclude>
<ClInclude Include="meta\ea_eaac_eatrax_streamfile.h">
<ClInclude Include="meta\ea_eaac_streamfile.h">
<Filter>meta\Header Files</Filter>
</ClInclude>
<ClInclude Include="meta\opus_interleave_streamfile.h">
<Filter>meta\Header Files</Filter>
</ClInclude>
<ClInclude Include="meta\ppst_streamfile.h">
<Filter>meta\Header Files</Filter>
</ClInclude>
<ClInclude Include="meta\sqex_scd_streamfile.h">
@ -442,6 +448,9 @@
<ClCompile Include="meta\omu.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\opus_ppp.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\otm.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
@ -475,6 +484,9 @@
<ClCompile Include="meta\pos.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\ppst.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\ps2_vds_vdm.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
@ -1126,7 +1138,7 @@
<ClCompile Include="meta\ngca.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\nsw_opus.c">
<ClCompile Include="meta\opus.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\ps2_mtaf.c">

View File

@ -148,7 +148,6 @@ static STREAMFILE *open_aix_impl(AIXSTREAMFILE *streamfile,const char * const fi
streamfile->sf.get_size = (void*)get_size_aix;
streamfile->sf.get_offset = (void*)get_offset_aix;
streamfile->sf.get_name = (void*)get_name_aix;
streamfile->sf.get_realname = (void*)get_name_aix;
streamfile->sf.open = (void*)open_aix_impl;
streamfile->sf.close = (void*)close_aix;

View File

@ -41,11 +41,7 @@ static size_t get_offset_bar(BARSTREAMFILE *streamFile) {
}
static void get_name_bar(BARSTREAMFILE *streamFile, char *name, size_t length) {
return streamFile->real_file->get_name(streamFile->real_file, name, length);
}
static void get_realname_bar(BARSTREAMFILE *streamFile, char *name, size_t length) {
return streamFile->real_file->get_realname(streamFile->real_file, name, length);
streamFile->real_file->get_name(streamFile->real_file, name, length);
}
STREAMFILE *open_bar(BARSTREAMFILE *streamFile, const char * const filename, size_t buffersize) {
@ -75,7 +71,6 @@ static void close_bar(BARSTREAMFILE *streamFile) {
streamfile->sf.get_size = (void*)get_size_bar;
streamfile->sf.get_offset = (void*)get_offset_bar;
streamfile->sf.get_name = (void*)get_name_bar;
streamfile->sf.get_realname = (void*)get_realname_bar;
streamfile->sf.open = (void*)open_bar;
streamfile->sf.close = (void*)close_bar;

View File

@ -1,145 +1,133 @@
#include "meta.h"
#include "../util.h"
#include "../coding/coding.h"
/* FWAV - Nintendo streams */
VGMSTREAM * init_vgmstream_bfwav(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
char filename[PATH_LIMIT];
coding_t coding_type;
coding_t coding_PCM16;
int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL;
int16_t (*read_16bit)(off_t,STREAMFILE*) = NULL;
/*int ima = 0;*/
int nsmbu_flag = 0;
off_t data_offset;
off_t head_offset;
int codec_number;
int channel_count;
int loop_flag;
off_t start_offset;
/* check extension, case insensitive */
streamFile->get_name(streamFile, filename, sizeof(filename));
if (strcasecmp("bfwav", filename_extension(filename)) && strcasecmp("fwav", filename_extension(filename))) {
if (strcasecmp("bfwavnsmbu",filename_extension(filename))) goto fail;
else nsmbu_flag = 1;
}
/* check header */
if ((uint32_t)read_32bitBE(0, streamFile) != 0x46574156) /* "FWAV" */
goto fail;
if ((uint16_t)read_16bitBE(4, streamFile) == 0xFEFF) { /* endian marker (BE most common) */
read_32bit = read_32bitBE;
read_16bit = read_16bitBE;
coding_PCM16 = coding_PCM16BE;
} else if ((uint16_t)read_16bitBE(4, streamFile) == 0xFFFE) { /* LE endian marker */
read_32bit = read_32bitLE;
read_16bit = read_16bitLE;
coding_PCM16 = coding_PCM16LE;
} else {
goto fail;
}
/* get head offset, check */
head_offset = read_32bit(0x18, streamFile);
data_offset = read_32bit(0x24, streamFile);
if ((uint32_t)read_32bitBE(head_offset, streamFile) != 0x494E464F) /* "INFO" (FWAV)*/
goto fail;
VGMSTREAM * vgmstream = NULL;
off_t start_offset;
off_t info_offset, data_offset;
int channel_count, loop_flag, codec;
int big_endian;
size_t interleave = 0;
int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL;
int16_t (*read_16bit)(off_t,STREAMFILE*) = NULL;
int nsmbu_flag = 0;
/* check type details */
codec_number = read_8bit(head_offset + 0x8, streamFile);
loop_flag = read_8bit(head_offset + 0x9, streamFile);
channel_count = read_32bit(head_offset + 0x1C, streamFile);
/* checks */
/* .bfwavnsmbu: fake extension to detect New Super Mario Bros U files with weird sample rate */
if (!check_extensions(streamFile, "bfwav,fwav,bfwavnsmbu"))
goto fail;
nsmbu_flag = check_extensions(streamFile, "bfwavnsmbu");
switch (codec_number) {
case 0:
coding_type = coding_PCM8;
break;
case 1:
coding_type = coding_PCM16;
break;
case 2:
coding_type = coding_NGC_DSP;
break;
default:
goto fail;
}
/* FWAV header */
if (read_32bitBE(0x00, streamFile) != 0x46574156) /* "FWAV" */
goto fail;
/* 0x06(2): header size (0x40), 0x08: version (0x00010200), 0x0c: file size 0x10(2): sections (2) */
if (channel_count < 1) goto fail;
if ((uint16_t)read_16bitBE(0x04, streamFile) == 0xFEFF) { /* BE BOM check */
read_32bit = read_32bitBE;
read_16bit = read_16bitBE;
big_endian = 1;
} else if ((uint16_t)read_16bitBE(0x04, streamFile) == 0xFFFE) { /* LE BOM check */
read_32bit = read_32bitLE;
read_16bit = read_16bitLE;
big_endian = 0;
} else {
goto fail;
}
/* build the VGMSTREAM */
info_offset = read_32bit(0x18, streamFile); /* 0x14(2): info mark (0x7000), 0x1c: info size */
data_offset = read_32bit(0x24, streamFile); /* 0x20(2): data mark (0x7001), 0x28: data size */
vgmstream = allocate_vgmstream(channel_count, loop_flag);
if (!vgmstream) goto fail;
/* INFO section */
if (read_32bitBE(info_offset, streamFile) != 0x494E464F) /* "INFO" */
goto fail;
codec = read_8bit(info_offset + 0x08, streamFile);
loop_flag = read_8bit(info_offset + 0x09, streamFile);
channel_count = read_32bit(info_offset + 0x1C, streamFile);
/* fill in the vital statistics */
vgmstream->num_samples = read_32bit(head_offset + 0x14, streamFile);
if (nsmbu_flag)
vgmstream->sample_rate = 16000;
else
vgmstream->sample_rate = (uint16_t)read_32bit(head_offset + 0xC, streamFile);
/* channels and loop flag are set by allocate_vgmstream */
/* parse channel table */
{
off_t channel1_info, data_start;
int i;
vgmstream->loop_start_sample = read_32bit(head_offset + 0x10, streamFile);
vgmstream->loop_end_sample = vgmstream->num_samples;
channel1_info = info_offset + 0x1c + read_32bit(info_offset+0x20+0x08*0+0x04, streamFile);
data_start = read_32bit(channel1_info+0x04, streamFile); /* within "DATA" after 0x08 */
vgmstream->coding_type = coding_type;
if (channel_count == 1)
vgmstream->layout_type = layout_none;
else
vgmstream->layout_type = layout_interleave;
/* channels use absolute offsets but should be ok as interleave */
interleave = 0;
if (channel_count > 1) {
off_t channel2_info = info_offset + 0x1c + read_32bit(info_offset+0x20+0x08*1+0x04, streamFile);
interleave = read_32bit(channel2_info+0x04, streamFile) - data_start;
}
start_offset = data_offset + 0x08 + data_start;
/* validate all channels just in case of multichannel with non-constant interleave */
for (i = 0; i < channel_count; i++) {
/* channel table, 0x00: flag (0x7100), 0x04: channel info offset */
off_t channel_info = info_offset + 0x1c + read_32bit(info_offset+0x20+0x08*i+0x04, streamFile);
/* channel info, 0x00(2): flag (0x1f00), 0x04: offset, 0x08(2): ADPCM flag (0x0300), 0x0c: ADPCM offset */
if ((uint16_t)read_16bit(channel_info+0x00, streamFile) != 0x1F00)
goto fail;
if (read_32bit(channel_info+0x04, streamFile) != data_start + interleave*i)
goto fail;
}
}
vgmstream->meta_type = meta_FWAV;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count, loop_flag);
if (!vgmstream) goto fail;
vgmstream->interleave_block_size = read_32bit(read_32bit(0x6c, streamFile) + 0x60, streamFile) - 0x18;
vgmstream->sample_rate = read_32bit(info_offset + 0x0C, streamFile);
if (nsmbu_flag)
vgmstream->sample_rate = 16000;
vgmstream->num_samples = read_32bit(info_offset + 0x14, streamFile);
vgmstream->loop_start_sample = read_32bit(info_offset + 0x10, streamFile);
vgmstream->loop_end_sample = vgmstream->num_samples;
vgmstream->meta_type = meta_FWAV;
vgmstream->layout_type = (channel_count == 1) ? layout_none : layout_interleave;
vgmstream->interleave_block_size = interleave;
switch (codec) {
case 0x00:
vgmstream->coding_type = coding_PCM8;
break;
case 0x01:
vgmstream->coding_type = big_endian ? coding_PCM16BE : coding_PCM16LE;
break;
case 0x02:
vgmstream->coding_type = coding_NGC_DSP;
{
int i, c;
off_t coef_header, coef_offset;
for (i = 0; i < vgmstream->channels; i++) {
for (c = 0; c < 16; c++) {
coef_header = info_offset + 0x1C + read_32bit(info_offset + 0x24 + (i*0x08), streamFile);
coef_offset = read_32bit(coef_header + 0x0c, streamFile) + coef_header;
vgmstream->ch[i].adpcm_coef[c] = read_16bit(coef_offset + c*2, streamFile);
}
}
}
break;
default: /* 0x03: IMA? */
goto fail;
}
start_offset = data_offset + 0x20;
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
goto fail;
return vgmstream;
if (vgmstream->coding_type == coding_NGC_DSP) {
int i, j;
for (j = 0; j<vgmstream->channels; j++) {
for (i = 0; i<16; i++) {
off_t coeffheader = head_offset + 0x1C + read_32bit(head_offset + 0x24 + (j*8), streamFile);
off_t coef_offset;
if ((uint32_t)read_16bit(coeffheader, streamFile) != 0x1F00) goto fail;
coef_offset = read_32bit(coeffheader + 0xC, streamFile) + coeffheader;
vgmstream->ch[j].adpcm_coef[i] = read_16bit(coef_offset + i * 2, streamFile);
}
}
}
/* open the file for reading by each channel */
{
int i;
for (i = 0; i<channel_count; i++) {
if (vgmstream->layout_type == layout_interleave)
vgmstream->ch[i].streamfile = streamFile->open(streamFile, filename,STREAMFILE_DEFAULT_BUFFER_SIZE);
else
vgmstream->ch[i].streamfile = streamFile->open(streamFile, filename,0x1000);
if (!vgmstream->ch[i].streamfile) goto fail;
vgmstream->ch[i].channel_start_offset =
vgmstream->ch[i].offset =
start_offset + i*vgmstream->interleave_block_size;
}
}
return vgmstream;
/* clean up anything we may have opened */
fail:
if (vgmstream) close_vgmstream(vgmstream);
return NULL;
close_vgmstream(vgmstream);
return NULL;
}

View File

@ -1,7 +1,7 @@
#include "meta.h"
#include "../layout/layout.h"
#include "../coding/coding.h"
#include "ea_eaac_eatrax_streamfile.h"
#include "ea_eaac_streamfile.h"
/* EAAudioCore formats, EA's current audio middleware */
@ -108,6 +108,48 @@ fail:
return NULL;
}
/* .SPS? - from Frostbite engine games? [Need for Speed Rivals (PS4)], v1 header */
VGMSTREAM * init_vgmstream_ea_sps_fb(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
off_t start_offset = 0, header_offset = 0, sps_offset, max_offset;
/* checks */
/* assumed to be .sps (no extensions in the archives) */
if (!check_extensions(streamFile,"sps"))
goto fail;
if (read_32bitBE(0x00,streamFile) != 0x011006C0)
goto fail;
/* file has some kind of data before .sps, exact offset unknown.
* Actual offsets are probably somewhere but for now just manually search. */
sps_offset = read_32bitBE(0x08, streamFile); /* points to some kind of table, number of entries unknown */
max_offset = sps_offset + 0x3000;
if (max_offset > get_streamfile_size(streamFile))
max_offset = get_streamfile_size(streamFile);
/* find .sps start block */
while (sps_offset < max_offset) {
if ((read_32bitBE(sps_offset, streamFile) & 0xFFFFFF00) == 0x48000000) {
header_offset = sps_offset + 0x04;
start_offset = sps_offset + (read_32bitBE(sps_offset, streamFile) & 0x00FFFFFF);
break;
}
sps_offset += 0x04;
}
if (!start_offset)
goto fail; /* not found */
vgmstream = init_vgmstream_eaaudiocore_header(streamFile, streamFile, header_offset, start_offset, meta_EA_SPS);
if (!vgmstream) goto fail;
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;
}
/* EA newest header from RwAudioCore (RenderWare?) / EAAudioCore library (still generated by sx.exe).
* Audio "assets" come in separate RAM headers (.SNR/SPH) and raw blocked streams (.SNS/SPS),
@ -120,16 +162,16 @@ static VGMSTREAM * init_vgmstream_eaaudiocore_header(STREAMFILE * streamHead, ST
uint32_t num_samples, loop_start = 0, loop_end = 0;
/* EA SNR/SPH header */
version = (read_8bit(header_offset + 0x00,streamHead) >> 4) & 0xf;
codec = (read_8bit(header_offset + 0x00,streamHead) >> 0) & 0xf;
channel_config = read_8bit(header_offset + 0x01,streamHead);
sample_rate = (uint16_t)read_16bitBE(header_offset + 0x02,streamHead);
version = (read_8bit(header_offset + 0x00,streamHead) >> 4) & 0x0F;
codec = (read_8bit(header_offset + 0x00,streamHead) >> 0) & 0x0F;
channel_config = read_8bit(header_offset + 0x01,streamHead) & 0xFE;
sample_rate = read_32bitBE(header_offset + 0x00,streamHead) & 0x1FFFF; /* some Dead Space 2 (PC) uses 96000 */
flags = (uint8_t)read_8bit(header_offset + 0x04,streamHead) & 0xFE; //todo upper nibble only? (the first bit is part of size)
num_samples = (uint32_t)read_32bitBE(header_offset + 0x04,streamHead) & 0x01FFFFFF;
/* rest is optional, depends on flags header used (ex. SNU and SPS may have bigger headers):
* &0x20: 1 int (usually 0x00), &0x00/40: nothing, &0x60: 2 ints (usually 0x00 and 0x14) */
/* V0: SNR+SNS, V1: SPR+SPS (not apparent differences) */
/* V0: SNR+SNS, V1: SPR+SPS (not apparent differences, other than the block flags used) */
if (version != 0 && version != 1) {
VGM_LOG("EA SNS/SPS: unknown version\n");
goto fail;
@ -219,14 +261,20 @@ static VGMSTREAM * init_vgmstream_eaaudiocore_header(STREAMFILE * streamHead, ST
case 0x06: /* "L32P": EALayer3 v2 "PCM" [Battlefield 1943 (PS3)] */
case 0x07: { /* "L32S": EALayer3 v2 "Spike" [Dante's Inferno (PS3)] */
mpeg_custom_config cfg = {0};
off_t mpeg_start_offset = start_offset + 0x08;
mpeg_custom_t type = (codec == 0x05 ? MPEG_EAL31b : (codec == 0x06) ? MPEG_EAL32P : MPEG_EAL32S);
/* layout is still blocks, but should work fine with the custom mpeg decoder */
vgmstream->codec_data = init_mpeg_custom(streamData, mpeg_start_offset, &vgmstream->coding_type, vgmstream->channels, type, &cfg);
if (!vgmstream->codec_data) goto fail;
/* remove blocks on reads for some edge cases in L32P and to properly apply discard modes
* (otherwise, and removing discards, it'd work with layout_blocked_ea_sns) */
temp_streamFile = setup_eaac_streamfile(streamData, version, codec, start_offset, 0);
if (!temp_streamFile) goto fail;
start_offset = 0x00; /* must point to the custom streamfile's beginning */
/* layout is still blocks, but should work fine with the custom mpeg decoder */
vgmstream->codec_data = init_mpeg_custom(temp_streamFile, start_offset, &vgmstream->coding_type, vgmstream->channels, type, &cfg);
if (!vgmstream->codec_data) goto fail;
vgmstream->layout_type = layout_none;
vgmstream->layout_type = layout_blocked_ea_sns;
break;
}
#endif
@ -253,10 +301,10 @@ static VGMSTREAM * init_vgmstream_eaaudiocore_header(STREAMFILE * streamHead, ST
vgmstream->layout_type = layout_none;
/* EATrax is "buffered" ATRAC9, uses custom IO since it's kind of complex to add to the decoder */
start_offset = 0x00; /* must point to header start */
temp_streamFile = setup_eatrax_streamfile(streamData, total_size);
temp_streamFile = setup_eaac_streamfile(streamData, version, codec, start_offset, total_size);
if (!temp_streamFile) goto fail;
start_offset = 0x00; /* must point to the custom streamfile's beginning */
break;
}
#endif
@ -278,11 +326,10 @@ static VGMSTREAM * init_vgmstream_eaaudiocore_header(STREAMFILE * streamHead, ST
if (!vgmstream_open_stream(vgmstream,temp_streamFile ? temp_streamFile : streamData,start_offset))
goto fail;
close_streamfile(temp_streamFile);
if (vgmstream->layout_type == layout_blocked_ea_sns)
block_update_ea_sns(start_offset, vgmstream);
close_streamfile(temp_streamFile);
return vgmstream;
fail:

View File

@ -1,127 +0,0 @@
#ifndef _EA_EAAC_EATRAX_STREAMFILE_H_
#define _EA_EAAC_EATRAX_STREAMFILE_H_
#include "../streamfile.h"
typedef struct {
/* state */
off_t logical_offset; /* offset that corresponds to physical_offset */
off_t physical_offset; /* actual file offset */
size_t total_size; /* size of the resulting substream */
} eatrax_io_data;
/* Reads skipping EA's block headers, so the resulting data is smaller than physical data,
* and physical_offset is bigger than offset (ex. reads at offset = 0x00 could be at physical_offset = 0x10).
* physical/logical_offset should always be at the start of a block and only advance when a block is fully done */
static size_t eatrax_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length, eatrax_io_data* data) {
size_t total_read = 0;
/* ignore bad reads */
if (offset < 0 || offset > data->total_size) {
return 0;
}
/* previous offset: re-start as we can't map logical<>physical offsets
* (kinda slow as it trashes buffers, but shouldn't happen often) */
if (offset < data->logical_offset) {
data->physical_offset = 0x00;
data->logical_offset = 0x00;
}
/* read doing one EA block at a time */
while (length > 0) {
size_t to_read, bytes_read;
off_t intrablock_offset, intradata_offset;
uint32_t block_size, block_flag, data_size;
block_flag = read_8bit(data->physical_offset+0x00,streamfile);
block_size = read_32bitBE(data->physical_offset+0x00,streamfile) & 0x00FFFFFF;
data_size = read_32bitBE(data->physical_offset+0x04,streamfile); /* typically block_size - 0x08 */
/* skip header block */
if (block_flag == 0x48) {
data->physical_offset += block_size;
continue;
}
/* stop on footer block */
if (block_flag == 0x45) {
data->physical_offset += block_size;
return total_read;
}
/* data block expected */
if (block_flag != 0x44) {
return total_read;
}
/* requested offset is outside current block, try next */
if (offset >= data->logical_offset + data_size) {
data->physical_offset += block_size;
data->logical_offset += data_size;
continue;
}
/* reads could fall in the middle of the block */
intradata_offset = offset - data->logical_offset;
intrablock_offset = 0x08 + intradata_offset;
/* clamp reads up to this block's end */
to_read = (data_size - intradata_offset);
if (to_read > length)
to_read = length;
if (to_read == 0)
return total_read; /* should never happen... */
/* finally read and move buffer/offsets */
bytes_read = read_streamfile(dest, data->physical_offset + intrablock_offset, to_read, streamfile);
total_read += bytes_read;
if (bytes_read != to_read)
return total_read; /* couldn't read fully */
dest += bytes_read;
offset += bytes_read;
length -= bytes_read;
/* block fully read, go next */
if (intradata_offset + bytes_read == data_size) {
data->physical_offset += block_size;
data->logical_offset += data_size;
}
}
return total_read;
}
static size_t eatrax_io_size(STREAMFILE *streamfile, eatrax_io_data* data) {
return data->total_size;
}
/* Prepares custom IO for EATrax, which is simply blocked ATRAC9 data, but blocks
* may end in the middle of an ATRAC9 frame, so reads remove their headers */
static STREAMFILE* setup_eatrax_streamfile(STREAMFILE *streamFile, size_t total_size) {
STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL;
eatrax_io_data io_data = {0};
size_t io_data_size = sizeof(eatrax_io_data);
io_data.total_size = total_size;
/* setup subfile */
new_streamFile = open_wrap_streamfile(streamFile);
if (!new_streamFile) goto fail;
temp_streamFile = new_streamFile;
new_streamFile = open_io_streamfile(temp_streamFile, &io_data,io_data_size, eatrax_io_read,eatrax_io_size);
if (!new_streamFile) goto fail;
temp_streamFile = new_streamFile;
return temp_streamFile;
fail:
close_streamfile(temp_streamFile);
return NULL;
}
#endif /* _EA_EAAC_STREAMFILE_H_ */

View File

@ -0,0 +1,218 @@
#ifndef _EA_EAAC_STREAMFILE_H_
#define _EA_EAAC_STREAMFILE_H_
#include "../streamfile.h"
typedef struct {
/* state */
off_t logical_offset; /* offset that corresponds to physical_offset */
off_t physical_offset; /* actual file offset */
/* config */
int version;
int codec;
off_t start_offset;
size_t total_size; /* size of the resulting substream */
} eaac_io_data;
/* Reads skipping EA's block headers, so the resulting data is smaller or larger than physical data.
* physical/logical_offset should always be at the start of a block and only advance when a block is fully done */
static size_t eaac_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length, eaac_io_data* data) {
size_t total_read = 0;
/* ignore bad reads */
if (offset < 0 || offset > data->total_size) {
return total_read;
}
/* previous offset: re-start as we can't map logical<>physical offsets
* (kinda slow as it trashes buffers, but shouldn't happen often) */
if (offset < data->logical_offset) {
data->physical_offset = data->start_offset;
data->logical_offset = 0x00;
}
/* read doing one EA block at a time */
while (length > 0) {
size_t to_read, bytes_read;
off_t intrablock_offset, intradata_offset;
uint32_t block_flag, block_size, data_size, skip_size;
block_flag = read_8bit(data->physical_offset+0x00,streamfile);
block_size = read_32bitBE(data->physical_offset+0x00,streamfile) & 0x00FFFFFF;
if (data->version == 1 && block_flag == 0x48) {
data->physical_offset += block_size;
continue; /* skip header block */
}
if (data->version == 1 && block_flag == 0x45)
return total_read; /* stop on last block (always empty) */
switch(data->codec) {
#if 0
case 0x03:
data_size = block_size - ???;
extra_size = (data_size % 0x800); /* deflated padding */
skip_size = 0x08 + 0x04*data->stream_count;
break;
#endif
case 0x05: /* EALayer3 v1 */
case 0x06: /* EALayer3 v2 "PCM" */
case 0x07: /* EALayer3 v2 "Spike" */
data_size = block_size - 0x08;
skip_size = 0x08;
break;
case 0x0a: /* EATrax */
data_size = read_32bitBE(data->physical_offset+0x04,streamfile); /* should be block_size - 0x08 */
skip_size = 0x08;
break;
default:
return total_read;
}
/* requested offset is outside current block, try next */
if (offset >= data->logical_offset + data_size) {
data->physical_offset += block_size;
data->logical_offset += data_size;
continue;
}
/* reads could fall in the middle of the block */
intradata_offset = offset - data->logical_offset;
intrablock_offset = skip_size + intradata_offset;
/* clamp reads up to this block's end */
to_read = (data_size - intradata_offset);
if (to_read > length)
to_read = length;
if (to_read == 0)
return total_read; /* should never happen... */
/* finally read and move buffer/offsets */
bytes_read = read_streamfile(dest, data->physical_offset + intrablock_offset, to_read, streamfile);
total_read += bytes_read;
if (bytes_read != to_read)
return total_read; /* couldn't read fully */
dest += bytes_read;
offset += bytes_read;
length -= bytes_read;
/* block fully read, go next */
if (intradata_offset + bytes_read == data_size) {
data->physical_offset += block_size;
data->logical_offset += data_size;
}
if (data->version == 0 && block_flag == 0x80)
break; /* stop on last block */
}
return total_read;
}
static size_t eaac_io_size(STREAMFILE *streamfile, eaac_io_data* data) {
off_t physical_offset, max_physical_offset;
size_t total_size = 0;
if (data->total_size)
return data->total_size;
physical_offset = data->start_offset;
max_physical_offset = get_streamfile_size(streamfile) - data->start_offset;
/* get size of the underlying, non-blocked data */
while (physical_offset < max_physical_offset) {
uint32_t block_flag, block_size, data_size;
block_flag = read_8bit(physical_offset+0x00,streamfile);
block_size = read_32bitBE(physical_offset+0x00,streamfile) & 0x00FFFFFF;
if (data->version == 0 && block_flag != 0x00 && block_flag != 0x80)
break; /* data/end block expected */
if (data->version == 1 && block_flag == 0x48) {
physical_offset += block_size;
continue; /* skip header block */
}
if (data->version == 1 && block_flag == 0x45)
break; /* stop on last block (always empty) */
if (data->version == 1 && block_flag != 0x44)
break; /* data block expected */
switch(data->codec) {
#if 0
case 0x03:
data_size = block_size - ???;
data_size += (data_size % 0x800); /* deflated padding */
break;
#endif
case 0x05: /* EALayer3 v1 */
case 0x06: /* EALayer3 v2 "PCM" */
case 0x07: /* EALayer3 v2 "Spike" */
data_size = block_size - 0x08;
break;
case 0x0a: /* EATrax */
data_size = read_32bitBE(physical_offset+0x04,streamfile); /* should be block_size - 0x08 */
break;
default:
return 0;
}
physical_offset += block_size;
total_size += data_size;
if (data->version == 0 && block_flag == 0x80)
break; /* stop on last block */
}
data->total_size = total_size;
return data->total_size;
}
/* Prepares custom IO for some blocked EAAudioCore formats, that need clean reads without block headers:
* - EA-XMA: deflated XMA in multistreams (separate 2ch frames)
* - EALayer3: MPEG granule 1 can go in the next block (in V2"P" mainly, others could use layout blocked_sns)
* - EATrax: ATRAC9 frames can be split between blooks
*/
static STREAMFILE* setup_eaac_streamfile(STREAMFILE *streamFile, int version, int codec, off_t start_offset, size_t total_size) {
STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL;
eaac_io_data io_data = {0};
size_t io_data_size = sizeof(eaac_io_data);
io_data.version = version;
io_data.codec = codec;
io_data.start_offset = start_offset;
io_data.total_size = total_size; /* optional */
io_data.physical_offset = start_offset;
/* setup subfile */
new_streamFile = open_wrap_streamfile(streamFile);
if (!new_streamFile) goto fail;
temp_streamFile = new_streamFile;
new_streamFile = open_io_streamfile(temp_streamFile, &io_data,io_data_size, eaac_io_read,eaac_io_size);
if (!new_streamFile) goto fail;
temp_streamFile = new_streamFile;
new_streamFile = open_buffer_streamfile(new_streamFile,0);
if (!new_streamFile) goto fail;
temp_streamFile = new_streamFile;
return temp_streamFile;
fail:
close_streamfile(temp_streamFile);
return NULL;
}
#endif /* _EA_EAAC_STREAMFILE_H_ */

View File

@ -56,7 +56,7 @@ VGMSTREAM * init_vgmstream_fsb_encrypted(STREAMFILE * streamFile) {
for (i = 0; i < fsbkey_list_count; i++) {
fsbkey_info entry = fsbkey_list[i];
;VGM_LOG("fsbkey: size=%i, is_fsb5=%i, is_alt=%i\n", entry.fsbkey_size,entry.is_fsb5, entry.is_alt);
//;VGM_LOG("fsbkey: size=%i, is_fsb5=%i, is_alt=%i\n", entry.fsbkey_size,entry.is_fsb5, entry.is_alt);
temp_streamFile = setup_fsb_streamfile(streamFile, entry.fsbkey, entry.fsbkey_size, entry.is_alt);
if (!temp_streamFile) goto fail;

View File

@ -66,6 +66,9 @@ static const uint8_t key_gh3[] = { 0x35,0x61,0x74,0x75,0x36,0x77,0x34,0x7A,0x61,
/* Supreme Commander 2 */ //"B2A7BB00"
static const uint8_t key_sc2[] = { 0x42,0x32,0x41,0x37,0x42,0x42,0x30,0x30 };
/* Cookie Run: Ovenbreak */ //"ghfxhslrghfxhslr"
static const uint8_t key_cro[] = { 0x67,0x68,0x66,0x78,0x68,0x73,0x6C,0x72,0x67,0x68,0x66,0x78,0x68,0x73,0x6C,0x72 };
// Unknown:
// - Battle: Los Angeles
// - Guitar Hero: Warriors of Rock, DJ hero FSB
@ -125,6 +128,7 @@ static const fsbkey_info fsbkey_list[] = {
{ 0,1, sizeof(key_sc2),key_sc2 },//untested
{ 1,0, sizeof(key_sc2),key_sc2 },//untested
{ 1,1, sizeof(key_sc2),key_sc2 },//untested
{ 1,0, sizeof(key_cro),key_cro },
};
static const int fsbkey_list_count = sizeof(fsbkey_list) / sizeof(fsbkey_list[0]);

View File

@ -228,6 +228,9 @@ static const hcakey_info hcakey_list[] = {
// Dx2 Shin Megami Tensei Liberation (iOS/Android)
{118714477}, // 000000000713706D
// Oira (Cygames) [iOS/Android]
{46460622}, // 0000000002C4EECE
};
#endif/*_HCA_KEYS_H_*/

View File

@ -1,91 +1,60 @@
#include "meta.h"
#include "../util.h"
/*
ISH+ISD
*/
#include "../coding/coding.h"
/* ISH+ISD - from various games [Chaos Field (GC), Pokemon XD - Gale of Darkness (GC)] */
VGMSTREAM * init_vgmstream_ish_isd(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
STREAMFILE * streamFileISH = NULL;
char filename[PATH_LIMIT];
char filenameISH[PATH_LIMIT];
int i;
int channel_count;
int loop_flag;
STREAMFILE * streamHeader = NULL;
off_t start_offset;
int channel_count, loop_flag;
/* check extension, case insensitive */
streamFile->get_name(streamFile,filename,sizeof(filename));
if (strcasecmp("isd",filename_extension(filename))) goto fail;
strcpy(filenameISH,filename);
strcpy(filenameISH+strlen(filenameISH)-3,"ish");
streamFileISH = streamFile->open(streamFile,filenameISH,STREAMFILE_DEFAULT_BUFFER_SIZE);
if (!streamFileISH) goto fail;
/* check header */
if (read_32bitBE(0x00,streamFileISH) != 0x495F5346) /* "I_SF" */
/* checks */
if (!check_extensions(streamFile, "isd"))
goto fail;
channel_count = read_32bitBE(0x14,streamFileISH);
loop_flag = (read_32bitBE(0x1C,streamFileISH) !=0);
streamHeader = open_streamfile_by_ext(streamFile,"ish");
if (!streamHeader) goto fail;
if (read_32bitBE(0x00,streamHeader) != 0x495F5346) /* "I_SF" */
goto fail;
channel_count = read_32bitBE(0x14,streamHeader);
loop_flag = (read_32bitBE(0x1C,streamHeader) != 0);
start_offset = 0;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail;
/* fill in the vital statistics */
vgmstream->channels = channel_count;
vgmstream->sample_rate = read_32bitBE(0x08,streamFileISH);
vgmstream->num_samples=read_32bitBE(0x0C,streamFileISH);
vgmstream->coding_type = coding_NGC_DSP;
if(loop_flag) {
vgmstream->loop_start_sample = read_32bitBE(0x20,streamFileISH)*14/8/channel_count;
vgmstream->loop_end_sample = read_32bitBE(0x24,streamFileISH)*14/8/channel_count;
}
if (channel_count == 1) {
vgmstream->layout_type = layout_none;
} else if (channel_count == 2) {
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = read_32bitBE(0x18,streamFileISH);
vgmstream->sample_rate = read_32bitBE(0x08,streamHeader);
vgmstream->num_samples = read_32bitBE(0x0C,streamHeader);
if (loop_flag) {
vgmstream->loop_start_sample = read_32bitBE(0x20,streamHeader)*14 / 0x08 /channel_count;
vgmstream->loop_end_sample = read_32bitBE(0x24,streamHeader)*14 / 0x08 / channel_count;
}
vgmstream->meta_type = meta_ISH_ISD;
/* open the file for reading by each channel */
{
for (i=0;i<channel_count;i++) {
vgmstream->ch[i].streamfile = streamFile->open(streamFile,filename,vgmstream->interleave_block_size);
if (!vgmstream->ch[i].streamfile) goto fail;
vgmstream->ch[i].channel_start_offset=
vgmstream->ch[i].offset=i*vgmstream->interleave_block_size;
}
vgmstream->coding_type = coding_NGC_DSP;
if (channel_count == 1) {
vgmstream->layout_type = layout_none;
} else {
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = read_32bitBE(0x18,streamHeader);
}
dsp_read_coefs_be(vgmstream,streamHeader,0x40,0x40);
if (vgmstream->coding_type == coding_NGC_DSP) {
int i;
for (i=0;i<16;i++) {
vgmstream->ch[0].adpcm_coef[i] = read_16bitBE(0x40+i*2,streamFileISH);
}
if (vgmstream->channels == 2) {
for (i=0;i<16;i++) {
vgmstream->ch[1].adpcm_coef[i] = read_16bitBE(0x80+i*2,streamFileISH);
}
}
}
close_streamfile(streamFileISH); streamFileISH=NULL;
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
goto fail;
close_streamfile(streamHeader);
return vgmstream;
/* clean up anything we may have opened */
fail:
if (streamFileISH) close_streamfile(streamFileISH);
if (vgmstream) close_vgmstream(vgmstream);
close_streamfile(streamHeader);
close_vgmstream(vgmstream);
return NULL;
}

View File

@ -669,7 +669,10 @@ VGMSTREAM * init_vgmstream_stm(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_awc(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_nsw_opus(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_opus_std(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_opus_n1(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_opus_capcom(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_opus_nop(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_pc_al2(STREAMFILE * streamFile);
@ -738,4 +741,10 @@ VGMSTREAM * init_vgmstream_txtp(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_smc_smh(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_ea_sps_fb(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_ppst(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_opus_ppp(STREAMFILE *streamFile);
#endif /*_META_H*/

View File

@ -72,12 +72,11 @@ VGMSTREAM * init_vgmstream_msb_msh(STREAMFILE *streamFile) {
vgmstream->interleave_block_size = 0x10;
close_streamfile(streamHeader);
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
goto fail;
return vgmstream;
close_streamfile(streamHeader);
return vgmstream;
fail:
close_streamfile(streamHeader);

View File

@ -1,67 +1,44 @@
#include "meta.h"
#include "../util.h"
#include "../coding/coding.h"
/* ADPCM (from NAOMI/NAOMI2 Arcade games) */
/* ADPCM - from NAOMI/NAOMI2 Arcade games [F355 Challenge (Naomi)] */
VGMSTREAM * init_vgmstream_naomi_adpcm(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
char filename[PATH_LIMIT];
off_t start_offset;
int loop_flag = 0;
int channel_count;
int loop_flag, channel_count;
size_t data_size;
/* check extension, case insensitive */
streamFile->get_name(streamFile,filename,sizeof(filename));
if (strcasecmp("adpcm",filename_extension(filename))) goto fail;
#if 0
/* check header */
if ((read_32bitBE(0x00,streamFile) != 0x41445043) || /* "ADPC" */
(read_32bitBE(0x04,streamFile) != 0x41445043)) /* "M_v0" */
goto fail;
#endif
/* checks */
if (!check_extensions(streamFile, "adpcm"))
goto fail;
if (read_32bitBE(0x00,streamFile) != 0x41445043 || /* "ADPC" */
read_32bitBE(0x04,streamFile) != 0x4D5F7630) /* "M_v0" */
goto fail;
/* there is some kind of info in the end padding, loop related? */
loop_flag = 0;
channel_count = 2;
start_offset = 0x40;
data_size = read_32bitLE(0x10,streamFile) * 0x100; /* data has padding */
/* build the VGMSTREAM */
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail;
/* fill in the vital statistics */
start_offset = 0x40;
vgmstream->channels = channel_count;
vgmstream->sample_rate = 44100;
vgmstream->coding_type = coding_AICA_int;
vgmstream->num_samples = (get_streamfile_size(streamFile)-start_offset);
if (loop_flag) {
vgmstream->loop_start_sample = 0;
vgmstream->loop_end_sample = (get_streamfile_size(streamFile)-start_offset);
}
vgmstream->num_samples = aica_bytes_to_samples(data_size, channel_count);
vgmstream->coding_type = coding_AICA_int;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = read_32bitLE(0x10,streamFile) * 0x80;
vgmstream->interleave_block_size = data_size / channel_count;
vgmstream->meta_type = meta_NAOMI_ADPCM;
/* open the file for reading */
{
int i;
STREAMFILE * file;
file = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE);
if (!file) goto fail;
for (i=0;i<channel_count;i++) {
vgmstream->ch[i].streamfile = file;
vgmstream->ch[i].channel_start_offset=
vgmstream->ch[i].offset=start_offset+
vgmstream->interleave_block_size*i;
vgmstream->ch[i].adpcm_step_index = 0x7f; /* AICA */
}
}
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
goto fail;
return vgmstream;
/* clean up anything we may have opened */
fail:
if (vgmstream) close_vgmstream(vgmstream);
close_vgmstream(vgmstream);
return NULL;
}

View File

@ -8,7 +8,8 @@ VGMSTREAM * init_vgmstream_nds_hwas(STREAMFILE *streamFile) {
off_t start_offset;
int channel_count, loop_flag = 0;
/* check extension, case insensitive (made-up extension) */
/* checks */
/* .hwas: usually in archives but also found named (ex. Guitar Hero On Tour) */
if (!check_extensions(streamFile,"hwas"))
goto fail;

File diff suppressed because it is too large Load Diff

View File

@ -25,7 +25,6 @@ VGMSTREAM * init_vgmstream_ngc_pdt(STREAMFILE *streamFile) {
read_32bitBE(0x0c,streamFile) != 0x04)
goto fail;
//VGM_LOG("1\n");
entries = read_16bitBE(0x02,streamFile);
entries_offset = read_32bitBE(0x10,streamFile);
coefs_offset = read_32bitBE(0x14,streamFile);
@ -74,7 +73,6 @@ VGMSTREAM * init_vgmstream_ngc_pdt(STREAMFILE *streamFile) {
}
}
//VGM_LOG("header: %lx\n", header_offset);//todo
/* parse header */
{
uint8_t flags;

View File

@ -1,124 +0,0 @@
#include "meta.h"
#include "../util.h"
#include "../coding/coding.h"
/* .OPUS - from Switch games (Lego City Undercover, Ultra SF II, Disgaea 5) */
VGMSTREAM * init_vgmstream_nsw_opus(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
off_t start_offset;
int loop_flag = 0, channel_count;
int num_samples = 0, loop_start = 0, loop_end = 0;
off_t offset = 0, data_offset;
size_t data_size, skip = 0;
/* check extension, case insensitive */
if ( !check_extensions(streamFile,"opus,lopus,nop")) /* no relation to Ogg Opus */
goto fail;
/* variations, maybe custom */
if (read_32bitBE(0x00,streamFile) == 0x01000080) { /* standard, Lego City Undercover */
offset = 0x00;
}
else if ((read_32bitBE(0x04,streamFile) == 0x00000000 && read_32bitBE(0x0c,streamFile) == 0x00000000) ||
(read_32bitBE(0x04,streamFile) == 0xFFFFFFFF && read_32bitBE(0x0c,streamFile) == 0xFFFFFFFF)) { /* Disgaea 5 */
offset = 0x10;
loop_start = read_32bitLE(0x00,streamFile);
loop_end = read_32bitLE(0x08,streamFile);
}
else if (read_32bitLE(0x04,streamFile) == 0x01 ||
read_32bitLE(0x04,streamFile) == 0x02) { /* Ultra Street Fighter II, RE: Revelations */
offset = read_32bitLE(0x1c,streamFile);
num_samples = read_32bitLE(0x00,streamFile);
/* 0x04: channels, 6 uses interleaved streams (2ch+2ch+2ch) */
loop_start = read_32bitLE(0x08,streamFile);
loop_end = read_32bitLE(0x0c,streamFile);
/* 0x10: frame size (with extra data) */
/* 0x14: extra chunk count (each of 0x08, after 0x30) */
/* 0x18: null */
}
else if (read_32bitBE(0x00, streamFile) == 0x73616466 && read_32bitBE(0x08, streamFile) == 0x6f707573) { /* Xenoblade Chronicles 2 */
offset = read_32bitLE(0x1c, streamFile);
num_samples = read_32bitLE(0x28, streamFile);
loop_flag = read_8bit(0x19, streamFile);
if (loop_flag) {
loop_start = read_32bitLE(0x2c, streamFile);
loop_end = read_32bitLE(0x30, streamFile);
}
}
else {
offset = 0x00;
}
if ((uint32_t)read_32bitLE(offset + 0x00,streamFile) != 0x80000001)
goto fail;
channel_count = read_8bit(offset + 0x09, streamFile);
/* 0x0a: packet size if CBR, 0 if VBR */
data_offset = offset + read_32bitLE(offset + 0x10, streamFile);
skip = read_32bitLE(offset + 0x1c, streamFile);
if ((uint32_t)read_32bitLE(data_offset, streamFile) != 0x80000004)
goto fail;
data_size = read_32bitLE(data_offset + 0x04, streamFile);
start_offset = data_offset + 0x08;
loop_flag = (loop_end > 0); /* -1 when not set */
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail;
vgmstream->sample_rate = read_32bitLE(offset + 0x0c,streamFile);
vgmstream->meta_type = meta_NSW_OPUS;
vgmstream->num_samples = num_samples;
vgmstream->loop_start_sample = loop_start;
vgmstream->loop_end_sample = loop_end;
#ifdef VGM_USE_FFMPEG
{
uint8_t buf[0x100];
size_t bytes;
ffmpeg_custom_config cfg;
ffmpeg_codec_data *ffmpeg_data;
bytes = ffmpeg_make_opus_header(buf,0x100, vgmstream->channels, skip, vgmstream->sample_rate);
if (bytes <= 0) goto fail;
memset(&cfg, 0, sizeof(ffmpeg_custom_config));
cfg.type = FFMPEG_SWITCH_OPUS;
ffmpeg_data = init_ffmpeg_config(streamFile, buf,bytes, start_offset,data_size, &cfg);
if (!ffmpeg_data) goto fail;
vgmstream->codec_data = ffmpeg_data;
vgmstream->coding_type = coding_FFmpeg;
vgmstream->layout_type = layout_none;
if (ffmpeg_data->skipSamples <= 0) {
ffmpeg_set_skip_samples(ffmpeg_data, skip);
}
if (vgmstream->num_samples == 0) {
vgmstream->num_samples = switch_opus_get_samples(start_offset, data_size,
vgmstream->sample_rate, streamFile) - skip;
}
}
#else
goto fail;
#endif
/* open the file for reading */
if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) )
goto fail;
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;
}

View File

@ -4,296 +4,88 @@
#include <string.h>
#include <ctype.h>
#ifdef WIN32
#define DIRSEP '\\'
#else
#define DIRSEP '/'
#endif
/* NWA - Visual Art's streams */
static int get_loops_nwainfo_ini(STREAMFILE *streamFile, int *out_loop_flag, int32_t *out_loop_start);
static int get_loops_gameexe_ini(STREAMFILE *streamFile, int *out_loop_flag, int32_t *out_loop_start, int32_t *out_loop_end);
static nwa_codec_data *open_nwa_vgmstream(STREAMFILE *streamFile);
static void free_nwa_vgmstream(nwa_codec_data *data);
/* NWA - Visual Art's streams [Air (PC), Clannad (PC)] */
VGMSTREAM * init_vgmstream_nwa(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
char filename[PATH_LIMIT];
int i;
int channel_count;
int loop_flag = 0;
int32_t loop_start_sample = 0;
int32_t loop_end_sample = 0;
int nwainfo_ini_found = 0;
int gameexe_ini_found = 0;
int just_pcm = 0;
int comp_level = -2;
nwa_codec_data *data = NULL;
off_t start_offset;
int channel_count, loop_flag = 0;
int32_t loop_start_sample = 0, loop_end_sample = 0;
int nwainfo_ini_found = 0, gameexe_ini_found = 0;
int compression_level;
/* check extension, case insensitive */
streamFile->get_name(streamFile,filename,sizeof(filename));
if (strcasecmp("nwa",filename_extension(filename))) goto fail;
/* checks */
if (!check_extensions(streamFile, "nwa"))
goto fail;
channel_count = read_16bitLE(0x00,streamFile);
if (channel_count != 1 && channel_count != 2) goto fail;
/* check if we're using raw pcm */
if (
read_32bitLE(0x08,streamFile)==-1 || /* compression level */
read_32bitLE(0x10,streamFile)==0 || /* block count */
read_32bitLE(0x18,streamFile)==0 || /* compressed data size */
read_32bitLE(0x20,streamFile)==0 || /* block size */
read_32bitLE(0x24,streamFile)==0 /* restsize */
)
{
just_pcm = 1;
}
else
{
comp_level = read_32bitLE(0x08,streamFile);
data = malloc(sizeof(nwa_codec_data));
if (!data) goto fail;
data->nwa = open_nwa(streamFile,filename);
if (!data->nwa) goto fail;
if ( read_32bitLE(0x08,streamFile)==-1 || /* compression level */
read_32bitLE(0x10,streamFile)==0 || /* block count */
read_32bitLE(0x18,streamFile)==0 || /* compressed data size */
read_32bitLE(0x20,streamFile)==0 || /* block size */
read_32bitLE(0x24,streamFile)==0 ) { /* restsize */
compression_level = -1;
} else {
compression_level = read_32bitLE(0x08,streamFile);
}
/* try to locate NWAINFO.INI in the same directory */
{
char ininame[PATH_LIMIT];
char * ini_lastslash;
char namebase_array[PATH_LIMIT];
char *namebase;
STREAMFILE *inistreamfile;
/* loop points come from external files */
nwainfo_ini_found = get_loops_nwainfo_ini(streamFile, &loop_flag, &loop_start_sample);
gameexe_ini_found = !nwainfo_ini_found && get_loops_gameexe_ini(streamFile, &loop_flag, &loop_start_sample, &loop_end_sample);
/* here we assume that the "special encoding" does not affect
* the directory separator */
strncpy(ininame,filename,sizeof(ininame));
ininame[sizeof(ininame)-1]='\0'; /* a pox on the stdlib! */
streamFile->get_realname(streamFile,namebase_array,sizeof(namebase_array));
ini_lastslash = strrchr(ininame,DIRSEP);
if (!ini_lastslash) {
strncpy(ininame,"NWAINFO.INI",sizeof(ininame));
namebase = namebase_array;
} else {
strncpy(ini_lastslash+1,"NWAINFO.INI",
sizeof(ininame)-(ini_lastslash+1-ininame));
namebase = strrchr(namebase_array,DIRSEP)+1;
}
ininame[sizeof(ininame)-1]='\0'; /* curse you, strncpy! */
inistreamfile = streamFile->open(streamFile,ininame,4096);
if (inistreamfile) {
/* ini found, try to find our name */
const char * ext;
int length;
int found;
off_t offset;
off_t file_size;
off_t found_off = -1;
nwainfo_ini_found = 1;
ext = filename_extension(namebase);
length = ext-1-namebase;
file_size = get_streamfile_size(inistreamfile);
for (found = 0, offset = 0; !found && offset<file_size; offset++) {
off_t suboffset;
/* Go for an n*m search 'cause it's easier than building an
* FSA for the search string. Just wanted to make the point that
* I'm not ignorant, just lazy. */
for (suboffset = offset;
suboffset<file_size &&
suboffset-offset<length &&
read_8bit(suboffset,inistreamfile)==
namebase[suboffset-offset];
suboffset++) {}
if (suboffset-offset==length &&
read_8bit(suboffset,inistreamfile)==0x09) { /* tab */
found=1;
found_off = suboffset+1;
}
}
if (found) {
char loopstring[9]={0};
if (read_streamfile((uint8_t*)loopstring,found_off,8,
inistreamfile)==8)
{
loop_start_sample = atol(loopstring);
if (loop_start_sample > 0) loop_flag = 1;
}
} /* if found file name in INI */
close_streamfile(inistreamfile);
} /* if opened INI ok */
} /* INI block */
/* try to locate Gameexe.ini in the same directory */
{
char ininame[PATH_LIMIT];
char * ini_lastslash;
char namebase_array[PATH_LIMIT];
char * namebase;
STREAMFILE *inistreamfile;
strncpy(ininame,filename,sizeof(ininame));
ininame[sizeof(ininame)-1]='\0'; /* a pox on the stdlib! */
streamFile->get_realname(streamFile,namebase_array,sizeof(namebase_array));
ini_lastslash = strrchr(ininame,DIRSEP);
if (!ini_lastslash) {
strncpy(ininame,"Gameexe.ini",sizeof(ininame));
namebase = namebase_array;
} else {
strncpy(ini_lastslash+1,"Gameexe.ini",
sizeof(ininame)-(ini_lastslash+1-ininame));
namebase = strrchr(namebase_array,DIRSEP)+1;
}
ininame[sizeof(ininame)-1]='\0'; /* curse you, strncpy! */
inistreamfile = streamFile->open(streamFile,ininame,4096);
if (inistreamfile) {
/* ini found, try to find our name */
const char * ext;
int length;
int found;
off_t offset;
off_t file_size;
off_t found_off = -1;
gameexe_ini_found = 1;
ext = filename_extension(namebase);
length = ext-1-namebase;
file_size = get_streamfile_size(inistreamfile);
/* format of line is:
* #DSTRACK = 00000000 - eeeeeeee - ssssssss = "name" = "name2?"
* ^22 ^33 ^45 ^57
*/
for (found = 0, offset = 0; !found && offset<file_size; offset++) {
off_t suboffset;
uint8_t buf[10];
if (read_8bit(offset,inistreamfile)!='#') continue;
if (read_streamfile(buf,offset+1,10,inistreamfile)!=10) break;
if (memcmp("DSTRACK = ",buf,10)) continue;
if (read_8bit(offset+44,inistreamfile)!='\"') continue;
for (suboffset = offset+45;
suboffset<file_size &&
suboffset-offset-45<length &&
tolower(read_8bit(suboffset,inistreamfile))==
tolower(namebase[suboffset-offset-45]);
suboffset++) {}
if (suboffset-offset-45==length &&
read_8bit(suboffset,inistreamfile)=='\"') { /* tab */
found=1;
found_off = offset+22; /* loop end */
}
}
if (found) {
char loopstring[9]={0};
int start_ok = 0, end_ok = 0;
int32_t total_samples =
read_32bitLE(0x1c,streamFile)/channel_count;
if (read_streamfile((uint8_t*)loopstring,found_off,8,
inistreamfile)==8)
{
if (!memcmp("99999999",loopstring,8))
{
loop_end_sample = total_samples;
}
else
{
loop_end_sample = atol(loopstring);
}
end_ok = 1;
}
if (read_streamfile((uint8_t*)loopstring,found_off+11,8,
inistreamfile)==8)
{
if (!memcmp("99999999",loopstring,8))
{
/* not ok to start at last sample,
* don't set start_ok flag */
}
else if (!memcmp("00000000",loopstring,8))
{
/* loops from the start aren't really loops */
}
else
{
loop_start_sample = atol(loopstring);
start_ok = 1;
}
}
if (start_ok && end_ok) loop_flag = 1;
} /* if found file name in INI */
close_streamfile(inistreamfile);
} /* if opened INI ok */
} /* INI block */
start_offset = 0x2c;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail;
/* fill in the vital statistics */
vgmstream->channels = channel_count;
vgmstream->sample_rate = read_32bitLE(0x04,streamFile);
vgmstream->num_samples = read_32bitLE(0x1c,streamFile) / channel_count;
vgmstream->num_samples = read_32bitLE(0x1c,streamFile)/channel_count;
if (just_pcm) {
switch (read_16bitLE(0x02,streamFile)) {
case 8:
vgmstream->coding_type = coding_PCM8;
vgmstream->interleave_block_size = 1;
break;
case 16:
vgmstream->coding_type = coding_PCM16LE;
vgmstream->interleave_block_size = 2;
break;
default:
goto fail;
}
if (channel_count > 1) {
switch(compression_level) {
case -1:
switch (read_16bitLE(0x02,streamFile)) {
case 8:
vgmstream->coding_type = coding_PCM8;
vgmstream->interleave_block_size = 0x01;
break;
case 16:
vgmstream->coding_type = coding_PCM16LE;
vgmstream->interleave_block_size = 0x02;
break;
default:
goto fail;
}
vgmstream->layout_type = layout_interleave;
} else {
break;
case 0:
case 1:
case 2:
case 3:
case 4:
case 5:
vgmstream->coding_type = coding_NWA;
vgmstream->layout_type = layout_none;
}
}
else
{
switch (comp_level)
{
case 0:
case 1:
case 2:
case 3:
case 4:
case 5:
vgmstream->coding_type = coding_NWA;
break;
default:
goto fail;
break;
}
vgmstream->layout_type = layout_none;
vgmstream->codec_data = open_nwa_vgmstream(streamFile);
if (!vgmstream->codec_data) goto fail;
break;
default:
goto fail;
break;
}
if (nwainfo_ini_found) {
vgmstream->meta_type = meta_NWA_NWAINFOINI;
if (loop_flag) {
@ -311,38 +103,205 @@ VGMSTREAM * init_vgmstream_nwa(STREAMFILE *streamFile) {
}
if (just_pcm) {
/* open the file for reading by each channel */
STREAMFILE *chstreamfile;
/* have both channels use the same buffer, as interleave is so small */
chstreamfile = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE);
if (!chstreamfile) goto fail;
for (i=0;i<channel_count;i++) {
vgmstream->ch[i].streamfile = chstreamfile;
vgmstream->ch[i].channel_start_offset=
vgmstream->ch[i].offset=0x2c+(off_t)(i*vgmstream->interleave_block_size);
}
}
else
{
vgmstream->codec_data = data;
}
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
goto fail;
return vgmstream;
/* clean up anything we may have opened */
fail:
if (vgmstream) close_vgmstream(vgmstream);
if (data) {
if (data->nwa)
close_vgmstream(vgmstream);
return NULL;
}
/* try to locate NWAINFO.INI in the same directory */
static int get_loops_nwainfo_ini(STREAMFILE *streamFile, int *out_loop_flag, int32_t *out_loop_start) {
STREAMFILE *streamLoops;
char namebase[PATH_LIMIT];
const char * ext;
int length;
int found;
off_t offset;
size_t file_size;
off_t found_off = -1;
int loop_flag = 0;
int32_t loop_start_sample = 0;
streamLoops = open_streamfile_by_filename(streamFile, "NWAINFO.INI");
if (!streamLoops) goto fail;
get_streamfile_filename(streamFile,namebase,PATH_LIMIT);
/* ini found, try to find our name */
ext = filename_extension(namebase);
length = ext - 1 - namebase;
file_size = get_streamfile_size(streamLoops);
for (found = 0, offset = 0; !found && offset < file_size; offset++) {
off_t suboffset;
/* Go for an n*m search 'cause it's easier than building an
* FSA for the search string. Just wanted to make the point that
* I'm not ignorant, just lazy. */
for (suboffset = offset;
suboffset < file_size &&
suboffset-offset < length &&
read_8bit(suboffset,streamLoops) == namebase[suboffset-offset];
suboffset++) {
/* skip */
}
if (suboffset-offset==length && read_8bit(suboffset,streamLoops)==0x09) { /* tab */
found = 1;
found_off = suboffset+1;
}
}
/* if found file name in INI */
if (found) {
char loopstring[9] = {0};
if (read_streamfile((uint8_t*)loopstring,found_off,8,streamLoops) == 8) {
loop_start_sample = atol(loopstring);
if (loop_start_sample > 0)
loop_flag = 1;
}
}
*out_loop_flag = loop_flag;
*out_loop_start = loop_start_sample;
close_streamfile(streamLoops);
return 1;
fail:
close_streamfile(streamLoops);
return 0;
}
/* try to locate Gameexe.ini in the same directory */
static int get_loops_gameexe_ini(STREAMFILE *streamFile, int *out_loop_flag, int32_t *out_loop_start, int32_t *out_loop_end) {
STREAMFILE *streamLoops;
char namebase[PATH_LIMIT];
const char * ext;
int length;
int found;
off_t offset;
off_t file_size;
off_t found_off = -1;
int loop_flag = 0;
int32_t loop_start_sample = 0, loop_end_sample = 0;
streamLoops = open_streamfile_by_filename(streamFile, "Gameexe.ini");
if (!streamLoops) goto fail;
get_streamfile_filename(streamFile,namebase,PATH_LIMIT);
/* ini found, try to find our name */
ext = filename_extension(namebase);
length = ext-1-namebase;
file_size = get_streamfile_size(streamLoops);
/* format of line is:
* #DSTRACK = 00000000 - eeeeeeee - ssssssss = "name" = "name2?"
* ^22 ^33 ^45 ^57
*/
for (found = 0, offset = 0; !found && offset<file_size; offset++) {
off_t suboffset;
uint8_t buf[10];
if (read_8bit(offset,streamLoops)!='#') continue;
if (read_streamfile(buf,offset+1,10,streamLoops)!=10) break;
if (memcmp("DSTRACK = ",buf,10)) continue;
if (read_8bit(offset+44,streamLoops)!='\"') continue;
for (suboffset = offset+45;
suboffset < file_size &&
suboffset-offset-45 < length &&
tolower(read_8bit(suboffset,streamLoops)) == tolower(namebase[suboffset-offset-45]);
suboffset++) {
/* skip */
}
if (suboffset-offset-45==length && read_8bit(suboffset,streamLoops)=='\"') { /* tab */
found = 1;
found_off = offset+22; /* loop end */
}
}
if (found) {
char loopstring[9] = {0};
int start_ok = 0, end_ok = 0;
int32_t total_samples = read_32bitLE(0x1c,streamFile) / read_16bitLE(0x00,streamFile);
if (read_streamfile((uint8_t*)loopstring,found_off,8,streamLoops)==8)
{
if (!memcmp("99999999",loopstring,8)) {
loop_end_sample = total_samples;
}
else {
loop_end_sample = atol(loopstring);
}
end_ok = 1;
}
if (read_streamfile((uint8_t*)loopstring,found_off+11,8,streamLoops)==8)
{
if (!memcmp("99999999",loopstring,8)) {
/* not ok to start at last sample,
* don't set start_ok flag */
}
else if (!memcmp("00000000",loopstring,8)) {
/* loops from the start aren't really loops */
}
else {
loop_start_sample = atol(loopstring);
start_ok = 1;
}
}
if (start_ok && end_ok) loop_flag = 1;
} /* if found file name in INI */
*out_loop_flag = loop_flag;
*out_loop_start = loop_start_sample;
*out_loop_end = loop_end_sample;
close_streamfile(streamLoops);
return 1;
fail:
close_streamfile(streamLoops);
return 0;
}
static nwa_codec_data *open_nwa_vgmstream(STREAMFILE *streamFile) {
nwa_codec_data *data = NULL;
char filename[PATH_LIMIT];
streamFile->get_name(streamFile,filename,sizeof(filename));
data = malloc(sizeof(nwa_codec_data));
if (!data) goto fail;
data->nwa = open_nwa(streamFile,filename);
if (!data->nwa) goto fail;
return data;
fail:
free_nwa_vgmstream(data);
return NULL;
}
static void free_nwa_vgmstream(nwa_codec_data *data) {
if (data) {
if (data->nwa) {
close_nwa(data->nwa);
}
free(data);
}
return NULL;
}

View File

@ -111,8 +111,7 @@ static void psychic_ogg_decryption_callback(void *ptr, size_t size, size_t nmemb
int i;
/* add 0x23 ('#') */
{
for (i = 0; i < bytes_read; i++)
for (i = 0; i < bytes_read; i++) {
((uint8_t*)ptr)[i] += 0x23;
}
}
@ -126,17 +125,14 @@ static void sngw_ogg_decryption_callback(void *ptr, size_t size, size_t nmemb, v
put_32bitBE(key, ov_streamfile->xor_value);
/* bytes are xor'd with key and nibble-swapped */
{
for (i = 0; i < bytes_read; i++) {
if (ov_streamfile->offset+i < 0x04) {
/* replace key in the first 4 bytes with "OggS" */
((uint8_t*)ptr)[i] = (uint8_t)header_id[(ov_streamfile->offset + i) % 4];
}
else {
uint8_t val = ((uint8_t*)ptr)[i] ^ key[(ov_streamfile->offset + i) % 4];
((uint8_t*)ptr)[i] = ((val << 4) & 0xf0) | ((val >> 4) & 0x0f);
}
/* bytes are xor'd with key and nibble-swapped, first "OggS" is changed */
for (i = 0; i < bytes_read; i++) {
if (ov_streamfile->offset+i < 0x04) {
((uint8_t*)ptr)[i] = (uint8_t)header_id[(ov_streamfile->offset + i) % 4];
}
else {
uint8_t val = ((uint8_t*)ptr)[i] ^ key[(ov_streamfile->offset + i) % 4];
((uint8_t*)ptr)[i] = ((val << 4) & 0xf0) | ((val >> 4) & 0x0f);
}
}
}
@ -150,9 +146,8 @@ static void isd_ogg_decryption_callback(void *ptr, size_t size, size_t nmemb, vo
int i;
/* bytes are xor'd with key */
{
for (i = 0; i < bytes_read; i++)
((uint8_t*)ptr)[i] ^= key[(ov_streamfile->offset + i) % 16];
for (i = 0; i < bytes_read; i++) {
((uint8_t*)ptr)[i] ^= key[(ov_streamfile->offset + i) % 16];
}
}
@ -162,16 +157,14 @@ static void l2sd_ogg_decryption_callback(void *ptr, size_t size, size_t nmemb, v
int i;
char *header_id = "OggS";
/* First "OggS" is changed */
{
for (i = 0; i < bytes_read; i++) {
if (ov_streamfile->offset+i < 0x04) {
/* replace key in the first 4 bytes with "OggS" */
((uint8_t*)ptr)[i] = (uint8_t)header_id[(ov_streamfile->offset + i) % 4];
}
else {
break;
}
/* first "OggS" is changed */
for (i = 0; i < bytes_read; i++) {
if (ov_streamfile->offset+i < 0x04) {
/* replace key in the first 4 bytes with "OggS" */
((uint8_t*)ptr)[i] = (uint8_t)header_id[(ov_streamfile->offset + i) % 4];
}
else {
break;
}
}
}
@ -210,6 +203,17 @@ static void eno_ogg_decryption_callback(void *ptr, size_t size, size_t nmemb, vo
}
}
static void ys8_ogg_decryption_callback(void *ptr, size_t size, size_t nmemb, void *datasource) {
size_t bytes_read = size*nmemb;
ogg_vorbis_streamfile * const ov_streamfile = datasource;
int i;
/* bytes are xor'd with key and nibble-swapped */
for (i = 0; i < bytes_read; i++) {
uint8_t val = ((uint8_t*)ptr)[i] ^ ov_streamfile->xor_value;
((uint8_t*)ptr)[i] = ((val << 4) & 0xf0) | ((val >> 4) & 0x0f);
}
}
/* Ogg Vorbis, by way of libvorbisfile; may contain loop comments */
VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) {
@ -225,6 +229,7 @@ VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) {
int is_l2sd = 0;
int is_rpgmvo = 0;
int is_eno = 0;
int is_ys8 = 0;
/* check extension */
@ -252,9 +257,7 @@ VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) {
/* check standard Ogg Vorbis */
if (is_ogg) {
/* check Psychic Software obfuscation (Darkwind: War on Wheels PC) */
if (read_32bitBE(0x00,streamFile) == 0x2c444430) {
if (read_32bitBE(0x00,streamFile) == 0x2c444430) { /* Psychic Software obfuscation [Darkwind: War on Wheels (PC)] */
is_psychic = 1;
ovmi.decryption_callback = psychic_ogg_decryption_callback;
}
@ -262,8 +265,12 @@ VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) {
is_l2sd = 1;
ovmi.decryption_callback = l2sd_ogg_decryption_callback;
}
else if (read_32bitBE(0x00,streamFile) == 0x048686C5) { /* XOR'ed + bitswapped "OggS" [Ys VIII (PC)] */
is_ys8 = 1;
ovmi.decryption_callback = ys8_ogg_decryption_callback;
}
else if (read_32bitBE(0x00,streamFile) != 0x4f676753) { /* "OggS" */
goto fail; /* not known (ex. Wwise) */
goto fail; /* unknown/not ogg (ex. Wwise) */
}
}
@ -326,6 +333,11 @@ VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) {
start_offset = 0x01;
}
/* check Ys VIII (PC) */
if (is_ys8) {
ovmi.xor_value = 0xF0;
ovmi.decryption_callback = ys8_ogg_decryption_callback;
}
if (is_um3) {
ovmi.meta_type = meta_OGG_UM3;
@ -343,6 +355,8 @@ VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) {
ovmi.meta_type = meta_OGG_RPGMV;
} else if (is_eno) {
ovmi.meta_type = meta_OGG_ENO;
} else if (is_ys8) {
ovmi.meta_type = meta_OGG_YS8;
} else {
ovmi.meta_type = meta_OGG_VORBIS;
}
@ -500,6 +514,8 @@ VGMSTREAM * init_vgmstream_ogg_vorbis_callbacks(STREAMFILE *streamFile, ov_callb
loop_length_found = 1;
}
}
//;VGM_LOG("OGG: user_comment=%s\n", user_comment);
}
}

235
src/meta/opus.c Normal file
View File

@ -0,0 +1,235 @@
#include "meta.h"
#include "../util.h"
#include "../coding/coding.h"
#include "../layout/layout.h"
#include "opus_interleave_streamfile.h"
/* Nintendo OPUS - from Switch games, including header variations (not the same as Ogg Opus) */
static VGMSTREAM * init_vgmstream_opus(STREAMFILE *streamFile, meta_t meta_type, off_t offset, int32_t num_samples, int32_t loop_start, int32_t loop_end) {
VGMSTREAM * vgmstream = NULL;
off_t start_offset;
int loop_flag = 0, channel_count;
off_t data_offset;
size_t data_size, skip = 0;
if ((uint32_t)read_32bitLE(offset + 0x00,streamFile) != 0x80000001)
goto fail;
channel_count = read_8bit(offset + 0x09, streamFile);
/* 0x0a: packet size if CBR, 0 if VBR */
data_offset = offset + read_32bitLE(offset + 0x10, streamFile);
skip = read_32bitLE(offset + 0x1c, streamFile);
if ((uint32_t)read_32bitLE(data_offset, streamFile) != 0x80000004)
goto fail;
data_size = read_32bitLE(data_offset + 0x04, streamFile);
start_offset = data_offset + 0x08;
loop_flag = (loop_end > 0); /* -1 when not set */
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail;
vgmstream->sample_rate = read_32bitLE(offset + 0x0c,streamFile);
vgmstream->meta_type = meta_OPUS;
vgmstream->num_samples = num_samples;
vgmstream->loop_start_sample = loop_start;
vgmstream->loop_end_sample = loop_end;
#ifdef VGM_USE_FFMPEG
{
uint8_t buf[0x100];
size_t bytes;
ffmpeg_custom_config cfg;
ffmpeg_codec_data *ffmpeg_data;
bytes = ffmpeg_make_opus_header(buf,0x100, vgmstream->channels, skip, vgmstream->sample_rate);
if (bytes <= 0) goto fail;
memset(&cfg, 0, sizeof(ffmpeg_custom_config));
cfg.type = FFMPEG_SWITCH_OPUS;
ffmpeg_data = init_ffmpeg_config(streamFile, buf,bytes, start_offset,data_size, &cfg);
if (!ffmpeg_data) goto fail;
vgmstream->codec_data = ffmpeg_data;
vgmstream->coding_type = coding_FFmpeg;
vgmstream->layout_type = layout_none;
if (ffmpeg_data->skipSamples <= 0) {
ffmpeg_set_skip_samples(ffmpeg_data, skip);
}
if (vgmstream->num_samples == 0) {
vgmstream->num_samples = switch_opus_get_samples(start_offset, data_size,
vgmstream->sample_rate, streamFile) - skip;
}
}
#else
goto fail;
#endif
/* open the file for reading */
if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) )
goto fail;
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;
}
/* standard Switch Opus, Nintendo header + raw data (generated by opus_test.c?) [Lego City Undercover (Switch)] */
VGMSTREAM * init_vgmstream_opus_std(STREAMFILE *streamFile) {
off_t offset = 0;
int num_samples = 0, loop_start = 0, loop_end = 0;
/* checks */
if (!check_extensions(streamFile,"opus,lopus"))
goto fail;
offset = 0x00;
num_samples = 0;
loop_start = 0;
loop_end = 0;
return init_vgmstream_opus(streamFile, meta_OPUS, offset, num_samples,loop_start,loop_end);
fail:
return NULL;
}
/* Nippon1 variation [Disgaea 5 (Switch)] */
VGMSTREAM * init_vgmstream_opus_n1(STREAMFILE *streamFile) {
off_t offset = 0;
int num_samples = 0, loop_start = 0, loop_end = 0;
/* checks */
if ( !check_extensions(streamFile,"opus,lopus"))
goto fail;
if (!((read_32bitBE(0x04,streamFile) == 0x00000000 && read_32bitBE(0x0c,streamFile) == 0x00000000) ||
(read_32bitBE(0x04,streamFile) == 0xFFFFFFFF && read_32bitBE(0x0c,streamFile) == 0xFFFFFFFF)))
goto fail;
offset = 0x10;
num_samples = 0;
loop_start = read_32bitLE(0x00,streamFile);
loop_end = read_32bitLE(0x08,streamFile);
return init_vgmstream_opus(streamFile, meta_OPUS, offset, num_samples,loop_start,loop_end);
fail:
return NULL;
}
/* Capcom variation [Ultra Street Fighter II (Switch), Resident Evil: Revelations (Switch)] */
VGMSTREAM * init_vgmstream_opus_capcom(STREAMFILE *streamFile) {
VGMSTREAM *vgmstream = NULL;
off_t offset = 0;
int num_samples = 0, loop_start = 0, loop_end = 0;
int channel_count;
/* checks */
if ( !check_extensions(streamFile,"opus,lopus"))
goto fail;
channel_count = read_32bitLE(0x04,streamFile);
if (channel_count != 1 && channel_count != 2 && channel_count != 6)
goto fail; /* unknown stream layout */
num_samples = read_32bitLE(0x00,streamFile);
/* 0x04: channels, >2 uses interleaved streams (2ch+2ch+2ch) */
loop_start = read_32bitLE(0x08,streamFile);
loop_end = read_32bitLE(0x0c,streamFile);
/* 0x10: frame size (with extra data) */
/* 0x14: extra chunk count */
/* 0x18: null */
offset = read_32bitLE(0x1c,streamFile);
/* 0x20-8: config? (0x0077C102 04000000 E107070C) */
/* 0x2c: some size? */
/* 0x30+: extra chunks (0x00: 0x7f, 0x04: num_sample), alt loop starts/regions? */
if (channel_count == 6) {
/* 2ch multistream hacky-hacks, don't try this at home. We'll end up with:
* main vgmstream > N vgmstream layers > substream IO deinterleaver > opus meta > Opus IO transmogrifier (phew) */
//todo deinterleave has some problems with reading after total_size
layered_layout_data* data = NULL;
int layers = channel_count / 2;
int i;
int loop_flag = (loop_end > 0);
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail;
vgmstream->layout_type = layout_layered;
/* init layout */
data = init_layout_layered(layers);
if (!data) goto fail;
vgmstream->layout_data = data;
/* open each layer subfile */
for (i = 0; i < layers; i++) {
STREAMFILE* temp_streamFile = setup_opus_interleave_streamfile(streamFile, offset+0x28*i, layers);
if (!temp_streamFile) goto fail;
data->layers[i] = init_vgmstream_opus(temp_streamFile, meta_OPUS, 0x00, num_samples,loop_start,loop_end);
close_streamfile(temp_streamFile);
if (!data->layers[i]) goto fail;
}
/* setup layered VGMSTREAMs */
if (!setup_layout_layered(data))
goto fail;
vgmstream->sample_rate = data->layers[0]->sample_rate;
vgmstream->num_samples = data->layers[0]->num_samples;
vgmstream->loop_start_sample = data->layers[0]->loop_start_sample;
vgmstream->loop_end_sample = data->layers[0]->loop_end_sample;
vgmstream->meta_type = meta_OPUS;
vgmstream->coding_type = data->layers[0]->coding_type;
return vgmstream;
}
else {
return init_vgmstream_opus(streamFile, meta_OPUS, offset, num_samples,loop_start,loop_end);
}
fail:
close_vgmstream(vgmstream);
return NULL;
}
/* Procyon Studio variation [Xenoblade Chronicles 2 (Switch)] */
VGMSTREAM * init_vgmstream_opus_nop(STREAMFILE *streamFile) {
off_t offset = 0;
int num_samples = 0, loop_start = 0, loop_end = 0, loop_flag;
/* checks */
if (!check_extensions(streamFile,"nop"))
goto fail;
if (read_32bitBE(0x00, streamFile) != 0x73616466 || /* "sadf" */
read_32bitBE(0x08, streamFile) != 0x6f707573) /* "opus" */
goto fail;
offset = read_32bitLE(0x1c, streamFile);
num_samples = read_32bitLE(0x28, streamFile);
loop_flag = read_8bit(0x19, streamFile);
if (loop_flag) {
loop_start = read_32bitLE(0x2c, streamFile);
loop_end = read_32bitLE(0x30, streamFile);
}
return init_vgmstream_opus(streamFile, meta_OPUS, offset, num_samples,loop_start,loop_end);
fail:
return NULL;
}

View File

@ -0,0 +1,146 @@
#ifndef _OPUS_CAPCOM_STREAMFILE_H_
#define _OPUS_CAPCOM_STREAMFILE_H_
#include "../streamfile.h"
typedef struct {
/* state */
off_t logical_offset; /* offset that corresponds to physical_offset */
off_t physical_offset; /* actual file offset */
int skip_frames; /* frames to skip from other streams at points */
/* config */
int version;
int streams;
off_t start_offset; /* pointing to the stream's beginning */
size_t total_size; /* size of the resulting substream */
} opus_interleave_io_data;
/* Reads skipping other streams */
static size_t opus_interleave_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length, opus_interleave_io_data* data) {
size_t total_read = 0;
/* ignore bad reads */
if (offset < 0 || offset > data->total_size) {
return total_read;
}
/* previous offset: re-start as we can't map logical<>physical offsets, since it may be VBR
* (kinda slow as it trashes buffers, but shouldn't happen often) */
if (offset < data->logical_offset) {
data->physical_offset = data->start_offset;
data->logical_offset = 0x00;
}
/* read doing one frame at a time */
while (length > 0) {
size_t to_read, bytes_read;
off_t intrablock_offset, intradata_offset;
uint32_t data_size;
data_size = read_32bitBE(data->physical_offset+0x00,streamfile);
//if (offset >= data->total_size) //todo fix
// return total_read;
/* Nintendo Opus header rather than a frame */
if ((uint32_t)data_size == 0x01000080) {
data_size = read_32bitLE(data->physical_offset+0x10,streamfile);
data_size += 0x08;
}
else {
data_size += 0x08;
}
/* skip frames from other streams */
if (data->skip_frames) {
data->physical_offset += data_size;
data->skip_frames--;
continue;
}
/* requested offset is outside current block, try next */
if (offset >= data->logical_offset + data_size) {
data->physical_offset += data_size;
data->logical_offset += data_size;
data->skip_frames = data->streams - 1;
continue;
}
/* reads could fall in the middle of the block */
intradata_offset = offset - data->logical_offset;
intrablock_offset = intradata_offset;
/* clamp reads up to this block's end */
to_read = (data_size - intradata_offset);
if (to_read > length)
to_read = length;
if (to_read == 0)
return total_read; /* should never happen... */
/* finally read and move buffer/offsets */
bytes_read = read_streamfile(dest, data->physical_offset + intrablock_offset, to_read, streamfile);
total_read += bytes_read;
if (bytes_read != to_read)
return total_read; /* couldn't read fully */
dest += bytes_read;
offset += bytes_read;
length -= bytes_read;
/* block fully read, go next */
if (intradata_offset + bytes_read == data_size) {
data->physical_offset += data_size;
data->logical_offset += data_size;
data->skip_frames = data->streams - 1;
}
}
return total_read;
}
static size_t opus_interleave_io_size(STREAMFILE *streamfile, opus_interleave_io_data* data) {
off_t info_offset;
if (data->total_size)
return data->total_size;
info_offset = read_32bitLE(data->start_offset+0x10,streamfile);
return read_32bitLE(data->start_offset + info_offset+0x04,streamfile);
}
/* Prepares custom IO for multistream Opus, interleaves 1 packet per stream */
static STREAMFILE* setup_opus_interleave_streamfile(STREAMFILE *streamFile, off_t start_offset, int streams) {
STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL;
opus_interleave_io_data io_data = {0};
size_t io_data_size = sizeof(opus_interleave_io_data);
io_data.start_offset = start_offset;
io_data.streams = streams;
io_data.physical_offset = start_offset;
io_data.total_size = opus_interleave_io_size(streamFile, &io_data); /* force init */
/* setup subfile */
new_streamFile = open_wrap_streamfile(streamFile);
if (!new_streamFile) goto fail;
temp_streamFile = new_streamFile;
new_streamFile = open_io_streamfile(temp_streamFile, &io_data,io_data_size, opus_interleave_io_read,opus_interleave_io_size);
if (!new_streamFile) goto fail;
temp_streamFile = new_streamFile;
new_streamFile = open_buffer_streamfile(new_streamFile,0);
if (!new_streamFile) goto fail;
temp_streamFile = new_streamFile;
return temp_streamFile;
fail:
close_streamfile(temp_streamFile);
return NULL;
}
#endif /* _OPUS_CAPCOM_STREAMFILE_H_ */

120
src/meta/opus_ppp.c Normal file
View File

@ -0,0 +1,120 @@
#include "meta.h"
#include "../coding/coding.h"
#include "../layout/layout.h"
static STREAMFILE* setup_opus_ppp_streamfile(STREAMFILE *streamFile, off_t subfile_offset, size_t subfile_size, const char* fake_ext);
/* .AT9 Opus - from Penny-Punching Princess (Switch) */
VGMSTREAM * init_vgmstream_opus_ppp(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
off_t segment_offset;
int loop_flag, channel_count;
int i;
segmented_layout_data *data = NULL;
int segment_count, loop_start_segment = 0, loop_end_segment = 0;
int num_samples = 0, loop_start_sample = 0, loop_end_sample = 0;
/* checks */
if (!check_extensions(streamFile, "at9"))
goto fail;
if (read_32bitBE(0x00,streamFile) != 0x09000000) /* file type? DSPs had 08 */
goto fail;
if (read_32bitLE(0x04,streamFile) + 0x1c != get_streamfile_size(streamFile))
goto fail;
/* 0x08(2): sample rate, 0x0a(2): loop flag?, 0x0c: num_samples (slightly smaller than added samples) */
segment_count = 3; /* intro/loop/end */
loop_start_segment = 1;
loop_end_segment = 1;
loop_flag = (segment_count > 0);
/* init layout */
data = init_layout_segmented(segment_count);
if (!data) goto fail;
/* open each segment subfile */
segment_offset = 0x1c;
for (i = 0; i < segment_count; i++) {
STREAMFILE* temp_streamFile;
size_t segment_size = read_32bitLE(0x10+0x04*i,streamFile);
if (!segment_size)
goto fail;
temp_streamFile = setup_opus_ppp_streamfile(streamFile, segment_offset,segment_size, "opus");
if (!temp_streamFile) goto fail;
data->segments[i] = init_vgmstream_opus_std(temp_streamFile);
close_streamfile(temp_streamFile);
if (!data->segments[i]) goto fail;
segment_offset += segment_size;
//todo there are some trailing samples that must be removed for smooth loops, start skip seems ok
data->segments[i]->num_samples -= 374; //not correct for all files, no idea how to calculate
/* get looping and samples */
if (loop_flag && loop_start_segment == i)
loop_start_sample = num_samples;
num_samples += data->segments[i]->num_samples;
if (loop_flag && loop_end_segment == i)
loop_end_sample = num_samples;
}
/* setup segmented VGMSTREAMs */
if (!setup_layout_segmented(data))
goto fail;
channel_count = data->segments[0]->channels;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail;
vgmstream->sample_rate = (uint16_t)read_16bitLE(0x08,streamFile);
vgmstream->num_samples = num_samples;
vgmstream->loop_start_sample = loop_start_sample;
vgmstream->loop_end_sample = loop_end_sample;
vgmstream->meta_type = meta_OPUS_PPP;
vgmstream->coding_type = data->segments[0]->coding_type;
vgmstream->layout_type = layout_segmented;
vgmstream->layout_data = data;
data->loop_segment = loop_start_segment;
return vgmstream;
fail:
close_vgmstream(vgmstream);
free_layout_segmented(data);
return NULL;
}
static STREAMFILE* setup_opus_ppp_streamfile(STREAMFILE *streamFile, off_t subfile_offset, size_t subfile_size, const char* fake_ext) {
STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL;
/* setup subfile */
new_streamFile = open_wrap_streamfile(streamFile);
if (!new_streamFile) goto fail;
temp_streamFile = new_streamFile;
new_streamFile = open_clamp_streamfile(temp_streamFile, subfile_offset,subfile_size);
if (!new_streamFile) goto fail;
temp_streamFile = new_streamFile;
new_streamFile = open_fakename_streamfile(temp_streamFile, NULL,fake_ext);
if (!new_streamFile) goto fail;
temp_streamFile = new_streamFile;
return temp_streamFile;
fail:
close_streamfile(temp_streamFile);
return NULL;
}

View File

@ -119,7 +119,7 @@ VGMSTREAM * init_vgmstream_p3d(STREAMFILE *streamFile) {
break;
default:
VGM_LOG("unknown codec 0x%04x\n", codec);
VGM_LOG("P3D: unknown codec 0x%04x\n", codec);
goto fail;
}
@ -175,7 +175,7 @@ VGMSTREAM * init_vgmstream_p3d(STREAMFILE *streamFile) {
#endif
default:
VGM_LOG("unknown codec 0x%04x\n", codec);
VGM_LOG("P3D: unknown codec 0x%04x\n", codec);
goto fail;
}

60
src/meta/ppst.c Normal file
View File

@ -0,0 +1,60 @@
#include "meta.h"
#include "../coding/coding.h"
#include "ppst_streamfile.h"
/* PPST - ParaPpa STream (maybe), extracted from .img bigfile [Parappa the Rapper (PSP)] */
VGMSTREAM * init_vgmstream_ppst(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
int total_subsongs, target_subsong = streamFile->stream_index;
/* checks */
if (!check_extensions(streamFile, "sng"))
goto fail;
if (read_32bitBE(0x00,streamFile) != 0x50505354) /* "PPST" */
goto fail;
/* header has some control and comment fields then interleaved RIFF .at3 */
/* count subsongs (mainly 4, rarely 1) */
{
off_t offset = 0xa0;
total_subsongs = 0;
while (offset < 0x800) {
if (read_32bitLE(offset + 0x04, streamFile) == 0) /* subsong size */
break;
total_subsongs++;
offset += 0x08;
}
if (target_subsong == 0) target_subsong = 1;
if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail;
}
{
off_t start_offset = 0x800;
size_t interleave_size = 0x4000;
size_t stride_size = 0x4000*total_subsongs;
/* subsong header at 0xa0, 0x00(1): id, 0x01(3): blocks of interleave */
size_t stream_size = read_32bitLE(0xA0+0x08*(target_subsong-1)+0x04, streamFile);
STREAMFILE* temp_streamFile = setup_ppst_streamfile(streamFile, start_offset+interleave_size*(target_subsong-1), interleave_size, stride_size, stream_size);
if (!temp_streamFile) goto fail;
vgmstream = init_vgmstream_riff(temp_streamFile);
close_streamfile(temp_streamFile);
if (!vgmstream) goto fail;
vgmstream->num_streams = total_subsongs;
vgmstream->stream_size = stream_size;
vgmstream->meta_type = meta_PPST;
}
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;
}

101
src/meta/ppst_streamfile.h Normal file
View File

@ -0,0 +1,101 @@
#ifndef _PPST_STREAMFILE_H_
#define _PPST_STREAMFILE_H_
#include "../streamfile.h"
typedef struct {
off_t start_physical_offset; /* interleaved data start, for this substream */
size_t interleave_block_size; /* max size that can be read before encountering other substreams */
size_t stride_size; /* step size between interleave blocks (interleave*channels) */
size_t stream_size; /* final size of the deinterleaved substream */
} ppst_io_data;
/* Handles deinterleaving of complete files, skipping portions or other substreams. */
static size_t ppst_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length, ppst_io_data* data) {
size_t total_read = 0;
while (length > 0) {
size_t to_read;
size_t length_available;
off_t block_num;
off_t intrablock_offset;
off_t physical_offset;
if (offset >= data->stream_size)
return total_read;
block_num = offset / data->interleave_block_size;
intrablock_offset = offset % data->interleave_block_size;
physical_offset = data->start_physical_offset + block_num*data->stride_size + intrablock_offset;
length_available = data->interleave_block_size - intrablock_offset;
if (length_available > data->stream_size - offset)
length_available = data->stream_size - offset;
if (length < length_available) {
to_read = length;
}
else {
to_read = length_available;
}
if (to_read > 0) {
size_t bytes_read;
bytes_read = read_streamfile(dest, physical_offset, to_read, streamfile);
total_read += bytes_read;
if (bytes_read != to_read) {
return total_read;
}
dest += bytes_read;
offset += bytes_read;
length -= bytes_read;
}
}
return total_read;
}
static size_t ppst_io_size(STREAMFILE *streamfile, ppst_io_data* data) {
return data->stream_size;
}
static STREAMFILE* setup_ppst_streamfile(STREAMFILE *streamFile, off_t start_offset, size_t interleave_block_size, size_t stride_size, size_t stream_size) {
STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL;
ppst_io_data io_data = {0};
size_t io_data_size = sizeof(ppst_io_data);
io_data.start_physical_offset = start_offset;
io_data.interleave_block_size = interleave_block_size;
io_data.stride_size = stride_size;
io_data.stream_size = stream_size;
/* setup subfile */
new_streamFile = open_wrap_streamfile(streamFile);
if (!new_streamFile) goto fail;
temp_streamFile = new_streamFile;
new_streamFile = open_io_streamfile(temp_streamFile, &io_data,io_data_size, ppst_io_read,ppst_io_size);
if (!new_streamFile) goto fail;
temp_streamFile = new_streamFile;
new_streamFile = open_buffer_streamfile(new_streamFile,0);
if (!new_streamFile) goto fail;
temp_streamFile = new_streamFile;
new_streamFile = open_fakename_streamfile(temp_streamFile, NULL,"at3");
if (!new_streamFile) goto fail;
temp_streamFile = new_streamFile;
return temp_streamFile;
fail:
close_streamfile(temp_streamFile);
return NULL;
}
#endif /* _SCD_STREAMFILE_H_ */

View File

@ -70,7 +70,6 @@ static size_t jstm_decryption_read(STREAMFILE *streamfile, uint8_t *dest, off_t
/* decrypt data (xor) */
for (i = 0; i < bytes_read; i++) {
if (offset+i >= data->start_offset) {
//VGM_LOG("xor %x to %x\n", dest[i], dest[i] ^ 0x5A);getchar();
dest[i] = dest[i] ^ 0x5A;
}
}

View File

@ -1,47 +1,46 @@
#include "meta.h"
#include "../util.h"
#include "../coding/coding.h"
/* MSA (from Psyvariar -Complete Edition-) */
/* MSA - from Psyvariar -Complete Edition- (PS2) */
VGMSTREAM * init_vgmstream_ps2_msa(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
off_t start_offset, datasize, filesize;
off_t start_offset, data_size, file_size;
int loop_flag, channel_count;
/* check extension, case insensitive */
if (!check_extensions(streamFile, "msa")) goto fail;
/* check header */
/* checks */
if (!check_extensions(streamFile, "msa"))
goto fail;
if (read_32bitBE(0x00,streamFile) != 0x00000000)
goto fail;
loop_flag = 0;
channel_count = 2;
start_offset = 0x14;
data_size = read_32bitLE(0x4,streamFile);
file_size = get_streamfile_size(streamFile);
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail;
start_offset = 0x14;
datasize = read_32bitLE(0x4,streamFile);
filesize = get_streamfile_size(streamFile);
vgmstream->channels = channel_count;
vgmstream->sample_rate = read_32bitLE(0x10,streamFile);
vgmstream->num_samples = datasize*28/(16*channel_count);
if (vgmstream->sample_rate == 0) /* ex. AME.MSA */
vgmstream->sample_rate = 44100;
vgmstream->num_samples = ps_bytes_to_samples(data_size, channel_count);//data_size*28/(0x10*channel_count);
vgmstream->coding_type = coding_PSX;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = 0x4000;
vgmstream->meta_type = meta_PS2_MSA;
/* MSAs are strangely truncated, so manually calculate samples
* data after last usable block is always silence or garbage */
if (datasize > filesize) {
off_t usable_size = filesize - start_offset;
/* MSAs are strangely truncated, so manually calculate samples.
* Data after last usable block is always silence or garbage. */
if (data_size > file_size) {
off_t usable_size = file_size - start_offset;
usable_size -= usable_size % (vgmstream->interleave_block_size*channel_count);/* block-aligned */
vgmstream->num_samples = usable_size * 28 / (16*channel_count);
vgmstream->num_samples = ps_bytes_to_samples(usable_size, channel_count);//usable_size * 28 / (16*channel_count);
}
/* open the file for reading */
if (!vgmstream_open_stream(vgmstream, streamFile, start_offset))
goto fail;
return vgmstream;

View File

@ -33,7 +33,6 @@ VGMSTREAM * init_vgmstream_smc_smh(STREAMFILE *streamFile) {
sample_rate = read_32bitLE(header_offset+0x08, streamHeader);
/* 0x0c(2): always 0x10, frame size? */
channel_count = read_16bitLE(header_offset+0x0e, streamHeader);
loop_flag = 0;
@ -53,10 +52,10 @@ VGMSTREAM * init_vgmstream_smc_smh(STREAMFILE *streamFile) {
vgmstream->interleave_block_size = read_32bitLE(0x04, streamHeader);
close_streamfile(streamHeader);
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
goto fail;
close_streamfile(streamHeader);
return vgmstream;
fail:

View File

@ -44,9 +44,6 @@ VGMSTREAM * init_vgmstream_sps_n1(STREAMFILE *streamFile) {
goto fail;
}
//VGM_LOG(vgmstream->num_samples != num_samples,
// "SPS: sps num_samples and subfile num_samples don't match\n");
//vgmstream->num_samples = num_samples; //todo adjusted for MAIATRAC3
vgmstream->sample_rate = sample_rate; /* .vag header doesn't match */
close_streamfile(temp_streamFile);

View File

@ -176,7 +176,7 @@ static int add_filename(txtp_header * txtp, char *filename) {
channel_mask |= (1 << (ch-1));
config += n;
if (config[0]== ',')
if (config[0]== ',' || config[0]== '-') /* "-" for PowerShell, may have problems with "," */
config++;
else if (config[0] != '\0')
break;

View File

@ -6,12 +6,6 @@
#include "vgmstream.h"
/* On EOF reads we can return length 0, or ignore and return the requested length + 0-set the buffer.
* Some decoders don't check for EOF and may decode garbage if returned 0, as read_Nbit() funcs return -1.
* Only matters for metas that get num_samples wrong (bigger than total data). */
#define STREAMFILE_IGNORE_EOF 0
/* a STREAMFILE that operates via standard IO using a buffer */
typedef struct {
STREAMFILE sf; /* callbacks */
@ -27,20 +21,26 @@ typedef struct {
static STREAMFILE * open_stdio_streamfile_buffer(const char * const filename, size_t buffersize);
static STREAMFILE * open_stdio_streamfile_buffer_by_file(FILE *infile,const char * const filename, size_t buffersize);
static size_t read_the_rest(uint8_t * dest, off_t offset, size_t length, STDIOSTREAMFILE * streamfile) {
size_t length_read_total=0;
static size_t read_stdio(STDIOSTREAMFILE *streamfile,uint8_t * dest, off_t offset, size_t length) {
size_t length_read_total = 0;
if (!streamfile || !dest || length <= 0 || offset < 0)
return 0;
/* is the part of the requested length in the buffer? */
if (offset >= streamfile->offset && offset < streamfile->offset + streamfile->validsize) {
size_t length_read;
size_t length_to_read;
off_t offset_into_buffer = offset - streamfile->offset;
length_read = streamfile->validsize - offset_into_buffer;
memcpy(dest,streamfile->buffer + offset_into_buffer,length_read);
length_read_total += length_read;
length -= length_read;
offset += length_read;
dest += length_read;
length_to_read = streamfile->validsize - offset_into_buffer;
if (length_to_read > length)
length_to_read = length;
memcpy(dest,streamfile->buffer + offset_into_buffer,length_to_read);
length_read_total += length_to_read;
length -= length_to_read;
offset += length_to_read;
dest += length_to_read;
}
/* What would make more sense here is to read the whole request
@ -49,25 +49,18 @@ static size_t read_the_rest(uint8_t * dest, off_t offset, size_t length, STDIOST
* The destination buffer is supposed to be much smaller than the
* STREAMFILE buffer, though. Maybe we should only ever return up
* to the buffer size to avoid having to deal with things like this
* which are outside of my intended use.
*/
* which are outside of my intended use. */
/* read the rest of the requested length */
while (length > 0) {
size_t length_to_read;
size_t length_read;
size_t length_to_read, length_read;
streamfile->validsize = 0; /* buffer is empty now */
/* request outside file: ignore to avoid seek/read */
if (offset > streamfile->filesize) {
streamfile->offset = streamfile->filesize;
VGM_LOG_ONCE("ERROR: reading over filesize 0x%x @ 0x%lx + 0x%x (buggy meta?)\n", streamfile->filesize, offset, length);
#if STREAMFILE_IGNORE_EOF
memset(dest,0,length); /* dest is already shifted */
return length_read_total + length; /* partially-read + 0-set buffer */
#else
return length_read_total; /* partially-read buffer */
#endif
}
/* position to new offset */
@ -90,13 +83,7 @@ static size_t read_the_rest(uint8_t * dest, off_t offset, size_t length, STDIOST
/* if we can't get enough to satisfy the request (EOF) we give up */
if (length_read < length_to_read) {
memcpy(dest,streamfile->buffer,length_read);
#if STREAMFILE_IGNORE_EOF
memset(dest+length_read,0,length-length_read);
return length_read_total + length; /* partially-read + 0-set buffer */
#else
return length_read_total + length_read; /* partially-read buffer */
#endif
}
/* use the new buffer */
@ -110,38 +97,6 @@ static size_t read_the_rest(uint8_t * dest, off_t offset, size_t length, STDIOST
return length_read_total;
}
static size_t read_stdio(STDIOSTREAMFILE *streamfile,uint8_t * dest, off_t offset, size_t length) {
if (!streamfile || !dest || length<=0)
return 0;
/* request outside file: ignore to avoid seek/read in read_the_rest() */
if (offset > streamfile->filesize) {
streamfile->offset = streamfile->filesize;
VGM_LOG_ONCE("ERROR: offset over filesize 0x%x @ 0x%lx + 0x%x (buggy meta?)\n", streamfile->filesize, offset, length);
#if STREAMFILE_IGNORE_EOF
memset(dest,0,length);
return length; /* 0-set buffer */
#else
return 0; /* nothing to read */
#endif
}
/* just copy if entire request is within the buffer */
if (offset >= streamfile->offset && offset + length <= streamfile->offset + streamfile->validsize) {
off_t offset_into_buffer = offset - streamfile->offset;
memcpy(dest,streamfile->buffer + offset_into_buffer,length);
return length;
}
/* request outside buffer: new fread */
{
size_t length_read = read_the_rest(dest,offset,length,streamfile);
return length_read;
}
}
static void close_stdio(STDIOSTREAMFILE * streamfile) {
fclose(streamfile->infile);
free(streamfile->buffer);
@ -206,7 +161,6 @@ static STREAMFILE * open_stdio_streamfile_buffer_by_file(FILE *infile,const char
streamfile->sf.get_size = (void*)get_size_stdio;
streamfile->sf.get_offset = (void*)get_offset_stdio;
streamfile->sf.get_name = (void*)get_name_stdio;
streamfile->sf.get_realname = (void*)get_name_stdio;
streamfile->sf.open = (void*)open_stdio;
streamfile->sf.close = (void*)close_stdio;
@ -250,6 +204,145 @@ STREAMFILE * open_stdio_streamfile_by_file(FILE * file, const char * filename) {
/* **************************************************** */
typedef struct {
STREAMFILE sf;
STREAMFILE *inner_sf;
off_t offset; /* current buffer data start */
uint8_t * buffer; /* data buffer */
size_t buffersize; /* max buffer size */
size_t validsize; /* current buffer size */
size_t filesize; /* buffered file size */
} BUFFER_STREAMFILE;
static size_t buffer_read(BUFFER_STREAMFILE *streamfile, uint8_t * dest, off_t offset, size_t length) {
size_t length_read_total = 0;
if (!streamfile || !dest || length <= 0 || offset < 0)
return 0;
/* is the part of the requested length in the buffer? */
if (offset >= streamfile->offset && offset < streamfile->offset + streamfile->validsize) {
size_t length_to_read;
off_t offset_into_buffer = offset - streamfile->offset;
length_to_read = streamfile->validsize - offset_into_buffer;
if (length_to_read > length)
length_to_read = length;
memcpy(dest,streamfile->buffer + offset_into_buffer,length_to_read);
length_read_total += length_to_read;
length -= length_to_read;
offset += length_to_read;
dest += length_to_read;
}
/* What would make more sense here is to read the whole request
* at once into the dest buffer, as it must be large enough, and then
* copy some part of that into our own buffer.
* The destination buffer is supposed to be much smaller than the
* STREAMFILE buffer, though. Maybe we should only ever return up
* to the buffer size to avoid having to deal with things like this
* which are outside of my intended use. */
/* read the rest of the requested length */
while (length > 0) {
size_t length_to_read, length_read;
streamfile->validsize = 0; /* buffer is empty now */
/* request outside file: ignore to avoid seek/read */
if (offset > streamfile->filesize) {
streamfile->offset = streamfile->filesize;
VGM_LOG_ONCE("ERROR: reading over filesize 0x%x @ 0x%lx + 0x%x (buggy meta?)\n", streamfile->filesize, offset, length);
return length_read_total; /* partially-read buffer */
}
streamfile->offset = offset;
/* decide how much must be read this time */
if (length > streamfile->buffersize)
length_to_read = streamfile->buffersize;
else
length_to_read = length;
/* fill the buffer */
length_read = streamfile->inner_sf->read(streamfile->inner_sf, streamfile->buffer, streamfile->offset, streamfile->buffersize);
streamfile->validsize = length_read;
/* if we can't get enough to satisfy the request (EOF) we give up */
if (length_read < length_to_read) {
memcpy(dest,streamfile->buffer,length_read);
return length_read_total + length_read; /* partially-read buffer */
}
/* use the new buffer */
memcpy(dest,streamfile->buffer,length_to_read);
length_read_total += length_to_read;
length -= length_to_read;
dest += length_to_read;
offset += length_to_read;
}
return length_read_total;
}
static size_t buffer_get_size(BUFFER_STREAMFILE * streamfile) {
return streamfile->filesize; /* cache */
}
static size_t buffer_get_offset(BUFFER_STREAMFILE * streamfile) {
return streamfile->offset; /* cache */ //todo internal offset?
}
static void buffer_get_name(BUFFER_STREAMFILE *streamfile, char *buffer, size_t length) {
streamfile->inner_sf->get_name(streamfile->inner_sf, buffer, length); /* default */
}
static STREAMFILE *buffer_open(BUFFER_STREAMFILE *streamfile, const char * const filename, size_t buffersize) {
STREAMFILE *new_inner_sf = streamfile->inner_sf->open(streamfile->inner_sf,filename,buffersize);
return open_buffer_streamfile(new_inner_sf, buffersize); /* original buffer size is preferable? */
}
static void buffer_close(BUFFER_STREAMFILE *streamfile) {
streamfile->inner_sf->close(streamfile->inner_sf);
free(streamfile->buffer);
free(streamfile);
}
STREAMFILE *open_buffer_streamfile(STREAMFILE *streamfile, size_t buffer_size) {
BUFFER_STREAMFILE *this_sf = NULL;
if (!streamfile) goto fail;
this_sf = calloc(1,sizeof(BUFFER_STREAMFILE));
if (!this_sf) goto fail;
this_sf->buffersize = buffer_size;
if (this_sf->buffersize == 0)
this_sf->buffersize = STREAMFILE_DEFAULT_BUFFER_SIZE;
this_sf->buffer = calloc(this_sf->buffersize,1);
if (!this_sf->buffer) goto fail;
/* set callbacks and internals */
this_sf->sf.read = (void*)buffer_read;
this_sf->sf.get_size = (void*)buffer_get_size;
this_sf->sf.get_offset = (void*)buffer_get_offset;
this_sf->sf.get_name = (void*)buffer_get_name;
this_sf->sf.open = (void*)buffer_open;
this_sf->sf.close = (void*)buffer_close;
this_sf->sf.stream_index = streamfile->stream_index;
this_sf->inner_sf = streamfile;
this_sf->filesize = streamfile->get_size(streamfile);
return &this_sf->sf;
fail:
if (this_sf) free(this_sf->buffer);
free(this_sf);
return NULL;
}
/* **************************************************** */
//todo stream_index: copy? pass? funtion? external?
//todo use realnames on reopen? simplify?
//todo use safe string ops, this ain't easy
@ -272,9 +365,6 @@ static size_t wrap_get_offset(WRAP_STREAMFILE * streamfile) {
static void wrap_get_name(WRAP_STREAMFILE *streamfile, char *buffer, size_t length) {
streamfile->inner_sf->get_name(streamfile->inner_sf, buffer, length); /* default */
}
static void wrap_get_realname(WRAP_STREAMFILE *streamfile, char *buffer, size_t length) {
streamfile->inner_sf->get_realname(streamfile->inner_sf, buffer, length); /* default */
}
static void wrap_open(WRAP_STREAMFILE *streamfile, const char * const filename, size_t buffersize) {
streamfile->inner_sf->open(streamfile->inner_sf, filename, buffersize); /* default (don't wrap) */
}
@ -296,7 +386,6 @@ STREAMFILE *open_wrap_streamfile(STREAMFILE *streamfile) {
this_sf->sf.get_size = (void*)wrap_get_size;
this_sf->sf.get_offset = (void*)wrap_get_offset;
this_sf->sf.get_name = (void*)wrap_get_name;
this_sf->sf.get_realname = (void*)wrap_get_realname;
this_sf->sf.open = (void*)wrap_open;
this_sf->sf.close = (void*)wrap_close;
this_sf->sf.stream_index = streamfile->stream_index;
@ -330,9 +419,6 @@ static off_t clamp_get_offset(CLAMP_STREAMFILE *streamfile) {
static void clamp_get_name(CLAMP_STREAMFILE *streamfile, char *buffer, size_t length) {
streamfile->inner_sf->get_name(streamfile->inner_sf, buffer, length); /* default */
}
static void clamp_get_realname(CLAMP_STREAMFILE *streamfile, char *buffer, size_t length) {
streamfile->inner_sf->get_realname(streamfile->inner_sf, buffer, length); /* default */
}
static STREAMFILE *clamp_open(CLAMP_STREAMFILE *streamfile, const char * const filename, size_t buffersize) {
char original_filename[PATH_LIMIT];
STREAMFILE *new_inner_sf;
@ -366,7 +452,6 @@ STREAMFILE *open_clamp_streamfile(STREAMFILE *streamfile, off_t start, size_t si
this_sf->sf.get_size = (void*)clamp_get_size;
this_sf->sf.get_offset = (void*)clamp_get_offset;
this_sf->sf.get_name = (void*)clamp_get_name;
this_sf->sf.get_realname = (void*)clamp_get_realname;
this_sf->sf.open = (void*)clamp_open;
this_sf->sf.close = (void*)clamp_close;
this_sf->sf.stream_index = streamfile->stream_index;
@ -407,9 +492,6 @@ static off_t io_get_offset(IO_STREAMFILE *streamfile) {
static void io_get_name(IO_STREAMFILE *streamfile, char *buffer, size_t length) {
streamfile->inner_sf->get_name(streamfile->inner_sf, buffer, length); /* default */
}
static void io_get_realname(IO_STREAMFILE *streamfile, char *buffer, size_t length) {
streamfile->inner_sf->get_realname(streamfile->inner_sf, buffer, length); /* default */
}
static STREAMFILE *io_open(IO_STREAMFILE *streamfile, const char * const filename, size_t buffersize) {
//todo should have some flag to decide if opening other files with IO
STREAMFILE *new_inner_sf = streamfile->inner_sf->open(streamfile->inner_sf,filename,buffersize);
@ -435,7 +517,6 @@ STREAMFILE *open_io_streamfile(STREAMFILE *streamfile, void* data, size_t data_s
this_sf->sf.get_size = (void*)io_get_size;
this_sf->sf.get_offset = (void*)io_get_offset;
this_sf->sf.get_name = (void*)io_get_name;
this_sf->sf.get_realname = (void*)io_get_realname;
this_sf->sf.open = (void*)io_open;
this_sf->sf.close = (void*)io_close;
this_sf->sf.stream_index = streamfile->stream_index;
@ -478,9 +559,6 @@ static void fakename_get_name(FAKENAME_STREAMFILE *streamfile, char *buffer, siz
strncpy(buffer,streamfile->fakename,length);
buffer[length-1]='\0';
}
static void fakename_get_realname(FAKENAME_STREAMFILE *streamfile, char *buffer, size_t length) {
fakename_get_name(streamfile, buffer, length);
}
static STREAMFILE *fakename_open(FAKENAME_STREAMFILE *streamfile, const char * const filename, size_t buffersize) {
/* detect re-opening the file */
if (strcmp(filename, streamfile->fakename) == 0) {
@ -513,7 +591,6 @@ STREAMFILE *open_fakename_streamfile(STREAMFILE *streamfile, const char * fakena
this_sf->sf.get_size = (void*)fakename_get_size;
this_sf->sf.get_offset = (void*)fakename_get_offset;
this_sf->sf.get_name = (void*)fakename_get_name;
this_sf->sf.get_realname = (void*)fakename_get_realname;
this_sf->sf.open = (void*)fakename_open;
this_sf->sf.close = (void*)fakename_close;
this_sf->sf.stream_index = streamfile->stream_index;
@ -594,9 +671,6 @@ static size_t multifile_get_offset(MULTIFILE_STREAMFILE * streamfile) {
static void multifile_get_name(MULTIFILE_STREAMFILE *streamfile, char *buffer, size_t length) {
streamfile->inner_sfs[0]->get_name(streamfile->inner_sfs[0], buffer, length);
}
static void multifile_get_realname(MULTIFILE_STREAMFILE *streamfile, char *buffer, size_t length) {
multifile_get_name(streamfile, buffer, length);
}
static STREAMFILE *multifile_open(MULTIFILE_STREAMFILE *streamfile, const char * const filename, size_t buffersize) {
char original_filename[PATH_LIMIT];
STREAMFILE *new_sf = NULL;
@ -662,7 +736,6 @@ STREAMFILE *open_multifile_streamfile(STREAMFILE **streamfiles, size_t streamfil
this_sf->sf.get_size = (void*)multifile_get_size;
this_sf->sf.get_offset = (void*)multifile_get_offset;
this_sf->sf.get_name = (void*)multifile_get_name;
this_sf->sf.get_realname = (void*)multifile_get_realname;
this_sf->sf.open = (void*)multifile_open;
this_sf->sf.close = (void*)multifile_close;
this_sf->sf.stream_index = streamfiles[0]->stream_index;

View File

@ -51,8 +51,6 @@ typedef struct _STREAMFILE {
off_t (*get_offset)(struct _STREAMFILE *);
/* for dual-file support */
void (*get_name)(struct _STREAMFILE *,char *name,size_t length);
/* for when the "name" is encoded specially, this is the actual user visible name */
void (*get_realname)(struct _STREAMFILE *,char *name,size_t length);
struct _STREAMFILE * (*open)(struct _STREAMFILE *,const char * const filename,size_t buffersize);
void (*close)(struct _STREAMFILE *);
@ -70,6 +68,11 @@ STREAMFILE *open_stdio_streamfile(const char * filename);
/* Opens a standard STREAMFILE from a pre-opened FILE. */
STREAMFILE *open_stdio_streamfile_by_file(FILE * file, const char * filename);
/* Opens a STREAMFILE that does buffered IO.
* Can be used when the underlying IO may be slow (like when using custom IO).
* Buffer size is optional. */
STREAMFILE *open_buffer_streamfile(STREAMFILE *streamfile, size_t buffer_size);
/* Opens a STREAMFILE that doesn't close the underlying streamfile.
* Calls to open won't wrap the new SF (assumes it needs to be closed).
* Can be used in metas to test custom IO without closing the external SF. */

View File

@ -363,7 +363,10 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = {
init_vgmstream_stm,
init_vgmstream_ea_snu,
init_vgmstream_awc,
init_vgmstream_nsw_opus,
init_vgmstream_opus_std,
init_vgmstream_opus_n1,
init_vgmstream_opus_capcom,
init_vgmstream_opus_nop,
init_vgmstream_pc_al2,
init_vgmstream_pc_ast,
init_vgmstream_naac,
@ -399,6 +402,9 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = {
init_vgmstream_msb_msh,
init_vgmstream_txtp,
init_vgmstream_smc_smh,
init_vgmstream_ea_sps_fb,
init_vgmstream_ppst,
init_vgmstream_opus_ppp,
init_vgmstream_txth, /* should go at the end (lower priority) */
#ifdef VGM_USE_FFMPEG
@ -768,10 +774,13 @@ void close_vgmstream(VGMSTREAM * vgmstream) {
}
if (vgmstream->coding_type == coding_NWA) {
nwa_codec_data *data = (nwa_codec_data *) vgmstream->codec_data;
close_nwa(data->nwa);
free(data);
vgmstream->codec_data = NULL;
if (vgmstream->codec_data) {
nwa_codec_data *data = (nwa_codec_data *) vgmstream->codec_data;
if (data->nwa)
close_nwa(data->nwa);
free(data);
vgmstream->codec_data = NULL;
}
}
@ -2516,7 +2525,7 @@ int get_vgmstream_average_bitrate(VGMSTREAM * vgmstream) {
/**
* Inits vgmstreams' channels doing two things:
* Inits vgmstream, doing two things:
* - sets the starting offset per channel (depending on the layout)
* - opens its own streamfile from on a base one. One streamfile per channel may be open (to improve read/seeks).
* Should be called in metas before returning the VGMSTREAM.
@ -2529,14 +2538,18 @@ int vgmstream_open_stream(VGMSTREAM * vgmstream, STREAMFILE *streamFile, off_t s
int use_same_offset_per_channel = 0;
/* stream/offsets not needed, manage themselves */
/* stream/offsets not needed, managed by layout */
if (vgmstream->layout_type == layout_aix ||
vgmstream->layout_type == layout_segmented ||
vgmstream->layout_type == layout_layered)
return 1;
/* stream/offsets not needed, managed by decoder */
if (vgmstream->coding_type == coding_NWA)
return 1;
#ifdef VGM_USE_FFMPEG
/* stream/offsets not needed, FFmpeg manages itself */
/* stream/offsets not needed, managed by decoder */
if (vgmstream->coding_type == coding_FFmpeg)
return 1;
#endif

View File

@ -272,8 +272,8 @@ typedef enum {
meta_DSP_AGSC, /* Retro: Metroid Prime 2 title */
meta_DSP_MPDSP, /* Monopoly Party single header stereo */
meta_DSP_JETTERS, /* Bomberman Jetters .dsp */
meta_DSP_MSS, /* ? */
meta_DSP_GCM, /* ? */
meta_DSP_MSS, /* Free Radical GC games */
meta_DSP_GCM, /* some of Traveller's Tales games */
meta_DSP_STR, /* Conan .str files */
meta_DSP_SADB, /* .sad */
meta_DSP_WSI, /* .wsi */
@ -627,7 +627,7 @@ typedef enum {
meta_BINK, /* RAD Game Tools BINK audio/video */
meta_EA_SNU, /* Electronic Arts SNU (Dead Space) */
meta_AWC, /* Rockstar AWC (GTA5, RDR) */
meta_NSW_OPUS, /* Lego City Undercover (Switch) */
meta_OPUS, /* Nintendo Opus [Lego City Undercover (Switch)] */
meta_PC_AL2, /* Conquest of Elysium 3 (PC) */
meta_PC_AST, /* Dead Rising (PC) */
meta_NAAC, /* Namco AAC (3DS) */
@ -670,6 +670,9 @@ typedef enum {
meta_OGG_ENO, /* Ogg Vorbis with encryption [Metronomicon (PC)] */
meta_TXTP, /* generic text playlist */
meta_SMC_SMH, /* Wangan Midnight (System 246) */
meta_OGG_YS8, /* Ogg Vorbis with encryption (Ys VIII PC) */
meta_PPST, /* PPST [Parappa the Rapper (PSP)] */
meta_OPUS_PPP, /* .at9 Opus [Penny-Punching Princess (Switch)] */
#ifdef VGM_USE_FFMPEG
meta_FFmpeg,

View File

@ -173,10 +173,6 @@ static void wasf_get_name(WINAMP_STREAMFILE *streamfile, char *buffer, size_t le
streamfile->stdiosf->get_name(streamfile->stdiosf, buffer, length);
}
static void wasf_get_realname(WINAMP_STREAMFILE *streamfile, char *buffer, size_t length) {
streamfile->stdiosf->get_realname(streamfile->stdiosf, buffer, length);
}
static STREAMFILE *wasf_open(WINAMP_STREAMFILE *streamFile, const char *const filename, size_t buffersize) {
int newfd;
FILE *newfile;
@ -227,7 +223,6 @@ static STREAMFILE *open_winamp_streamfile_by_file(FILE *infile, const char * pat
this_sf->sf.get_size = (void*)wasf_get_size;
this_sf->sf.get_offset = (void*)wasf_get_offset;
this_sf->sf.get_name = (void*)wasf_get_name;
this_sf->sf.get_realname = (void*)wasf_get_realname;
this_sf->sf.open = (void*)wasf_open;
this_sf->sf.close = (void*)wasf_close;

View File

@ -127,7 +127,6 @@ static STREAMFILE *open_xmplay_streamfile_by_xmpfile(XMPFILE infile, const char
streamfile->sf.get_size = (void*)xmpsf_get_size;
streamfile->sf.get_offset = (void*)xmpsf_get_offset;
streamfile->sf.get_name = (void*)xmpsf_get_name;
streamfile->sf.get_realname = (void*)xmpsf_get_name;
streamfile->sf.open = (void*)xmpsf_open;
streamfile->sf.close = (void*)xmpsf_close;
streamfile->infile = infile;