mirror of
https://github.com/vgmstream/vgmstream.git
synced 2024-11-24 15:00:11 +01:00
commit
968349fa1c
@ -579,7 +579,7 @@ Console location and format depends on plugin:
|
||||
- *Audacious*: start with `audacious -V` from terminal
|
||||
- CLI utils: printed to stdout directly
|
||||
|
||||
Only a few errors are printed ATM but may be helpful for more common cases.
|
||||
Only a few errors types are printed but may be helpful for more common cases.
|
||||
|
||||
## Tagging
|
||||
Some of vgmstream's plugins support simple read-only tagging via external files.
|
||||
|
@ -119,7 +119,7 @@ static const char* extension_list[] = {
|
||||
"brstm",
|
||||
"brstmspm",
|
||||
"brwav",
|
||||
"brwsd",
|
||||
"brwsd", //fake extension for RWSD (non-format)
|
||||
"bsnd",
|
||||
"btsnd",
|
||||
"bvg",
|
||||
@ -1225,7 +1225,8 @@ static const meta_info meta_info_list[] = {
|
||||
{meta_HCA, "CRI HCA header"},
|
||||
{meta_SVAG_SNK, "SNK SVAG header"},
|
||||
{meta_PS2_VDS_VDM, "Procyon Studio VDS/VDM header"},
|
||||
{meta_FFMPEG, "FFmpeg supported file format"},
|
||||
{meta_FFMPEG, "FFmpeg supported format"},
|
||||
{meta_FFMPEG_faulty, "FFmpeg supported format (check log)"},
|
||||
{meta_X360_CXS, "tri-Crescendo CXS header"},
|
||||
{meta_AKB, "Square-Enix AKB header"},
|
||||
{meta_X360_PASX, "Premium Agency PASX header"},
|
||||
|
@ -203,6 +203,7 @@
|
||||
<ClCompile Include="meta\bcstm.c" />
|
||||
<ClCompile Include="meta\bfstm.c" />
|
||||
<ClCompile Include="meta\bfwav.c" />
|
||||
<ClCompile Include="meta\bw_mp3_riff.c" />
|
||||
<ClCompile Include="meta\bwav.c" />
|
||||
<ClCompile Include="meta\esf.c" />
|
||||
<ClCompile Include="meta\excitebots.c" />
|
||||
|
@ -1948,6 +1948,9 @@
|
||||
<ClCompile Include="meta\bwav.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\bw_mp3_riff.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\rad.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
|
@ -16,6 +16,7 @@ typedef struct {
|
||||
int segment_count;
|
||||
int layer_count;
|
||||
|
||||
int force_disable_loop;
|
||||
} aix_header_t;
|
||||
|
||||
static VGMSTREAM* build_segmented_vgmstream(STREAMFILE* sf, aix_header_t* aix);
|
||||
@ -74,13 +75,14 @@ VGMSTREAM* init_vgmstream_aix(STREAMFILE* sf) {
|
||||
goto fail;
|
||||
|
||||
/* Metroid: Other M (Wii)'s bgm_m_stage_06_02 is truncated on disc, seemingly not an extraction error.
|
||||
* Playing expected samples aligns to bgm_m_stage_06, so not sure how the game would actually play it,
|
||||
* tweaking samples to max size sounds a bit abrupt. */
|
||||
* Playing expected samples aligns to bgm_m_stage_06, but from tests seems the song stops once reaching
|
||||
* the missing audio (doesn't loop). */
|
||||
if (aix.segment_count == 3 && aix.segment_offsets[1] + aix.segment_sizes[1] > get_streamfile_size(sf)) {
|
||||
aix.segment_count = 2;
|
||||
|
||||
aix.segment_sizes[1] = get_streamfile_size(sf) - aix.segment_offsets[1];
|
||||
//aix.segment_samples[1] = 0;
|
||||
aix.force_disable_loop = 1; /* force */
|
||||
vgm_logi("AIX: missing data, parts will be silent\n");
|
||||
}
|
||||
}
|
||||
@ -209,7 +211,7 @@ static VGMSTREAM* build_segmented_vgmstream(STREAMFILE* sf, aix_header_t* aix) {
|
||||
if (!setup_layout_segmented(data))
|
||||
goto fail;
|
||||
|
||||
/* known loop cases:
|
||||
/* known loop cases (no info on header, controlled by game?):
|
||||
* - 1 segment: main/no loop [Hatsune Miku: Project Diva (PSP)]
|
||||
* - 2 segments: intro + loop [SoulCalibur IV (PS3)]
|
||||
* - 3 segments: intro + loop + end [Dragon Ball Z: Burst Limit (PS3), Metroid: Other M (Wii)]
|
||||
@ -218,6 +220,8 @@ static VGMSTREAM* build_segmented_vgmstream(STREAMFILE* sf, aix_header_t* aix) {
|
||||
loop_flag = (aix->segment_count > 0 && aix->segment_count <= 5);
|
||||
loop_start_segment = (aix->segment_count > 3) ? 2 : 1;
|
||||
loop_end_segment = (aix->segment_count > 3) ? (aix->segment_count - 2) : 1;
|
||||
if (aix->force_disable_loop)
|
||||
loop_flag = 0;
|
||||
|
||||
/* build the segmented VGMSTREAM */
|
||||
vgmstream = allocate_segmented_vgmstream(data, loop_flag, loop_start_segment, loop_end_segment);
|
||||
|
@ -7,7 +7,7 @@ VGMSTREAM* init_vgmstream_atsl(STREAMFILE* sf) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
STREAMFILE* temp_sf = NULL;
|
||||
int total_subsongs, target_subsong = sf->stream_index;
|
||||
int type, big_endian = 0, entries;
|
||||
int type, version, big_endian = 0, entries;
|
||||
uint32_t subfile_offset = 0, subfile_size = 0, header_size, entry_size;
|
||||
|
||||
init_vgmstream_t init_vgmstream = NULL;
|
||||
@ -42,12 +42,14 @@ VGMSTREAM* init_vgmstream_atsl(STREAMFILE* sf) {
|
||||
* - 01000000 01010501 .atsl from Nioh (PC)[KOVS] 2017-11
|
||||
* - 01000000 00010501 .atsl from Nioh (PC)[KOVS] 2017-11
|
||||
* - 000B0301 00080601 .atsl in G1l from Sengoku Musou Sanada Maru (Switch)[KTSS] 2017-09
|
||||
* - 03070301 01060301 .atsl from Nioh (PS4)[ATRAC9]
|
||||
* - 00090301 01060501 .atsl from Nioh (PS4)[ATRAC9]-bigger header
|
||||
* - 010C0301 01060601 .atsl from Dynasty Warriors 9 (PS4)[KTAC]
|
||||
* - 010D0301 01010601 .atsl from Dynasty Warriors 9 DLC (PC)[KOVS]
|
||||
*/
|
||||
|
||||
type = read_u16le(0x0c, sf);
|
||||
//version = read_u16le(0x0e, sf);
|
||||
version = read_u16le(0x0e, sf);
|
||||
switch(type) {
|
||||
case 0x0100: /* KOVS */
|
||||
init_vgmstream = init_vgmstream_ogg_vorbis;
|
||||
@ -71,10 +73,17 @@ VGMSTREAM* init_vgmstream_atsl(STREAMFILE* sf) {
|
||||
fake_ext = "at9";
|
||||
entry_size = 0x28;
|
||||
break;
|
||||
case 0x0601: /* KTAC */
|
||||
init_vgmstream = init_vgmstream_ktac;
|
||||
fake_ext = "ktac";
|
||||
entry_size = 0x3c;
|
||||
case 0x0601: /* KTAC / ATRAC9 */
|
||||
if (version == 0x0103 || version == 0x0105) {
|
||||
init_vgmstream = init_vgmstream_riff;
|
||||
fake_ext = "at9";
|
||||
entry_size = 0x3c;
|
||||
}
|
||||
else {
|
||||
init_vgmstream = init_vgmstream_ktac;
|
||||
fake_ext = "ktac";
|
||||
entry_size = 0x3c;
|
||||
}
|
||||
break;
|
||||
case 0x0800: /* KTSS */
|
||||
init_vgmstream = init_vgmstream_ktss;
|
||||
|
75
src/meta/bw_mp3_riff.c
Normal file
75
src/meta/bw_mp3_riff.c
Normal file
@ -0,0 +1,75 @@
|
||||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
|
||||
|
||||
/* Bioware pseudo-format (for sfx) [Star Wars: Knights of the Old Republic 1/2 (PC/Switch/iOS)] */
|
||||
VGMSTREAM* init_vgmstream_bw_mp3_riff(STREAMFILE* sf) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
STREAMFILE* temp_sf = NULL;
|
||||
uint32_t subfile_offset = 0, subfile_size = 0;
|
||||
|
||||
|
||||
/* checks */
|
||||
if (read_u32be(0x00, sf) != 0xFFF360C4)
|
||||
goto fail;
|
||||
//if ((read_u32be(0x00, sf) & 0xFFF00000) != 0xFFF00000) /* no point to check other mp3s */
|
||||
// goto fail;
|
||||
if (!is_id32be(0x0d, sf, "LAME") && !is_id32be(0x1d6, sf, "RIFF"))
|
||||
goto fail;
|
||||
|
||||
/* strange mix of micro empty MP3 (LAME3.93, common header) + standard RIFF */
|
||||
|
||||
subfile_offset = 0x1d6;
|
||||
subfile_size = read_u32le(subfile_offset + 0x04, sf) + 0x08;
|
||||
|
||||
temp_sf = setup_subfile_streamfile(sf, subfile_offset, subfile_size, "wav");
|
||||
if (!temp_sf) goto fail;
|
||||
|
||||
/* init the VGMSTREAM */
|
||||
vgmstream = init_vgmstream_riff(temp_sf);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
close_streamfile(temp_sf);
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_streamfile(temp_sf);
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Bioware pseudo-format (for music) [Star Wars: Knights of the Old Republic 1/2 (PC/Switch/iOS)] */
|
||||
VGMSTREAM* init_vgmstream_bw_riff_mp3(STREAMFILE* sf) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
STREAMFILE* temp_sf = NULL;
|
||||
uint32_t subfile_offset = 0, subfile_size = 0;
|
||||
|
||||
|
||||
/* checks */
|
||||
if (!is_id32be(0x00, sf, "RIFF"))
|
||||
goto fail;
|
||||
if (read_u32le(0x04, sf) != 0x32)
|
||||
goto fail;
|
||||
if (get_streamfile_size(sf) <= 0x32 + 0x08) /* ? */
|
||||
goto fail;
|
||||
|
||||
/* strange mix of micro RIFF (with codec 0x01, "fact" and "data" size 0)) + standard MP3 (sometimes with ID3) */
|
||||
|
||||
subfile_offset = 0x3A;
|
||||
subfile_size = get_streamfile_size(sf) - subfile_offset;
|
||||
|
||||
temp_sf = setup_subfile_streamfile(sf, subfile_offset, subfile_size, "mp3");
|
||||
if (!temp_sf) goto fail;
|
||||
|
||||
/* init the VGMSTREAM */
|
||||
vgmstream = init_vgmstream_mpeg(temp_sf);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
close_streamfile(temp_sf);
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_streamfile(temp_sf);
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
@ -7,7 +7,6 @@ static int read_pos_file(uint8_t* buf, size_t bufsize, STREAMFILE* sf);
|
||||
static int find_meta_loops(ffmpeg_codec_data* data, int32_t* p_loop_start, int32_t* p_loop_end);
|
||||
|
||||
/* parses any format supported by FFmpeg and not handled elsewhere:
|
||||
* - MP3 (.mp3, .mus): Marc Ecko's Getting Up (PC)
|
||||
* - MPC (.mpc, mp+): Moonshine Runners (PC), Asphalt 7 (PC)
|
||||
* - FLAC (.flac): Warcraft 3 Reforged (PC), Call of Duty: Ghosts (PC)
|
||||
* - DUCK (.wav): Sonic Jam (SAT), Virtua Fighter 2 (SAT)
|
||||
@ -26,6 +25,7 @@ VGMSTREAM* init_vgmstream_ffmpeg(STREAMFILE* sf) {
|
||||
int loop_flag = 0, channels, sample_rate;
|
||||
int32_t loop_start = 0, loop_end = 0, num_samples = 0, encoder_delay = 0;
|
||||
int total_subsongs, target_subsong = sf->stream_index;
|
||||
int faulty = 0; /* mark wonky rips in hopes people may fix them */
|
||||
|
||||
/* no checks */
|
||||
//if (!check_extensions(sf, "..."))
|
||||
@ -102,6 +102,21 @@ VGMSTREAM* init_vgmstream_ffmpeg(STREAMFILE* sf) {
|
||||
ffmpeg_set_skip_samples(data, encoder_delay);
|
||||
}
|
||||
|
||||
/* detect broken RIFFs */
|
||||
if (is_id32be(0x00, sf, "RIFF")) {
|
||||
uint32_t size = read_u32le(0x04, sf);
|
||||
/* There is a log in RIFF too but to be extra sure and sometimes FFmpeg don't handle it (this is mainly for wrong AT3).
|
||||
* Some proper RIFF can be parsed here too (like DUCK). */
|
||||
if (size + 0x08 > get_streamfile_size(sf)) {
|
||||
vgm_logi("RIFF/FFmpeg: incorrect size, file may have missing data\n");
|
||||
faulty = 1;
|
||||
}
|
||||
else if (size + 0x08 < get_streamfile_size(sf)) {
|
||||
vgm_logi("RIFF/FFmpeg: incorrect size, file may have padded data\n");
|
||||
faulty = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* default but often inaccurate when calculated using bitrate (wrong for VBR) */
|
||||
if (!num_samples) {
|
||||
num_samples = ffmpeg_get_samples(data); /* may be 0 if FFmpeg can't precalculate it */
|
||||
@ -115,7 +130,7 @@ VGMSTREAM* init_vgmstream_ffmpeg(STREAMFILE* sf) {
|
||||
vgmstream = allocate_vgmstream(channels, loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->meta_type = meta_FFMPEG;
|
||||
vgmstream->meta_type = faulty ? meta_FFMPEG_faulty : meta_FFMPEG;
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
|
||||
vgmstream->codec_data = data;
|
||||
|
@ -2,7 +2,7 @@
|
||||
#include "../coding/coding.h"
|
||||
#include "../layout/layout.h"
|
||||
|
||||
typedef enum { NONE, MSADPCM, DSP, GCADPCM, ATRAC9, KVS, /*KNS*/ } ktsr_codec;
|
||||
typedef enum { NONE, MSADPCM, DSP, GCADPCM, ATRAC9, RIFF_ATRAC9, KOVS, /*KNS*/ } ktsr_codec;
|
||||
|
||||
#define MAX_CHANNELS 8
|
||||
|
||||
@ -126,8 +126,28 @@ VGMSTREAM* init_vgmstream_ktsr(STREAMFILE* sf) {
|
||||
}
|
||||
#endif
|
||||
|
||||
case RIFF_ATRAC9: {
|
||||
VGMSTREAM* riff_vgmstream = NULL; //TODO: meh
|
||||
STREAMFILE* temp_sf = setup_subfile_streamfile(sf_b, ktsr.stream_offsets[0], ktsr.stream_sizes[0], "at9");
|
||||
if (!temp_sf) goto fail;
|
||||
|
||||
riff_vgmstream = init_vgmstream_riff(temp_sf);
|
||||
close_streamfile(temp_sf);
|
||||
if (!riff_vgmstream) goto fail;
|
||||
|
||||
riff_vgmstream->stream_size = vgmstream->stream_size;
|
||||
riff_vgmstream->num_streams = vgmstream->num_streams;
|
||||
riff_vgmstream->channel_layout = vgmstream->channel_layout;
|
||||
|
||||
strcpy(riff_vgmstream->stream_name, vgmstream->stream_name);
|
||||
|
||||
close_vgmstream(vgmstream);
|
||||
if (sf_b != sf) close_streamfile(sf_b);
|
||||
return riff_vgmstream;
|
||||
}
|
||||
|
||||
#ifdef VGM_USE_VORBIS
|
||||
case KVS: {
|
||||
case KOVS: {
|
||||
VGMSTREAM* ogg_vgmstream = NULL; //TODO: meh
|
||||
STREAMFILE* temp_sf = setup_subfile_streamfile(sf_b, ktsr.stream_offsets[0], ktsr.stream_sizes[0], "kvs");
|
||||
if (!temp_sf) goto fail;
|
||||
@ -234,18 +254,28 @@ static int parse_codec(ktsr_header* ktsr) {
|
||||
/* platform + format to codec, simplified until more codec combos are found */
|
||||
switch(ktsr->platform) {
|
||||
case 0x01: /* PC */
|
||||
if (ktsr->is_external)
|
||||
ktsr->codec = KVS;
|
||||
else if (ktsr->format == 0x00)
|
||||
if (ktsr->is_external) {
|
||||
if (ktsr->format == 0x0005)
|
||||
ktsr->codec = KOVS;
|
||||
else
|
||||
goto fail;
|
||||
}
|
||||
else if (ktsr->format == 0x0000) {
|
||||
ktsr->codec = MSADPCM;
|
||||
else
|
||||
}
|
||||
else {
|
||||
goto fail;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x03: /* VITA */
|
||||
if (ktsr->is_external)
|
||||
goto fail;
|
||||
else if (ktsr->format == 0x01)
|
||||
case 0x03: /* PS4/VITA */
|
||||
if (ktsr->is_external) {
|
||||
if (ktsr->format == 0x1001)
|
||||
ktsr->codec = RIFF_ATRAC9;
|
||||
else
|
||||
goto fail;
|
||||
}
|
||||
else if (ktsr->format == 0x0001)
|
||||
ktsr->codec = ATRAC9;
|
||||
else
|
||||
goto fail;
|
||||
@ -254,7 +284,7 @@ static int parse_codec(ktsr_header* ktsr) {
|
||||
case 0x04: /* Switch */
|
||||
if (ktsr->is_external)
|
||||
goto fail; /* KTSS? */
|
||||
else if (ktsr->format == 0x00)
|
||||
else if (ktsr->format == 0x0000)
|
||||
ktsr->codec = DSP;
|
||||
else
|
||||
goto fail;
|
||||
@ -281,14 +311,14 @@ static int parse_ktsr_subfile(ktsr_header* ktsr, STREAMFILE* sf, uint32_t offset
|
||||
/* probably could check the flag in sound header, but the format is kinda messy */
|
||||
switch(type) {
|
||||
|
||||
case 0x38D0437D: /* external [Nioh (PC), Atelier Ryza (PC)] */
|
||||
case 0x3DEA478D: /* external [Nioh (PC)] */
|
||||
case 0x38D0437D: /* external [Nioh (PC/PS4), Atelier Ryza (PC)] */
|
||||
case 0x3DEA478D: /* external [Nioh (PC)] (smaller) */
|
||||
case 0xDF92529F: /* external [Atelier Ryza (PC)] */
|
||||
case 0x6422007C: /* external [Atelier Ryza (PC)] */
|
||||
/* 08 subtype? (ex. 0x522B86B9)
|
||||
* 0c channels
|
||||
* 10 ? (always 0x002706B8)
|
||||
* 14 codec? (05=KVS)
|
||||
* 14 external codec
|
||||
* 18 sample rate
|
||||
* 1c num samples
|
||||
* 20 null?
|
||||
@ -317,11 +347,6 @@ static int parse_ktsr_subfile(ktsr_header* ktsr, STREAMFILE* sf, uint32_t offset
|
||||
}
|
||||
ktsr->is_external = 1;
|
||||
|
||||
if (ktsr->format != 0x05) {
|
||||
VGM_LOG("ktsr: unknown subcodec at %x\n", offset);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 0x41FDBD4E: /* internal [Attack on Titan: Wings of Freedom (Vita)] */
|
||||
|
@ -988,4 +988,7 @@ VGMSTREAM* init_vgmstream_adm3(STREAMFILE* sf);
|
||||
|
||||
VGMSTREAM* init_vgmstream_tt_ad(STREAMFILE* sf);
|
||||
|
||||
VGMSTREAM* init_vgmstream_bw_mp3_riff(STREAMFILE* sf);
|
||||
VGMSTREAM* init_vgmstream_bw_riff_mp3(STREAMFILE* sf);
|
||||
|
||||
#endif /*_META_H*/
|
||||
|
@ -524,6 +524,8 @@ VGMSTREAM* (*init_vgmstream_functions[])(STREAMFILE* sf) = {
|
||||
init_vgmstream_esf,
|
||||
init_vgmstream_adm3,
|
||||
init_vgmstream_tt_ad,
|
||||
init_vgmstream_bw_mp3_riff,
|
||||
init_vgmstream_bw_riff_mp3,
|
||||
|
||||
/* lower priority metas (no clean header identity, somewhat ambiguous, or need extension/companion file to identify) */
|
||||
init_vgmstream_mpeg,
|
||||
|
@ -587,7 +587,8 @@ typedef enum {
|
||||
meta_HCA, /* CRI HCA */
|
||||
meta_SVAG_SNK,
|
||||
meta_PS2_VDS_VDM, /* Graffiti Kingdom */
|
||||
meta_FFMPEG, /* any file supported by FFmpeg */
|
||||
meta_FFMPEG,
|
||||
meta_FFMPEG_faulty,
|
||||
meta_X360_CXS, /* Eternal Sonata (Xbox 360) */
|
||||
meta_AKB, /* SQEX iOS */
|
||||
meta_X360_PASX, /* Namco PASX (Soul Calibur II HD X360) */
|
||||
|
Loading…
Reference in New Issue
Block a user