mirror of
https://github.com/vgmstream/vgmstream.git
synced 2025-01-31 04:13:47 +01:00
commit
4ea0d1a9d7
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
@ -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"},
|
||||||
|
@ -1,34 +1,42 @@
|
|||||||
#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) {
|
||||||
|
@ -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"
|
||||||
>
|
>
|
||||||
|
@ -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" />
|
||||||
|
@ -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">
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
240
src/meta/bfwav.c
240
src/meta/bfwav.c
@ -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;
|
||||||
int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL;
|
size_t interleave = 0;
|
||||||
int16_t (*read_16bit)(off_t,STREAMFILE*) = NULL;
|
int32_t (*read_32bit)(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;
|
|
||||||
|
|
||||||
/* check extension, case insensitive */
|
|
||||||
streamFile->get_name(streamFile, filename, sizeof(filename));
|
|
||||||
if (strcasecmp("bfwav", filename_extension(filename)) && strcasecmp("fwav", filename_extension(filename))) {
|
|
||||||
if (strcasecmp("bfwavnsmbu",filename_extension(filename))) goto fail;
|
|
||||||
else nsmbu_flag = 1;
|
|
||||||
}
|
|
||||||
/* check header */
|
|
||||||
if ((uint32_t)read_32bitBE(0, streamFile) != 0x46574156) /* "FWAV" */
|
|
||||||
goto fail;
|
|
||||||
|
|
||||||
if ((uint16_t)read_16bitBE(4, streamFile) == 0xFEFF) { /* endian marker (BE most common) */
|
|
||||||
read_32bit = read_32bitBE;
|
|
||||||
read_16bit = read_16bitBE;
|
|
||||||
coding_PCM16 = coding_PCM16BE;
|
|
||||||
} else if ((uint16_t)read_16bitBE(4, streamFile) == 0xFFFE) { /* LE endian marker */
|
|
||||||
read_32bit = read_32bitLE;
|
|
||||||
read_16bit = read_16bitLE;
|
|
||||||
coding_PCM16 = coding_PCM16LE;
|
|
||||||
} else {
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* get head offset, check */
|
|
||||||
head_offset = read_32bit(0x18, streamFile);
|
|
||||||
data_offset = read_32bit(0x24, streamFile);
|
|
||||||
|
|
||||||
if ((uint32_t)read_32bitBE(head_offset, streamFile) != 0x494E464F) /* "INFO" (FWAV)*/
|
|
||||||
goto fail;
|
|
||||||
|
|
||||||
|
|
||||||
/* check type details */
|
|
||||||
codec_number = read_8bit(head_offset + 0x8, streamFile);
|
|
||||||
loop_flag = read_8bit(head_offset + 0x9, streamFile);
|
|
||||||
channel_count = read_32bit(head_offset + 0x1C, streamFile);
|
|
||||||
|
|
||||||
switch (codec_number) {
|
|
||||||
case 0:
|
|
||||||
coding_type = coding_PCM8;
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
coding_type = coding_PCM16;
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
coding_type = coding_NGC_DSP;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (channel_count < 1) goto fail;
|
|
||||||
|
|
||||||
/* build the VGMSTREAM */
|
|
||||||
|
|
||||||
vgmstream = allocate_vgmstream(channel_count, loop_flag);
|
|
||||||
if (!vgmstream) goto fail;
|
|
||||||
|
|
||||||
/* fill in the vital statistics */
|
|
||||||
vgmstream->num_samples = read_32bit(head_offset + 0x14, streamFile);
|
|
||||||
if (nsmbu_flag)
|
|
||||||
vgmstream->sample_rate = 16000;
|
|
||||||
else
|
|
||||||
vgmstream->sample_rate = (uint16_t)read_32bit(head_offset + 0xC, streamFile);
|
|
||||||
/* channels and loop flag are set by allocate_vgmstream */
|
|
||||||
|
|
||||||
vgmstream->loop_start_sample = read_32bit(head_offset + 0x10, streamFile);
|
|
||||||
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;
|
/* checks */
|
||||||
|
/* .bfwavnsmbu: fake extension to detect New Super Mario Bros U files with weird sample rate */
|
||||||
|
if (!check_extensions(streamFile, "bfwav,fwav,bfwavnsmbu"))
|
||||||
|
goto fail;
|
||||||
|
nsmbu_flag = check_extensions(streamFile, "bfwavnsmbu");
|
||||||
|
|
||||||
vgmstream->interleave_block_size = read_32bit(read_32bit(0x6c, streamFile) + 0x60, streamFile) - 0x18;
|
/* 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) */
|
||||||
|
|
||||||
start_offset = data_offset + 0x20;
|
if ((uint16_t)read_16bitBE(0x04, streamFile) == 0xFEFF) { /* BE BOM check */
|
||||||
|
read_32bit = read_32bitBE;
|
||||||
|
read_16bit = read_16bitBE;
|
||||||
|
big_endian = 1;
|
||||||
|
} else if ((uint16_t)read_16bitBE(0x04, streamFile) == 0xFFFE) { /* LE BOM check */
|
||||||
|
read_32bit = read_32bitLE;
|
||||||
|
read_16bit = read_16bitLE;
|
||||||
|
big_endian = 0;
|
||||||
|
} else {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
if (vgmstream->coding_type == coding_NGC_DSP) {
|
info_offset = read_32bit(0x18, streamFile); /* 0x14(2): info mark (0x7000), 0x1c: info size */
|
||||||
int i, j;
|
data_offset = read_32bit(0x24, streamFile); /* 0x20(2): data mark (0x7001), 0x28: data size */
|
||||||
|
|
||||||
for (j = 0; j<vgmstream->channels; j++) {
|
/* INFO section */
|
||||||
for (i = 0; i<16; i++) {
|
if (read_32bitBE(info_offset, streamFile) != 0x494E464F) /* "INFO" */
|
||||||
off_t coeffheader = head_offset + 0x1C + read_32bit(head_offset + 0x24 + (j*8), streamFile);
|
goto fail;
|
||||||
off_t coef_offset;
|
codec = read_8bit(info_offset + 0x08, streamFile);
|
||||||
if ((uint32_t)read_16bit(coeffheader, streamFile) != 0x1F00) goto fail;
|
loop_flag = read_8bit(info_offset + 0x09, streamFile);
|
||||||
|
channel_count = read_32bit(info_offset + 0x1C, streamFile);
|
||||||
|
|
||||||
coef_offset = read_32bit(coeffheader + 0xC, streamFile) + coeffheader;
|
/* parse channel table */
|
||||||
vgmstream->ch[j].adpcm_coef[i] = read_16bit(coef_offset + i * 2, streamFile);
|
{
|
||||||
}
|
off_t channel1_info, data_start;
|
||||||
}
|
int i;
|
||||||
}
|
|
||||||
|
channel1_info = info_offset + 0x1c + read_32bit(info_offset+0x20+0x08*0+0x04, streamFile);
|
||||||
|
data_start = read_32bit(channel1_info+0x04, streamFile); /* within "DATA" after 0x08 */
|
||||||
|
|
||||||
|
/* channels use absolute offsets but should be ok as interleave */
|
||||||
|
interleave = 0;
|
||||||
|
if (channel_count > 1) {
|
||||||
|
off_t channel2_info = info_offset + 0x1c + read_32bit(info_offset+0x20+0x08*1+0x04, streamFile);
|
||||||
|
interleave = read_32bit(channel2_info+0x04, streamFile) - data_start;
|
||||||
|
}
|
||||||
|
|
||||||
|
start_offset = data_offset + 0x08 + data_start;
|
||||||
|
|
||||||
|
/* validate all channels just in case of multichannel with non-constant interleave */
|
||||||
|
for (i = 0; i < channel_count; i++) {
|
||||||
|
/* channel table, 0x00: flag (0x7100), 0x04: channel info offset */
|
||||||
|
off_t channel_info = info_offset + 0x1c + read_32bit(info_offset+0x20+0x08*i+0x04, streamFile);
|
||||||
|
/* channel info, 0x00(2): flag (0x1f00), 0x04: offset, 0x08(2): ADPCM flag (0x0300), 0x0c: ADPCM offset */
|
||||||
|
if ((uint16_t)read_16bit(channel_info+0x00, streamFile) != 0x1F00)
|
||||||
|
goto fail;
|
||||||
|
if (read_32bit(channel_info+0x04, streamFile) != data_start + interleave*i)
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* build the VGMSTREAM */
|
||||||
|
vgmstream = allocate_vgmstream(channel_count, loop_flag);
|
||||||
|
if (!vgmstream) goto fail;
|
||||||
|
|
||||||
/* open the file for reading by each channel */
|
vgmstream->sample_rate = read_32bit(info_offset + 0x0C, streamFile);
|
||||||
{
|
if (nsmbu_flag)
|
||||||
int i;
|
vgmstream->sample_rate = 16000;
|
||||||
for (i = 0; i<channel_count; i++) {
|
|
||||||
if (vgmstream->layout_type == layout_interleave)
|
|
||||||
vgmstream->ch[i].streamfile = streamFile->open(streamFile, filename,STREAMFILE_DEFAULT_BUFFER_SIZE);
|
|
||||||
else
|
|
||||||
vgmstream->ch[i].streamfile = streamFile->open(streamFile, filename,0x1000);
|
|
||||||
|
|
||||||
if (!vgmstream->ch[i].streamfile) goto fail;
|
vgmstream->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->ch[i].channel_start_offset =
|
vgmstream->meta_type = meta_FWAV;
|
||||||
vgmstream->ch[i].offset =
|
vgmstream->layout_type = (channel_count == 1) ? layout_none : layout_interleave;
|
||||||
start_offset + i*vgmstream->interleave_block_size;
|
vgmstream->interleave_block_size = interleave;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return vgmstream;
|
switch (codec) {
|
||||||
|
case 0x00:
|
||||||
|
vgmstream->coding_type = coding_PCM8;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0x01:
|
||||||
|
vgmstream->coding_type = big_endian ? coding_PCM16BE : coding_PCM16LE;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0x02:
|
||||||
|
vgmstream->coding_type = coding_NGC_DSP;
|
||||||
|
{
|
||||||
|
int i, c;
|
||||||
|
off_t coef_header, coef_offset;
|
||||||
|
|
||||||
|
for (i = 0; i < vgmstream->channels; i++) {
|
||||||
|
for (c = 0; c < 16; c++) {
|
||||||
|
coef_header = info_offset + 0x1C + read_32bit(info_offset + 0x24 + (i*0x08), streamFile);
|
||||||
|
coef_offset = read_32bit(coef_header + 0x0c, streamFile) + coef_header;
|
||||||
|
vgmstream->ch[i].adpcm_coef[c] = read_16bit(coef_offset + c*2, streamFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default: /* 0x03: IMA? */
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
|
||||||
|
goto fail;
|
||||||
|
return vgmstream;
|
||||||
|
|
||||||
/* clean up anything we may have opened */
|
|
||||||
fail:
|
fail:
|
||||||
if (vgmstream) close_vgmstream(vgmstream);
|
close_vgmstream(vgmstream);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
@ -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:
|
||||||
|
@ -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_ */
|
|
218
src/meta/ea_eaac_streamfile.h
Normal file
218
src/meta/ea_eaac_streamfile.h
Normal 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_ */
|
@ -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;
|
||||||
|
@ -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]);
|
||||||
|
@ -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_*/
|
||||||
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
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;
|
dsp_read_coefs_be(vgmstream,streamHeader,0x40,0x40);
|
||||||
|
|
||||||
|
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
|
@ -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*/
|
||||||
|
@ -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);
|
||||||
|
@ -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" */
|
goto fail;
|
||||||
(read_32bitBE(0x04,streamFile) != 0x41445043)) /* "M_v0" */
|
if (read_32bitBE(0x00,streamFile) != 0x41445043 || /* "ADPC" */
|
||||||
goto fail;
|
read_32bitBE(0x04,streamFile) != 0x4D5F7630) /* "M_v0" */
|
||||||
#endif
|
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;
|
||||||
/* build the VGMSTREAM */
|
data_size = read_32bitLE(0x10,streamFile) * 0x100; /* data has padding */
|
||||||
|
|
||||||
|
/* 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;
|
||||||
}
|
}
|
||||||
|
@ -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
@ -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;
|
||||||
|
@ -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;
|
|
||||||
}
|
|
543
src/meta/nwa.c
543
src/meta/nwa.c
@ -4,296 +4,88 @@
|
|||||||
#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;
|
||||||
)
|
} else {
|
||||||
{
|
compression_level = read_32bitLE(0x08,streamFile);
|
||||||
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 */
|
/* loop points come from external files */
|
||||||
{
|
nwainfo_ini_found = get_loops_nwainfo_ini(streamFile, &loop_flag, &loop_start_sample);
|
||||||
char ininame[PATH_LIMIT];
|
gameexe_ini_found = !nwainfo_ini_found && get_loops_gameexe_ini(streamFile, &loop_flag, &loop_start_sample, &loop_end_sample);
|
||||||
char * ini_lastslash;
|
|
||||||
char namebase_array[PATH_LIMIT];
|
|
||||||
char *namebase;
|
|
||||||
STREAMFILE *inistreamfile;
|
|
||||||
|
|
||||||
/* here we assume that the "special encoding" does not affect
|
start_offset = 0x2c;
|
||||||
* the directory separator */
|
|
||||||
strncpy(ininame,filename,sizeof(ininame));
|
|
||||||
ininame[sizeof(ininame)-1]='\0'; /* a pox on the stdlib! */
|
|
||||||
|
|
||||||
streamFile->get_realname(streamFile,namebase_array,sizeof(namebase_array));
|
|
||||||
|
|
||||||
ini_lastslash = strrchr(ininame,DIRSEP);
|
|
||||||
if (!ini_lastslash) {
|
|
||||||
strncpy(ininame,"NWAINFO.INI",sizeof(ininame));
|
|
||||||
namebase = namebase_array;
|
|
||||||
} else {
|
|
||||||
strncpy(ini_lastslash+1,"NWAINFO.INI",
|
|
||||||
sizeof(ininame)-(ini_lastslash+1-ininame));
|
|
||||||
namebase = strrchr(namebase_array,DIRSEP)+1;
|
|
||||||
}
|
|
||||||
ininame[sizeof(ininame)-1]='\0'; /* curse you, strncpy! */
|
|
||||||
|
|
||||||
inistreamfile = streamFile->open(streamFile,ininame,4096);
|
|
||||||
|
|
||||||
if (inistreamfile) {
|
|
||||||
/* ini found, try to find our name */
|
|
||||||
const char * ext;
|
|
||||||
int length;
|
|
||||||
int found;
|
|
||||||
off_t offset;
|
|
||||||
off_t file_size;
|
|
||||||
off_t found_off = -1;
|
|
||||||
|
|
||||||
nwainfo_ini_found = 1;
|
|
||||||
|
|
||||||
ext = filename_extension(namebase);
|
|
||||||
length = ext-1-namebase;
|
|
||||||
file_size = get_streamfile_size(inistreamfile);
|
|
||||||
|
|
||||||
for (found = 0, offset = 0; !found && offset<file_size; offset++) {
|
|
||||||
off_t suboffset;
|
|
||||||
/* Go for an n*m search 'cause it's easier than building an
|
|
||||||
* FSA for the search string. Just wanted to make the point that
|
|
||||||
* I'm not ignorant, just lazy. */
|
|
||||||
for (suboffset = offset;
|
|
||||||
suboffset<file_size &&
|
|
||||||
suboffset-offset<length &&
|
|
||||||
read_8bit(suboffset,inistreamfile)==
|
|
||||||
namebase[suboffset-offset];
|
|
||||||
suboffset++) {}
|
|
||||||
|
|
||||||
if (suboffset-offset==length &&
|
|
||||||
read_8bit(suboffset,inistreamfile)==0x09) { /* tab */
|
|
||||||
found=1;
|
|
||||||
found_off = suboffset+1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (found) {
|
|
||||||
char loopstring[9]={0};
|
|
||||||
|
|
||||||
if (read_streamfile((uint8_t*)loopstring,found_off,8,
|
|
||||||
inistreamfile)==8)
|
|
||||||
{
|
|
||||||
loop_start_sample = atol(loopstring);
|
|
||||||
if (loop_start_sample > 0) loop_flag = 1;
|
|
||||||
}
|
|
||||||
} /* if found file name in INI */
|
|
||||||
|
|
||||||
close_streamfile(inistreamfile);
|
|
||||||
} /* if opened INI ok */
|
|
||||||
} /* INI block */
|
|
||||||
|
|
||||||
/* try to locate Gameexe.ini in the same directory */
|
|
||||||
{
|
|
||||||
char ininame[PATH_LIMIT];
|
|
||||||
char * ini_lastslash;
|
|
||||||
char namebase_array[PATH_LIMIT];
|
|
||||||
char * namebase;
|
|
||||||
STREAMFILE *inistreamfile;
|
|
||||||
|
|
||||||
strncpy(ininame,filename,sizeof(ininame));
|
|
||||||
ininame[sizeof(ininame)-1]='\0'; /* a pox on the stdlib! */
|
|
||||||
|
|
||||||
streamFile->get_realname(streamFile,namebase_array,sizeof(namebase_array));
|
|
||||||
|
|
||||||
ini_lastslash = strrchr(ininame,DIRSEP);
|
|
||||||
if (!ini_lastslash) {
|
|
||||||
strncpy(ininame,"Gameexe.ini",sizeof(ininame));
|
|
||||||
namebase = namebase_array;
|
|
||||||
} else {
|
|
||||||
strncpy(ini_lastslash+1,"Gameexe.ini",
|
|
||||||
sizeof(ininame)-(ini_lastslash+1-ininame));
|
|
||||||
namebase = strrchr(namebase_array,DIRSEP)+1;
|
|
||||||
}
|
|
||||||
ininame[sizeof(ininame)-1]='\0'; /* curse you, strncpy! */
|
|
||||||
|
|
||||||
inistreamfile = streamFile->open(streamFile,ininame,4096);
|
|
||||||
|
|
||||||
if (inistreamfile) {
|
|
||||||
/* ini found, try to find our name */
|
|
||||||
const char * ext;
|
|
||||||
int length;
|
|
||||||
int found;
|
|
||||||
off_t offset;
|
|
||||||
off_t file_size;
|
|
||||||
off_t found_off = -1;
|
|
||||||
|
|
||||||
gameexe_ini_found = 1;
|
|
||||||
|
|
||||||
ext = filename_extension(namebase);
|
|
||||||
length = ext-1-namebase;
|
|
||||||
file_size = get_streamfile_size(inistreamfile);
|
|
||||||
|
|
||||||
/* format of line is:
|
|
||||||
* #DSTRACK = 00000000 - eeeeeeee - ssssssss = "name" = "name2?"
|
|
||||||
* ^22 ^33 ^45 ^57
|
|
||||||
*/
|
|
||||||
|
|
||||||
for (found = 0, offset = 0; !found && offset<file_size; offset++) {
|
|
||||||
off_t suboffset;
|
|
||||||
uint8_t buf[10];
|
|
||||||
|
|
||||||
if (read_8bit(offset,inistreamfile)!='#') continue;
|
|
||||||
if (read_streamfile(buf,offset+1,10,inistreamfile)!=10) break;
|
|
||||||
if (memcmp("DSTRACK = ",buf,10)) continue;
|
|
||||||
if (read_8bit(offset+44,inistreamfile)!='\"') continue;
|
|
||||||
|
|
||||||
for (suboffset = offset+45;
|
|
||||||
suboffset<file_size &&
|
|
||||||
suboffset-offset-45<length &&
|
|
||||||
tolower(read_8bit(suboffset,inistreamfile))==
|
|
||||||
tolower(namebase[suboffset-offset-45]);
|
|
||||||
suboffset++) {}
|
|
||||||
|
|
||||||
if (suboffset-offset-45==length &&
|
|
||||||
read_8bit(suboffset,inistreamfile)=='\"') { /* tab */
|
|
||||||
found=1;
|
|
||||||
found_off = offset+22; /* loop end */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (found) {
|
|
||||||
char loopstring[9]={0};
|
|
||||||
int start_ok = 0, end_ok = 0;
|
|
||||||
int32_t total_samples =
|
|
||||||
read_32bitLE(0x1c,streamFile)/channel_count;
|
|
||||||
|
|
||||||
if (read_streamfile((uint8_t*)loopstring,found_off,8,
|
|
||||||
inistreamfile)==8)
|
|
||||||
{
|
|
||||||
if (!memcmp("99999999",loopstring,8))
|
|
||||||
{
|
|
||||||
loop_end_sample = total_samples;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
loop_end_sample = atol(loopstring);
|
|
||||||
}
|
|
||||||
end_ok = 1;
|
|
||||||
}
|
|
||||||
if (read_streamfile((uint8_t*)loopstring,found_off+11,8,
|
|
||||||
inistreamfile)==8)
|
|
||||||
{
|
|
||||||
if (!memcmp("99999999",loopstring,8))
|
|
||||||
{
|
|
||||||
/* not ok to start at last sample,
|
|
||||||
* don't set start_ok flag */
|
|
||||||
}
|
|
||||||
else if (!memcmp("00000000",loopstring,8))
|
|
||||||
{
|
|
||||||
/* loops from the start aren't really loops */
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
loop_start_sample = atol(loopstring);
|
|
||||||
start_ok = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (start_ok && end_ok) loop_flag = 1;
|
|
||||||
} /* if found file name in INI */
|
|
||||||
|
|
||||||
close_streamfile(inistreamfile);
|
|
||||||
} /* if opened INI ok */
|
|
||||||
} /* INI block */
|
|
||||||
|
|
||||||
/* 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 = 0x01;
|
||||||
vgmstream->interleave_block_size = 1;
|
break;
|
||||||
break;
|
case 16:
|
||||||
case 16:
|
vgmstream->coding_type = coding_PCM16LE;
|
||||||
vgmstream->coding_type = coding_PCM16LE;
|
vgmstream->interleave_block_size = 0x02;
|
||||||
vgmstream->interleave_block_size = 2;
|
break;
|
||||||
break;
|
default:
|
||||||
default:
|
goto fail;
|
||||||
goto fail;
|
}
|
||||||
}
|
|
||||||
if (channel_count > 1) {
|
|
||||||
vgmstream->layout_type = layout_interleave;
|
vgmstream->layout_type = layout_interleave;
|
||||||
} else {
|
break;
|
||||||
|
|
||||||
|
case 0:
|
||||||
|
case 1:
|
||||||
|
case 2:
|
||||||
|
case 3:
|
||||||
|
case 4:
|
||||||
|
case 5:
|
||||||
|
vgmstream->coding_type = coding_NWA;
|
||||||
vgmstream->layout_type = layout_none;
|
vgmstream->layout_type = layout_none;
|
||||||
}
|
vgmstream->codec_data = open_nwa_vgmstream(streamFile);
|
||||||
}
|
if (!vgmstream->codec_data) goto fail;
|
||||||
else
|
break;
|
||||||
{
|
|
||||||
switch (comp_level)
|
default:
|
||||||
{
|
goto fail;
|
||||||
case 0:
|
break;
|
||||||
case 1:
|
|
||||||
case 2:
|
|
||||||
case 3:
|
|
||||||
case 4:
|
|
||||||
case 5:
|
|
||||||
vgmstream->coding_type = coding_NWA;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
goto fail;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
vgmstream->layout_type = layout_none;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (nwainfo_ini_found) {
|
if (nwainfo_ini_found) {
|
||||||
vgmstream->meta_type = meta_NWA_NWAINFOINI;
|
vgmstream->meta_type = meta_NWA_NWAINFOINI;
|
||||||
if (loop_flag) {
|
if (loop_flag) {
|
||||||
@ -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;
|
|
||||||
}
|
}
|
||||||
|
@ -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,17 +125,14 @@ static void sngw_ogg_decryption_callback(void *ptr, size_t size, size_t nmemb, v
|
|||||||
|
|
||||||
put_32bitBE(key, ov_streamfile->xor_value);
|
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) {
|
((uint8_t*)ptr)[i] = (uint8_t)header_id[(ov_streamfile->offset + i) % 4];
|
||||||
/* replace key in the first 4 bytes with "OggS" */
|
}
|
||||||
((uint8_t*)ptr)[i] = (uint8_t)header_id[(ov_streamfile->offset + i) % 4];
|
else {
|
||||||
}
|
uint8_t val = ((uint8_t*)ptr)[i] ^ key[(ov_streamfile->offset + i) % 4];
|
||||||
else {
|
((uint8_t*)ptr)[i] = ((val << 4) & 0xf0) | ((val >> 4) & 0x0f);
|
||||||
uint8_t val = ((uint8_t*)ptr)[i] ^ key[(ov_streamfile->offset + i) % 4];
|
|
||||||
((uint8_t*)ptr)[i] = ((val << 4) & 0xf0) | ((val >> 4) & 0x0f);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -150,9 +146,8 @@ static void isd_ogg_decryption_callback(void *ptr, size_t size, size_t nmemb, vo
|
|||||||
int i;
|
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,16 +157,14 @@ 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" */
|
((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 {
|
break;
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -210,6 +203,17 @@ static void eno_ogg_decryption_callback(void *ptr, size_t size, size_t nmemb, vo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void ys8_ogg_decryption_callback(void *ptr, size_t size, size_t nmemb, void *datasource) {
|
||||||
|
size_t bytes_read = size*nmemb;
|
||||||
|
ogg_vorbis_streamfile * const ov_streamfile = datasource;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
/* bytes are xor'd with key and nibble-swapped */
|
||||||
|
for (i = 0; i < bytes_read; i++) {
|
||||||
|
uint8_t val = ((uint8_t*)ptr)[i] ^ ov_streamfile->xor_value;
|
||||||
|
((uint8_t*)ptr)[i] = ((val << 4) & 0xf0) | ((val >> 4) & 0x0f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Ogg Vorbis, by way of libvorbisfile; may contain loop comments */
|
/* 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
235
src/meta/opus.c
Normal 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;
|
||||||
|
}
|
146
src/meta/opus_interleave_streamfile.h
Normal file
146
src/meta/opus_interleave_streamfile.h
Normal 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
120
src/meta/opus_ppp.c
Normal 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;
|
||||||
|
}
|
@ -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
60
src/meta/ppst.c
Normal 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
101
src/meta/ppst_streamfile.h
Normal 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_ */
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
@ -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:
|
||||||
|
@ -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);
|
||||||
|
@ -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;
|
||||||
|
241
src/streamfile.c
241
src/streamfile.c
@ -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;
|
||||||
|
@ -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. */
|
||||||
|
@ -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,10 +774,13 @@ void close_vgmstream(VGMSTREAM * vgmstream) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (vgmstream->coding_type == coding_NWA) {
|
if (vgmstream->coding_type == coding_NWA) {
|
||||||
nwa_codec_data *data = (nwa_codec_data *) vgmstream->codec_data;
|
if (vgmstream->codec_data) {
|
||||||
close_nwa(data->nwa);
|
nwa_codec_data *data = (nwa_codec_data *) vgmstream->codec_data;
|
||||||
free(data);
|
if (data->nwa)
|
||||||
vgmstream->codec_data = NULL;
|
close_nwa(data->nwa);
|
||||||
|
free(data);
|
||||||
|
vgmstream->codec_data = NULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -2516,7 +2525,7 @@ int get_vgmstream_average_bitrate(VGMSTREAM * vgmstream) {
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Inits vgmstreams' channels doing two things:
|
* Inits vgmstream, doing two things:
|
||||||
* - sets the starting offset per channel (depending on the layout)
|
* - 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
|
||||||
|
@ -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,
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user