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; STREAMFILE sf;
VFSFile *vfsFile; VFSFile *vfsFile;
off_t offset; off_t offset;
char name[260]; char name[32768];
char realname[260]; //char realname[32768];
} VFSSTREAMFILE; } VFSSTREAMFILE;
static STREAMFILE *open_vfs_by_VFSFILE(VFSFile *file, const char *path); 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'; 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, static STREAMFILE *open_vfs_impl(VFSSTREAMFILE *streamfile,
const char *const filename, const char *const filename,
size_t buffersize) { 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_size = get_size_vfs;
streamfile->sf.get_offset = get_offset_vfs; streamfile->sf.get_offset = get_offset_vfs;
streamfile->sf.get_name = get_name_vfs; streamfile->sf.get_name = get_name_vfs;
streamfile->sf.get_realname = get_realname_vfs;
streamfile->sf.open = open_vfs_impl; streamfile->sf.open = open_vfs_impl;
streamfile->sf.close = close_vfs; streamfile->sf.close = close_vfs;
@ -88,12 +81,16 @@ STREAMFILE *open_vfs_by_VFSFILE(VFSFile *file, const char *path) {
streamfile->offset = 0; streamfile->offset = 0;
strncpy(streamfile->name, path, sizeof(streamfile->name)); strncpy(streamfile->name, path, sizeof(streamfile->name));
streamfile->name[sizeof(streamfile->name) - 1] = '\0'; streamfile->name[sizeof(streamfile->name) - 1] = '\0';
{
gchar *realname = g_filename_from_uri(path, NULL, NULL); // for reference, actual file path ("name" has protocol path, file://...).
strncpy(streamfile->realname, realname, sizeof(streamfile->realname)); // name should work for all situations but in case it's needed again maybe
streamfile->realname[sizeof(streamfile->realname) - 1] = '\0'; // get_name should always return realname, as it's used to open companion VFSFiles
g_free(realname); //{
} // 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; 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_size = (size_t (__cdecl *)(_STREAMFILE *)) get_size_foo;
streamfile->sf.get_offset = (off_t (__cdecl *)(_STREAMFILE *)) get_offset_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_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.open = (_STREAMFILE *(__cdecl *)(_STREAMFILE *,const char *const ,size_t)) open_foo;
streamfile->sf.close = (void (__cdecl *)(_STREAMFILE *)) close_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)) if (!ealayer3_skip_data(stream, data, num_stream, 0))
goto fail; 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 */ /* get second frame/granule (MPEG1 only) if first granule was found */
granule_found = 0; granule_found = 0;
@ -298,7 +300,7 @@ static int ealayer3_parse_frame_v2(vgm_bitstream *is, ealayer3_frame_info * eaf)
if (!ok) goto fail; 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_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); //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); 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; size_t bytes_filled;
int i; int i;
if (!eaf->pcm_size)
return 1;
bytes_filled = sizeof(sample)*ms->samples_filled*data->channels_per_frame; bytes_filled = sizeof(sample)*ms->samples_filled*data->channels_per_frame;
if (bytes_filled + eaf->pcm_size > ms->output_buffer_size) { 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->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_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); 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; decode_to_discard = data->samples_per_frame;//+ eaf->v1_pcm_number;
} }
else { else {
// todo also discard
if (decode_to_discard == 0) /* seems ok? */ if (decode_to_discard == 0) /* seems ok? */
decode_to_discard += data->samples_per_frame;//+ eaf->v1_pcm_number; decode_to_discard += data->samples_per_frame;//+ eaf->v1_pcm_number;
else if (decode_to_discard == 576) /* untested */ 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_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): /* todo supposed skip modes (only seen 0x00):
* *
* AB00CCCC CCCCCCCC if A is set: DDEEEEEE EEEEFFFF FFFFFFGG GGGGGGGG * 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 * 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 */ /* modify decoded samples depending on flag */
if (eaf->v2_mode == 0x00) { if (eaf->v2_mode == 0x00) {
size_t decode_to_discard = eaf->v2_mode_value; size_t decode_to_discard = eaf->v2_mode_value; /* (usually 0 in V2P, varies in V2S) */
if (decode_to_discard == 0)
if (decode_to_discard == 576) decode_to_discard = 576;
decode_to_discard = data->samples_per_frame;//+ eaf->v2_pcm_number;
//todo output seems correct-ish but reaches file end and tries to parse more frames
ms->decode_to_discard += decode_to_discard; ms->decode_to_discard += decode_to_discard;
} }
#endif
} }
return 1; return 1;

View File

@ -19,6 +19,7 @@ static const char* extension_list[] = {
//"ac3", //FFmpeg, not parsed //common? //"ac3", //FFmpeg, not parsed //common?
"ace", //fake, for tri-Ace's formats (to be removed) "ace", //fake, for tri-Ace's formats (to be removed)
"acm", "acm",
"ad", //txth/reserved [Xenosaga Freaks (PS2)]
"adm", "adm",
"adp", "adp",
"adpcm", "adpcm",
@ -957,7 +958,7 @@ static const meta_info meta_info_list[] = {
{meta_BINK, "RAD Game Tools Bink header"}, {meta_BINK, "RAD Game Tools Bink header"},
{meta_EA_SNU, "Electronic Arts SNU header"}, {meta_EA_SNU, "Electronic Arts SNU header"},
{meta_AWC, "Rockstar AWC 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_AL2, "Illwinter Game Design AL2 raw header"},
{meta_PC_AST, "Capcom AST (PC) header"}, {meta_PC_AST, "Capcom AST (PC) header"},
{meta_UBI_SB, "Ubisoft SBx 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_OGG_ENO, "Ogg Vorbis (ENO header)"},
{meta_TXTP, "TXTP generic header"}, {meta_TXTP, "TXTP generic header"},
{meta_SMC_SMH, "Genki SMC+SMH 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 #ifdef VGM_USE_FFMPEG
{meta_FFmpeg, "FFmpeg supported file format"}, {meta_FFmpeg, "FFmpeg supported file format"},

View File

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

View File

@ -225,7 +225,15 @@
> >
</File> </File>
<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>
<File <File
@ -703,7 +711,7 @@
> >
</File> </File>
<File <File
RelativePath=".\meta\nsw_opus.c" RelativePath=".\meta\opus.c"
> >
</File> </File>
<File <File
@ -733,6 +741,10 @@
<File <File
RelativePath=".\meta\omu.c" RelativePath=".\meta\omu.c"
> >
</File>
<File
RelativePath=".\meta\opus_ppp.c"
>
</File> </File>
<File <File
RelativePath=".\meta\otm.c" RelativePath=".\meta\otm.c"
@ -794,6 +806,10 @@
RelativePath=".\meta\pos.c" RelativePath=".\meta\pos.c"
> >
</File> </File>
<File
RelativePath=".\meta\ppst.c"
>
</File>
<File <File
RelativePath=".\meta\ps2_vds_vdm.c" RelativePath=".\meta\ps2_vds_vdm.c"
> >

View File

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

View File

@ -74,7 +74,13 @@
<ClInclude Include="meta\bar_streamfile.h"> <ClInclude Include="meta\bar_streamfile.h">
<Filter>meta\Header Files</Filter> <Filter>meta\Header Files</Filter>
</ClInclude> </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> <Filter>meta\Header Files</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="meta\sqex_scd_streamfile.h"> <ClInclude Include="meta\sqex_scd_streamfile.h">
@ -442,6 +448,9 @@
<ClCompile Include="meta\omu.c"> <ClCompile Include="meta\omu.c">
<Filter>meta\Source Files</Filter> <Filter>meta\Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="meta\opus_ppp.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\otm.c"> <ClCompile Include="meta\otm.c">
<Filter>meta\Source Files</Filter> <Filter>meta\Source Files</Filter>
</ClCompile> </ClCompile>
@ -475,6 +484,9 @@
<ClCompile Include="meta\pos.c"> <ClCompile Include="meta\pos.c">
<Filter>meta\Source Files</Filter> <Filter>meta\Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="meta\ppst.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\ps2_vds_vdm.c"> <ClCompile Include="meta\ps2_vds_vdm.c">
<Filter>meta\Source Files</Filter> <Filter>meta\Source Files</Filter>
</ClCompile> </ClCompile>
@ -1126,7 +1138,7 @@
<ClCompile Include="meta\ngca.c"> <ClCompile Include="meta\ngca.c">
<Filter>meta\Source Files</Filter> <Filter>meta\Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="meta\nsw_opus.c"> <ClCompile Include="meta\opus.c">
<Filter>meta\Source Files</Filter> <Filter>meta\Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="meta\ps2_mtaf.c"> <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_size = (void*)get_size_aix;
streamfile->sf.get_offset = (void*)get_offset_aix; streamfile->sf.get_offset = (void*)get_offset_aix;
streamfile->sf.get_name = (void*)get_name_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.open = (void*)open_aix_impl;
streamfile->sf.close = (void*)close_aix; 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) { static void get_name_bar(BARSTREAMFILE *streamFile, char *name, size_t length) {
return streamFile->real_file->get_name(streamFile->real_file, name, length); 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 *open_bar(BARSTREAMFILE *streamFile, const char * const filename, size_t buffersize) { 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_size = (void*)get_size_bar;
streamfile->sf.get_offset = (void*)get_offset_bar; streamfile->sf.get_offset = (void*)get_offset_bar;
streamfile->sf.get_name = (void*)get_name_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.open = (void*)open_bar;
streamfile->sf.close = (void*)close_bar; streamfile->sf.close = (void*)close_bar;

View File

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

View File

@ -1,7 +1,7 @@
#include "meta.h" #include "meta.h"
#include "../layout/layout.h" #include "../layout/layout.h"
#include "../coding/coding.h" #include "../coding/coding.h"
#include "ea_eaac_eatrax_streamfile.h" #include "ea_eaac_streamfile.h"
/* EAAudioCore formats, EA's current audio middleware */ /* EAAudioCore formats, EA's current audio middleware */
@ -108,6 +108,48 @@ fail:
return NULL; 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). /* 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), * 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; uint32_t num_samples, loop_start = 0, loop_end = 0;
/* EA SNR/SPH header */ /* EA SNR/SPH header */
version = (read_8bit(header_offset + 0x00,streamHead) >> 4) & 0xf; version = (read_8bit(header_offset + 0x00,streamHead) >> 4) & 0x0F;
codec = (read_8bit(header_offset + 0x00,streamHead) >> 0) & 0xf; codec = (read_8bit(header_offset + 0x00,streamHead) >> 0) & 0x0F;
channel_config = read_8bit(header_offset + 0x01,streamHead); channel_config = read_8bit(header_offset + 0x01,streamHead) & 0xFE;
sample_rate = (uint16_t)read_16bitBE(header_offset + 0x02,streamHead); 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) 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; 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): /* 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) */ * &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) { if (version != 0 && version != 1) {
VGM_LOG("EA SNS/SPS: unknown version\n"); VGM_LOG("EA SNS/SPS: unknown version\n");
goto fail; 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 0x06: /* "L32P": EALayer3 v2 "PCM" [Battlefield 1943 (PS3)] */
case 0x07: { /* "L32S": EALayer3 v2 "Spike" [Dante's Inferno (PS3)] */ case 0x07: { /* "L32S": EALayer3 v2 "Spike" [Dante's Inferno (PS3)] */
mpeg_custom_config cfg = {0}; 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); 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 */ /* remove blocks on reads for some edge cases in L32P and to properly apply discard modes
vgmstream->codec_data = init_mpeg_custom(streamData, mpeg_start_offset, &vgmstream->coding_type, vgmstream->channels, type, &cfg); * (otherwise, and removing discards, it'd work with layout_blocked_ea_sns) */
if (!vgmstream->codec_data) goto fail; 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; break;
} }
#endif #endif
@ -253,10 +301,10 @@ static VGMSTREAM * init_vgmstream_eaaudiocore_header(STREAMFILE * streamHead, ST
vgmstream->layout_type = layout_none; vgmstream->layout_type = layout_none;
/* EATrax is "buffered" ATRAC9, uses custom IO since it's kind of complex to add to the decoder */ /* 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_eaac_streamfile(streamData, version, codec, start_offset, total_size);
temp_streamFile = setup_eatrax_streamfile(streamData, total_size);
if (!temp_streamFile) goto fail; if (!temp_streamFile) goto fail;
start_offset = 0x00; /* must point to the custom streamfile's beginning */
break; break;
} }
#endif #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)) if (!vgmstream_open_stream(vgmstream,temp_streamFile ? temp_streamFile : streamData,start_offset))
goto fail; goto fail;
close_streamfile(temp_streamFile);
if (vgmstream->layout_type == layout_blocked_ea_sns) if (vgmstream->layout_type == layout_blocked_ea_sns)
block_update_ea_sns(start_offset, vgmstream); block_update_ea_sns(start_offset, vgmstream);
close_streamfile(temp_streamFile);
return vgmstream; return vgmstream;
fail: 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++) { for (i = 0; i < fsbkey_list_count; i++) {
fsbkey_info entry = fsbkey_list[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); temp_streamFile = setup_fsb_streamfile(streamFile, entry.fsbkey, entry.fsbkey_size, entry.is_alt);
if (!temp_streamFile) goto fail; 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" /* Supreme Commander 2 */ //"B2A7BB00"
static const uint8_t key_sc2[] = { 0x42,0x32,0x41,0x37,0x42,0x42,0x30,0x30 }; 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: // Unknown:
// - Battle: Los Angeles // - Battle: Los Angeles
// - Guitar Hero: Warriors of Rock, DJ hero FSB // - 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 { 0,1, sizeof(key_sc2),key_sc2 },//untested
{ 1,0, sizeof(key_sc2),key_sc2 },//untested { 1,0, sizeof(key_sc2),key_sc2 },//untested
{ 1,1, 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]); 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) // Dx2 Shin Megami Tensei Liberation (iOS/Android)
{118714477}, // 000000000713706D {118714477}, // 000000000713706D
// Oira (Cygames) [iOS/Android]
{46460622}, // 0000000002C4EECE
}; };
#endif/*_HCA_KEYS_H_*/ #endif/*_HCA_KEYS_H_*/

View File

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

View File

@ -669,7 +669,10 @@ VGMSTREAM * init_vgmstream_stm(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_awc(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); 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_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*/ #endif /*_META_H*/

View File

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

View File

@ -1,67 +1,44 @@
#include "meta.h" #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 * init_vgmstream_naomi_adpcm(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL; VGMSTREAM * vgmstream = NULL;
char filename[PATH_LIMIT];
off_t start_offset; off_t start_offset;
int loop_flag = 0; int loop_flag, channel_count;
int 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 /* checks */
/* check header */ if (!check_extensions(streamFile, "adpcm"))
if ((read_32bitBE(0x00,streamFile) != 0x41445043) || /* "ADPC" */
(read_32bitBE(0x04,streamFile) != 0x41445043)) /* "M_v0" */
goto fail; goto fail;
#endif 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; loop_flag = 0;
channel_count = 2; 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); vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail; if (!vgmstream) goto fail;
/* fill in the vital statistics */
start_offset = 0x40;
vgmstream->channels = channel_count;
vgmstream->sample_rate = 44100; vgmstream->sample_rate = 44100;
vgmstream->coding_type = coding_AICA_int; vgmstream->num_samples = aica_bytes_to_samples(data_size, channel_count);
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->coding_type = coding_AICA_int;
vgmstream->layout_type = layout_interleave; 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; vgmstream->meta_type = meta_NAOMI_ADPCM;
/* open the file for reading */ if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
{ goto fail;
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 */
}
}
return vgmstream; return vgmstream;
/* clean up anything we may have opened */
fail: fail:
if (vgmstream) close_vgmstream(vgmstream); close_vgmstream(vgmstream);
return NULL; return NULL;
} }

View File

@ -8,7 +8,8 @@ VGMSTREAM * init_vgmstream_nds_hwas(STREAMFILE *streamFile) {
off_t start_offset; off_t start_offset;
int channel_count, loop_flag = 0; 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")) if (!check_extensions(streamFile,"hwas"))
goto fail; 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) read_32bitBE(0x0c,streamFile) != 0x04)
goto fail; goto fail;
//VGM_LOG("1\n");
entries = read_16bitBE(0x02,streamFile); entries = read_16bitBE(0x02,streamFile);
entries_offset = read_32bitBE(0x10,streamFile); entries_offset = read_32bitBE(0x10,streamFile);
coefs_offset = read_32bitBE(0x14,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 */ /* parse header */
{ {
uint8_t flags; 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,281 +4,70 @@
#include <string.h> #include <string.h>
#include <ctype.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 * init_vgmstream_nwa(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL; VGMSTREAM * vgmstream = NULL;
char filename[PATH_LIMIT]; off_t start_offset;
int i; int channel_count, loop_flag = 0;
int channel_count; int32_t loop_start_sample = 0, loop_end_sample = 0;
int loop_flag = 0; int nwainfo_ini_found = 0, gameexe_ini_found = 0;
int32_t loop_start_sample = 0; int compression_level;
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;
/* check extension, case insensitive */
streamFile->get_name(streamFile,filename,sizeof(filename)); /* checks */
if (strcasecmp("nwa",filename_extension(filename))) goto fail; if (!check_extensions(streamFile, "nwa"))
goto fail;
channel_count = read_16bitLE(0x00,streamFile); channel_count = read_16bitLE(0x00,streamFile);
if (channel_count != 1 && channel_count != 2) goto fail; if (channel_count != 1 && channel_count != 2) goto fail;
/* check if we're using raw pcm */ /* check if we're using raw pcm */
if ( if ( read_32bitLE(0x08,streamFile)==-1 || /* compression level */
read_32bitLE(0x08,streamFile)==-1 || /* compression level */
read_32bitLE(0x10,streamFile)==0 || /* block count */ read_32bitLE(0x10,streamFile)==0 || /* block count */
read_32bitLE(0x18,streamFile)==0 || /* compressed data size */ read_32bitLE(0x18,streamFile)==0 || /* compressed data size */
read_32bitLE(0x20,streamFile)==0 || /* block size */ read_32bitLE(0x20,streamFile)==0 || /* block size */
read_32bitLE(0x24,streamFile)==0 /* restsize */ read_32bitLE(0x24,streamFile)==0 ) { /* restsize */
) compression_level = -1;
{
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;
}
/* 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;
/* 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 { } else {
strncpy(ini_lastslash+1,"NWAINFO.INI", compression_level = read_32bitLE(0x08,streamFile);
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) { /* loop points come from external files */
char loopstring[9]={0}; 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);
if (read_streamfile((uint8_t*)loopstring,found_off,8, start_offset = 0x2c;
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 */
/* build the VGMSTREAM */ /* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag); vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail; if (!vgmstream) goto fail;
/* fill in the vital statistics */
vgmstream->channels = channel_count;
vgmstream->sample_rate = read_32bitLE(0x04,streamFile); 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; switch(compression_level) {
case -1:
if (just_pcm) {
switch (read_16bitLE(0x02,streamFile)) { switch (read_16bitLE(0x02,streamFile)) {
case 8: case 8:
vgmstream->coding_type = coding_PCM8; vgmstream->coding_type = coding_PCM8;
vgmstream->interleave_block_size = 1; vgmstream->interleave_block_size = 0x01;
break; break;
case 16: case 16:
vgmstream->coding_type = coding_PCM16LE; vgmstream->coding_type = coding_PCM16LE;
vgmstream->interleave_block_size = 2; vgmstream->interleave_block_size = 0x02;
break; break;
default: default:
goto fail; goto fail;
} }
if (channel_count > 1) {
vgmstream->layout_type = layout_interleave; vgmstream->layout_type = layout_interleave;
} else { break;
vgmstream->layout_type = layout_none;
}
}
else
{
switch (comp_level)
{
case 0: case 0:
case 1: case 1:
case 2: case 2:
@ -286,13 +75,16 @@ VGMSTREAM * init_vgmstream_nwa(STREAMFILE *streamFile) {
case 4: case 4:
case 5: case 5:
vgmstream->coding_type = coding_NWA; vgmstream->coding_type = coding_NWA;
vgmstream->layout_type = layout_none;
vgmstream->codec_data = open_nwa_vgmstream(streamFile);
if (!vgmstream->codec_data) goto fail;
break; break;
default: default:
goto fail; goto fail;
break; break;
} }
vgmstream->layout_type = layout_none;
}
if (nwainfo_ini_found) { if (nwainfo_ini_found) {
vgmstream->meta_type = meta_NWA_NWAINFOINI; vgmstream->meta_type = meta_NWA_NWAINFOINI;
@ -311,38 +103,205 @@ VGMSTREAM * init_vgmstream_nwa(STREAMFILE *streamFile) {
} }
if (just_pcm) { if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
/* open the file for reading by each channel */ goto fail;
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;
}
return vgmstream; return vgmstream;
/* clean up anything we may have opened */
fail: fail:
if (vgmstream) close_vgmstream(vgmstream); close_vgmstream(vgmstream);
if (data) { return NULL;
if (data->nwa) }
/* 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); close_nwa(data->nwa);
} }
free(data); 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; int i;
/* add 0x23 ('#') */ /* add 0x23 ('#') */
{ for (i = 0; i < bytes_read; i++) {
for (i = 0; i < bytes_read; i++)
((uint8_t*)ptr)[i] += 0x23; ((uint8_t*)ptr)[i] += 0x23;
} }
} }
@ -126,11 +125,9 @@ static void sngw_ogg_decryption_callback(void *ptr, size_t size, size_t nmemb, v
put_32bitBE(key, ov_streamfile->xor_value); put_32bitBE(key, ov_streamfile->xor_value);
/* bytes are xor'd with key and nibble-swapped */ /* bytes are xor'd with key and nibble-swapped, first "OggS" is changed */
{
for (i = 0; i < bytes_read; i++) { for (i = 0; i < bytes_read; i++) {
if (ov_streamfile->offset+i < 0x04) { 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]; ((uint8_t*)ptr)[i] = (uint8_t)header_id[(ov_streamfile->offset + i) % 4];
} }
else { else {
@ -138,7 +135,6 @@ static void sngw_ogg_decryption_callback(void *ptr, size_t size, size_t nmemb, v
((uint8_t*)ptr)[i] = ((val << 4) & 0xf0) | ((val >> 4) & 0x0f); ((uint8_t*)ptr)[i] = ((val << 4) & 0xf0) | ((val >> 4) & 0x0f);
} }
} }
}
} }
static void isd_ogg_decryption_callback(void *ptr, size_t size, size_t nmemb, void *datasource) { static void isd_ogg_decryption_callback(void *ptr, size_t size, size_t nmemb, void *datasource) {
@ -150,8 +146,7 @@ static void isd_ogg_decryption_callback(void *ptr, size_t size, size_t nmemb, vo
int i; int i;
/* bytes are xor'd with key */ /* bytes are xor'd with key */
{ for (i = 0; i < bytes_read; i++) {
for (i = 0; i < bytes_read; i++)
((uint8_t*)ptr)[i] ^= key[(ov_streamfile->offset + i) % 16]; ((uint8_t*)ptr)[i] ^= key[(ov_streamfile->offset + i) % 16];
} }
} }
@ -162,8 +157,7 @@ static void l2sd_ogg_decryption_callback(void *ptr, size_t size, size_t nmemb, v
int i; int i;
char *header_id = "OggS"; char *header_id = "OggS";
/* First "OggS" is changed */ /* first "OggS" is changed */
{
for (i = 0; i < bytes_read; i++) { for (i = 0; i < bytes_read; i++) {
if (ov_streamfile->offset+i < 0x04) { if (ov_streamfile->offset+i < 0x04) {
/* replace key in the first 4 bytes with "OggS" */ /* replace key in the first 4 bytes with "OggS" */
@ -173,7 +167,6 @@ static void l2sd_ogg_decryption_callback(void *ptr, size_t size, size_t nmemb, v
break; break;
} }
} }
}
} }
static void rpgmvo_ogg_decryption_callback(void *ptr, size_t size, size_t nmemb, void *datasource) { static void rpgmvo_ogg_decryption_callback(void *ptr, size_t size, size_t nmemb, void *datasource) {
@ -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 */ /* Ogg Vorbis, by way of libvorbisfile; may contain loop comments */
VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) { VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) {
@ -225,6 +229,7 @@ VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) {
int is_l2sd = 0; int is_l2sd = 0;
int is_rpgmvo = 0; int is_rpgmvo = 0;
int is_eno = 0; int is_eno = 0;
int is_ys8 = 0;
/* check extension */ /* check extension */
@ -252,9 +257,7 @@ VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) {
/* check standard Ogg Vorbis */ /* check standard Ogg Vorbis */
if (is_ogg) { if (is_ogg) {
if (read_32bitBE(0x00,streamFile) == 0x2c444430) { /* Psychic Software obfuscation [Darkwind: War on Wheels (PC)] */
/* check Psychic Software obfuscation (Darkwind: War on Wheels PC) */
if (read_32bitBE(0x00,streamFile) == 0x2c444430) {
is_psychic = 1; is_psychic = 1;
ovmi.decryption_callback = psychic_ogg_decryption_callback; ovmi.decryption_callback = psychic_ogg_decryption_callback;
} }
@ -262,8 +265,12 @@ VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) {
is_l2sd = 1; is_l2sd = 1;
ovmi.decryption_callback = l2sd_ogg_decryption_callback; 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" */ 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; start_offset = 0x01;
} }
/* check Ys VIII (PC) */
if (is_ys8) {
ovmi.xor_value = 0xF0;
ovmi.decryption_callback = ys8_ogg_decryption_callback;
}
if (is_um3) { if (is_um3) {
ovmi.meta_type = meta_OGG_UM3; ovmi.meta_type = meta_OGG_UM3;
@ -343,6 +355,8 @@ VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) {
ovmi.meta_type = meta_OGG_RPGMV; ovmi.meta_type = meta_OGG_RPGMV;
} else if (is_eno) { } else if (is_eno) {
ovmi.meta_type = meta_OGG_ENO; ovmi.meta_type = meta_OGG_ENO;
} else if (is_ys8) {
ovmi.meta_type = meta_OGG_YS8;
} else { } else {
ovmi.meta_type = meta_OGG_VORBIS; ovmi.meta_type = meta_OGG_VORBIS;
} }
@ -500,6 +514,8 @@ VGMSTREAM * init_vgmstream_ogg_vorbis_callbacks(STREAMFILE *streamFile, ov_callb
loop_length_found = 1; 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; break;
default: default:
VGM_LOG("unknown codec 0x%04x\n", codec); VGM_LOG("P3D: unknown codec 0x%04x\n", codec);
goto fail; goto fail;
} }
@ -175,7 +175,7 @@ VGMSTREAM * init_vgmstream_p3d(STREAMFILE *streamFile) {
#endif #endif
default: default:
VGM_LOG("unknown codec 0x%04x\n", codec); VGM_LOG("P3D: unknown codec 0x%04x\n", codec);
goto fail; 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) */ /* decrypt data (xor) */
for (i = 0; i < bytes_read; i++) { for (i = 0; i < bytes_read; i++) {
if (offset+i >= data->start_offset) { if (offset+i >= data->start_offset) {
//VGM_LOG("xor %x to %x\n", dest[i], dest[i] ^ 0x5A);getchar();
dest[i] = dest[i] ^ 0x5A; dest[i] = dest[i] ^ 0x5A;
} }
} }

View File

@ -1,47 +1,46 @@
#include "meta.h" #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 * init_vgmstream_ps2_msa(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL; VGMSTREAM * vgmstream = NULL;
off_t start_offset, datasize, filesize; off_t start_offset, data_size, file_size;
int loop_flag, channel_count; int loop_flag, channel_count;
/* check extension, case insensitive */ /* checks */
if (!check_extensions(streamFile, "msa")) goto fail; if (!check_extensions(streamFile, "msa"))
goto fail;
/* check header */
if (read_32bitBE(0x00,streamFile) != 0x00000000) if (read_32bitBE(0x00,streamFile) != 0x00000000)
goto fail; goto fail;
loop_flag = 0; loop_flag = 0;
channel_count = 2; channel_count = 2;
start_offset = 0x14;
data_size = read_32bitLE(0x4,streamFile);
file_size = get_streamfile_size(streamFile);
/* build the VGMSTREAM */ /* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag); vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail; 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->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->coding_type = coding_PSX;
vgmstream->layout_type = layout_interleave; vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = 0x4000; vgmstream->interleave_block_size = 0x4000;
vgmstream->meta_type = meta_PS2_MSA; vgmstream->meta_type = meta_PS2_MSA;
/* MSAs are strangely truncated, so manually calculate samples /* MSAs are strangely truncated, so manually calculate samples.
* data after last usable block is always silence or garbage */ * Data after last usable block is always silence or garbage. */
if (datasize > filesize) { if (data_size > file_size) {
off_t usable_size = filesize - start_offset; off_t usable_size = file_size - start_offset;
usable_size -= usable_size % (vgmstream->interleave_block_size*channel_count);/* block-aligned */ 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)) if (!vgmstream_open_stream(vgmstream, streamFile, start_offset))
goto fail; goto fail;
return vgmstream; return vgmstream;

View File

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

View File

@ -44,9 +44,6 @@ VGMSTREAM * init_vgmstream_sps_n1(STREAMFILE *streamFile) {
goto fail; 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 */ vgmstream->sample_rate = sample_rate; /* .vag header doesn't match */
close_streamfile(temp_streamFile); close_streamfile(temp_streamFile);

View File

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

View File

@ -6,12 +6,6 @@
#include "vgmstream.h" #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 */ /* a STREAMFILE that operates via standard IO using a buffer */
typedef struct { typedef struct {
STREAMFILE sf; /* callbacks */ 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(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 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) { static size_t read_stdio(STDIOSTREAMFILE *streamfile,uint8_t * dest, off_t offset, size_t length) {
size_t length_read_total=0; 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? */ /* is the part of the requested length in the buffer? */
if (offset >= streamfile->offset && offset < streamfile->offset + streamfile->validsize) { 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; 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_to_read = streamfile->validsize - offset_into_buffer;
length_read_total += length_read; if (length_to_read > length)
length -= length_read; length_to_read = length;
offset += length_read;
dest += length_read; 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 /* 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 * The destination buffer is supposed to be much smaller than the
* STREAMFILE buffer, though. Maybe we should only ever return up * STREAMFILE buffer, though. Maybe we should only ever return up
* to the buffer size to avoid having to deal with things like this * 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 */ /* read the rest of the requested length */
while (length > 0) { while (length > 0) {
size_t length_to_read; size_t length_to_read, length_read;
size_t length_read;
streamfile->validsize = 0; /* buffer is empty now */ streamfile->validsize = 0; /* buffer is empty now */
/* request outside file: ignore to avoid seek/read */ /* request outside file: ignore to avoid seek/read */
if (offset > streamfile->filesize) { if (offset > streamfile->filesize) {
streamfile->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); 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 */ return length_read_total; /* partially-read buffer */
#endif
} }
/* position to new offset */ /* 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 we can't get enough to satisfy the request (EOF) we give up */
if (length_read < length_to_read) { if (length_read < length_to_read) {
memcpy(dest,streamfile->buffer,length_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 */ return length_read_total + length_read; /* partially-read buffer */
#endif
} }
/* use the new buffer */ /* 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; 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) { static void close_stdio(STDIOSTREAMFILE * streamfile) {
fclose(streamfile->infile); fclose(streamfile->infile);
free(streamfile->buffer); 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_size = (void*)get_size_stdio;
streamfile->sf.get_offset = (void*)get_offset_stdio; streamfile->sf.get_offset = (void*)get_offset_stdio;
streamfile->sf.get_name = (void*)get_name_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.open = (void*)open_stdio;
streamfile->sf.close = (void*)close_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 stream_index: copy? pass? funtion? external?
//todo use realnames on reopen? simplify? //todo use realnames on reopen? simplify?
//todo use safe string ops, this ain't easy //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) { static void wrap_get_name(WRAP_STREAMFILE *streamfile, char *buffer, size_t length) {
streamfile->inner_sf->get_name(streamfile->inner_sf, buffer, length); /* default */ 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) { 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) */ 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_size = (void*)wrap_get_size;
this_sf->sf.get_offset = (void*)wrap_get_offset; this_sf->sf.get_offset = (void*)wrap_get_offset;
this_sf->sf.get_name = (void*)wrap_get_name; 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.open = (void*)wrap_open;
this_sf->sf.close = (void*)wrap_close; this_sf->sf.close = (void*)wrap_close;
this_sf->sf.stream_index = streamfile->stream_index; 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) { static void clamp_get_name(CLAMP_STREAMFILE *streamfile, char *buffer, size_t length) {
streamfile->inner_sf->get_name(streamfile->inner_sf, buffer, length); /* default */ 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) { static STREAMFILE *clamp_open(CLAMP_STREAMFILE *streamfile, const char * const filename, size_t buffersize) {
char original_filename[PATH_LIMIT]; char original_filename[PATH_LIMIT];
STREAMFILE *new_inner_sf; 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_size = (void*)clamp_get_size;
this_sf->sf.get_offset = (void*)clamp_get_offset; this_sf->sf.get_offset = (void*)clamp_get_offset;
this_sf->sf.get_name = (void*)clamp_get_name; 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.open = (void*)clamp_open;
this_sf->sf.close = (void*)clamp_close; this_sf->sf.close = (void*)clamp_close;
this_sf->sf.stream_index = streamfile->stream_index; 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) { static void io_get_name(IO_STREAMFILE *streamfile, char *buffer, size_t length) {
streamfile->inner_sf->get_name(streamfile->inner_sf, buffer, length); /* default */ 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) { 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 //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); 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_size = (void*)io_get_size;
this_sf->sf.get_offset = (void*)io_get_offset; this_sf->sf.get_offset = (void*)io_get_offset;
this_sf->sf.get_name = (void*)io_get_name; 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.open = (void*)io_open;
this_sf->sf.close = (void*)io_close; this_sf->sf.close = (void*)io_close;
this_sf->sf.stream_index = streamfile->stream_index; 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); strncpy(buffer,streamfile->fakename,length);
buffer[length-1]='\0'; 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) { static STREAMFILE *fakename_open(FAKENAME_STREAMFILE *streamfile, const char * const filename, size_t buffersize) {
/* detect re-opening the file */ /* detect re-opening the file */
if (strcmp(filename, streamfile->fakename) == 0) { 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_size = (void*)fakename_get_size;
this_sf->sf.get_offset = (void*)fakename_get_offset; this_sf->sf.get_offset = (void*)fakename_get_offset;
this_sf->sf.get_name = (void*)fakename_get_name; 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.open = (void*)fakename_open;
this_sf->sf.close = (void*)fakename_close; this_sf->sf.close = (void*)fakename_close;
this_sf->sf.stream_index = streamfile->stream_index; 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) { 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); 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) { static STREAMFILE *multifile_open(MULTIFILE_STREAMFILE *streamfile, const char * const filename, size_t buffersize) {
char original_filename[PATH_LIMIT]; char original_filename[PATH_LIMIT];
STREAMFILE *new_sf = NULL; 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_size = (void*)multifile_get_size;
this_sf->sf.get_offset = (void*)multifile_get_offset; this_sf->sf.get_offset = (void*)multifile_get_offset;
this_sf->sf.get_name = (void*)multifile_get_name; 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.open = (void*)multifile_open;
this_sf->sf.close = (void*)multifile_close; this_sf->sf.close = (void*)multifile_close;
this_sf->sf.stream_index = streamfiles[0]->stream_index; this_sf->sf.stream_index = streamfiles[0]->stream_index;

View File

@ -51,8 +51,6 @@ typedef struct _STREAMFILE {
off_t (*get_offset)(struct _STREAMFILE *); off_t (*get_offset)(struct _STREAMFILE *);
/* for dual-file support */ /* for dual-file support */
void (*get_name)(struct _STREAMFILE *,char *name,size_t length); 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); struct _STREAMFILE * (*open)(struct _STREAMFILE *,const char * const filename,size_t buffersize);
void (*close)(struct _STREAMFILE *); void (*close)(struct _STREAMFILE *);
@ -70,6 +68,11 @@ STREAMFILE *open_stdio_streamfile(const char * filename);
/* Opens a standard STREAMFILE from a pre-opened FILE. */ /* Opens a standard STREAMFILE from a pre-opened FILE. */
STREAMFILE *open_stdio_streamfile_by_file(FILE * file, const char * filename); 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. /* 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). * 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. */ * 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_stm,
init_vgmstream_ea_snu, init_vgmstream_ea_snu,
init_vgmstream_awc, 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_al2,
init_vgmstream_pc_ast, init_vgmstream_pc_ast,
init_vgmstream_naac, init_vgmstream_naac,
@ -399,6 +402,9 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = {
init_vgmstream_msb_msh, init_vgmstream_msb_msh,
init_vgmstream_txtp, init_vgmstream_txtp,
init_vgmstream_smc_smh, 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) */ init_vgmstream_txth, /* should go at the end (lower priority) */
#ifdef VGM_USE_FFMPEG #ifdef VGM_USE_FFMPEG
@ -768,11 +774,14 @@ void close_vgmstream(VGMSTREAM * vgmstream) {
} }
if (vgmstream->coding_type == coding_NWA) { if (vgmstream->coding_type == coding_NWA) {
if (vgmstream->codec_data) {
nwa_codec_data *data = (nwa_codec_data *) vgmstream->codec_data; nwa_codec_data *data = (nwa_codec_data *) vgmstream->codec_data;
if (data->nwa)
close_nwa(data->nwa); close_nwa(data->nwa);
free(data); free(data);
vgmstream->codec_data = NULL; vgmstream->codec_data = NULL;
} }
}
if (vgmstream->layout_type==layout_aix) { if (vgmstream->layout_type==layout_aix) {
@ -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) * - 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). * - 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. * 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; 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 || if (vgmstream->layout_type == layout_aix ||
vgmstream->layout_type == layout_segmented || vgmstream->layout_type == layout_segmented ||
vgmstream->layout_type == layout_layered) vgmstream->layout_type == layout_layered)
return 1; return 1;
/* stream/offsets not needed, managed by decoder */
if (vgmstream->coding_type == coding_NWA)
return 1;
#ifdef VGM_USE_FFMPEG #ifdef VGM_USE_FFMPEG
/* stream/offsets not needed, FFmpeg manages itself */ /* stream/offsets not needed, managed by decoder */
if (vgmstream->coding_type == coding_FFmpeg) if (vgmstream->coding_type == coding_FFmpeg)
return 1; return 1;
#endif #endif

View File

@ -272,8 +272,8 @@ typedef enum {
meta_DSP_AGSC, /* Retro: Metroid Prime 2 title */ meta_DSP_AGSC, /* Retro: Metroid Prime 2 title */
meta_DSP_MPDSP, /* Monopoly Party single header stereo */ meta_DSP_MPDSP, /* Monopoly Party single header stereo */
meta_DSP_JETTERS, /* Bomberman Jetters .dsp */ meta_DSP_JETTERS, /* Bomberman Jetters .dsp */
meta_DSP_MSS, /* ? */ meta_DSP_MSS, /* Free Radical GC games */
meta_DSP_GCM, /* ? */ meta_DSP_GCM, /* some of Traveller's Tales games */
meta_DSP_STR, /* Conan .str files */ meta_DSP_STR, /* Conan .str files */
meta_DSP_SADB, /* .sad */ meta_DSP_SADB, /* .sad */
meta_DSP_WSI, /* .wsi */ meta_DSP_WSI, /* .wsi */
@ -627,7 +627,7 @@ typedef enum {
meta_BINK, /* RAD Game Tools BINK audio/video */ meta_BINK, /* RAD Game Tools BINK audio/video */
meta_EA_SNU, /* Electronic Arts SNU (Dead Space) */ meta_EA_SNU, /* Electronic Arts SNU (Dead Space) */
meta_AWC, /* Rockstar AWC (GTA5, RDR) */ 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_AL2, /* Conquest of Elysium 3 (PC) */
meta_PC_AST, /* Dead Rising (PC) */ meta_PC_AST, /* Dead Rising (PC) */
meta_NAAC, /* Namco AAC (3DS) */ meta_NAAC, /* Namco AAC (3DS) */
@ -670,6 +670,9 @@ typedef enum {
meta_OGG_ENO, /* Ogg Vorbis with encryption [Metronomicon (PC)] */ meta_OGG_ENO, /* Ogg Vorbis with encryption [Metronomicon (PC)] */
meta_TXTP, /* generic text playlist */ meta_TXTP, /* generic text playlist */
meta_SMC_SMH, /* Wangan Midnight (System 246) */ 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 #ifdef VGM_USE_FFMPEG
meta_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); 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) { static STREAMFILE *wasf_open(WINAMP_STREAMFILE *streamFile, const char *const filename, size_t buffersize) {
int newfd; int newfd;
FILE *newfile; 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_size = (void*)wasf_get_size;
this_sf->sf.get_offset = (void*)wasf_get_offset; this_sf->sf.get_offset = (void*)wasf_get_offset;
this_sf->sf.get_name = (void*)wasf_get_name; 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.open = (void*)wasf_open;
this_sf->sf.close = (void*)wasf_close; 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_size = (void*)xmpsf_get_size;
streamfile->sf.get_offset = (void*)xmpsf_get_offset; streamfile->sf.get_offset = (void*)xmpsf_get_offset;
streamfile->sf.get_name = (void*)xmpsf_get_name; streamfile->sf.get_name = (void*)xmpsf_get_name;
streamfile->sf.get_realname = (void*)xmpsf_get_name;
streamfile->sf.open = (void*)xmpsf_open; streamfile->sf.open = (void*)xmpsf_open;
streamfile->sf.close = (void*)xmpsf_close; streamfile->sf.close = (void*)xmpsf_close;
streamfile->infile = infile; streamfile->infile = infile;