Updated VGMStream to r1640-85-gfe316bb2

CQTexperiment
Christopher Snowhill 2021-10-01 20:21:12 -07:00
parent fdae7eec21
commit cb70b066d7
48 changed files with 1038 additions and 1065 deletions

View File

@ -586,6 +586,8 @@
83AFABBC23795202002F3947 /* xssb.c in Sources */ = {isa = PBXBuildFile; fileRef = 83AFABB923795201002F3947 /* xssb.c */; }; 83AFABBC23795202002F3947 /* xssb.c in Sources */ = {isa = PBXBuildFile; fileRef = 83AFABB923795201002F3947 /* xssb.c */; };
83AFABBD23795202002F3947 /* ea_eaac_opus_streamfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 83AFABBA23795202002F3947 /* ea_eaac_opus_streamfile.h */; }; 83AFABBD23795202002F3947 /* ea_eaac_opus_streamfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 83AFABBA23795202002F3947 /* ea_eaac_opus_streamfile.h */; };
83AFABBE23795202002F3947 /* isb.c in Sources */ = {isa = PBXBuildFile; fileRef = 83AFABBB23795202002F3947 /* isb.c */; }; 83AFABBE23795202002F3947 /* isb.c in Sources */ = {isa = PBXBuildFile; fileRef = 83AFABBB23795202002F3947 /* isb.c */; };
83B46FD12707FB2100847FC9 /* at3plus_decoder.h in Headers */ = {isa = PBXBuildFile; fileRef = 83B46FCD2707FB2100847FC9 /* at3plus_decoder.h */; };
83B46FD52707FB9A00847FC9 /* endianness.h in Headers */ = {isa = PBXBuildFile; fileRef = 83B46FD42707FB9A00847FC9 /* endianness.h */; };
83BAFB6C19F45EB3005DAB60 /* bfstm.c in Sources */ = {isa = PBXBuildFile; fileRef = 83BAFB6B19F45EB3005DAB60 /* bfstm.c */; }; 83BAFB6C19F45EB3005DAB60 /* bfstm.c in Sources */ = {isa = PBXBuildFile; fileRef = 83BAFB6B19F45EB3005DAB60 /* bfstm.c */; };
83C7280F22BC893D00678B4A /* xwb_xsb.h in Headers */ = {isa = PBXBuildFile; fileRef = 83C727FB22BC893800678B4A /* xwb_xsb.h */; }; 83C7280F22BC893D00678B4A /* xwb_xsb.h in Headers */ = {isa = PBXBuildFile; fileRef = 83C727FB22BC893800678B4A /* xwb_xsb.h */; };
83C7281022BC893D00678B4A /* nps.c in Sources */ = {isa = PBXBuildFile; fileRef = 83C727FC22BC893900678B4A /* nps.c */; }; 83C7281022BC893D00678B4A /* nps.c in Sources */ = {isa = PBXBuildFile; fileRef = 83C727FC22BC893900678B4A /* nps.c */; };
@ -1394,6 +1396,8 @@
83AFABB923795201002F3947 /* xssb.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xssb.c; sourceTree = "<group>"; }; 83AFABB923795201002F3947 /* xssb.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xssb.c; sourceTree = "<group>"; };
83AFABBA23795202002F3947 /* ea_eaac_opus_streamfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ea_eaac_opus_streamfile.h; sourceTree = "<group>"; }; 83AFABBA23795202002F3947 /* ea_eaac_opus_streamfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ea_eaac_opus_streamfile.h; sourceTree = "<group>"; };
83AFABBB23795202002F3947 /* isb.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = isb.c; sourceTree = "<group>"; }; 83AFABBB23795202002F3947 /* isb.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = isb.c; sourceTree = "<group>"; };
83B46FCD2707FB2100847FC9 /* at3plus_decoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = at3plus_decoder.h; sourceTree = "<group>"; };
83B46FD42707FB9A00847FC9 /* endianness.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = endianness.h; sourceTree = "<group>"; };
83BAFB6B19F45EB3005DAB60 /* bfstm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = bfstm.c; sourceTree = "<group>"; }; 83BAFB6B19F45EB3005DAB60 /* bfstm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = bfstm.c; sourceTree = "<group>"; };
83C727FB22BC893800678B4A /* xwb_xsb.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = xwb_xsb.h; sourceTree = "<group>"; }; 83C727FB22BC893800678B4A /* xwb_xsb.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = xwb_xsb.h; sourceTree = "<group>"; };
83C727FC22BC893900678B4A /* nps.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = nps.c; sourceTree = "<group>"; }; 83C727FC22BC893900678B4A /* nps.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = nps.c; sourceTree = "<group>"; };
@ -1631,6 +1635,7 @@
836F6DE218BDC2180095E648 /* adx_decoder.c */, 836F6DE218BDC2180095E648 /* adx_decoder.c */,
8315958320FEC831007002F0 /* asf_decoder.c */, 8315958320FEC831007002F0 /* asf_decoder.c */,
8306B08120984517000302D4 /* at3plus_decoder.c */, 8306B08120984517000302D4 /* at3plus_decoder.c */,
83B46FCD2707FB2100847FC9 /* at3plus_decoder.h */,
830EBE0F2004655D0023AA10 /* atrac9_decoder.c */, 830EBE0F2004655D0023AA10 /* atrac9_decoder.c */,
834FE0B2215C798C000A5D3D /* celt_fsb_decoder.c */, 834FE0B2215C798C000A5D3D /* celt_fsb_decoder.c */,
83031EBF243C50A800C3F3E0 /* circus_decoder_lib_data.h */, 83031EBF243C50A800C3F3E0 /* circus_decoder_lib_data.h */,
@ -2253,6 +2258,7 @@
children = ( children = (
83D26A8026E66DC2001A9475 /* chunks.c */, 83D26A8026E66DC2001A9475 /* chunks.c */,
83D26A7E26E66DC2001A9475 /* chunks.h */, 83D26A7E26E66DC2001A9475 /* chunks.h */,
83B46FD42707FB9A00847FC9 /* endianness.h */,
83D26A7D26E66DC2001A9475 /* log.c */, 83D26A7D26E66DC2001A9475 /* log.c */,
83D26A7F26E66DC2001A9475 /* log.h */, 83D26A7F26E66DC2001A9475 /* log.h */,
8315868826F586F900803A3A /* m2_psb.c */, 8315868826F586F900803A3A /* m2_psb.c */,
@ -2319,6 +2325,7 @@
83E7FD6225EF2B0C00683FD2 /* tac_decoder_lib_ops.h in Headers */, 83E7FD6225EF2B0C00683FD2 /* tac_decoder_lib_ops.h in Headers */,
83AF2CCC26226BA500538240 /* encrypted_bgm_streamfile.h in Headers */, 83AF2CCC26226BA500538240 /* encrypted_bgm_streamfile.h in Headers */,
8349A90F1FE6258200E26435 /* aix_streamfile.h in Headers */, 8349A90F1FE6258200E26435 /* aix_streamfile.h in Headers */,
83B46FD52707FB9A00847FC9 /* endianness.h in Headers */,
836F705718BDC2190095E648 /* util.h in Headers */, 836F705718BDC2190095E648 /* util.h in Headers */,
83C7282722BC8C1500678B4A /* plugins.h in Headers */, 83C7282722BC8C1500678B4A /* plugins.h in Headers */,
834FE0ED215C79ED000A5D3D /* fsb_interleave_streamfile.h in Headers */, 834FE0ED215C79ED000A5D3D /* fsb_interleave_streamfile.h in Headers */,
@ -2333,6 +2340,7 @@
8306B0E020984590000302D4 /* ppst_streamfile.h in Headers */, 8306B0E020984590000302D4 /* ppst_streamfile.h in Headers */,
83AA7F722519BFEA004C5298 /* vorbis_bitreader.h in Headers */, 83AA7F722519BFEA004C5298 /* vorbis_bitreader.h in Headers */,
834FE0BA215C798C000A5D3D /* ea_mt_decoder_utk.h in Headers */, 834FE0BA215C798C000A5D3D /* ea_mt_decoder_utk.h in Headers */,
83B46FD12707FB2100847FC9 /* at3plus_decoder.h in Headers */,
835C883722CC17BE001B4B3F /* ogg_vorbis_streamfile.h in Headers */, 835C883722CC17BE001B4B3F /* ogg_vorbis_streamfile.h in Headers */,
837CEAFE23487F2C00E62A4A /* jstm_streamfile.h in Headers */, 837CEAFE23487F2C00E62A4A /* jstm_streamfile.h in Headers */,
832BF82021E0514B006F50F1 /* zsnd_streamfile.h in Headers */, 832BF82021E0514B006F50F1 /* zsnd_streamfile.h in Headers */,

View File

@ -3,14 +3,7 @@
#ifdef VGM_USE_MAIATRAC3PLUS #ifdef VGM_USE_MAIATRAC3PLUS
#include "maiatrac3plus.h" #include "maiatrac3plus.h"
#include "at3plus_decoder.h"
struct maiatrac3plus_codec_data {
sample_t* buffer;
int channels;
int samples_discard;
void* handle;
};
maiatrac3plus_codec_data *init_at3plus() { maiatrac3plus_codec_data *init_at3plus() {

View File

@ -0,0 +1,11 @@
#ifndef _AT3PLUS_DECODER_H
#define _AT3PLUS_DECODER_H
struct maiatrac3plus_codec_data {
sample_t* buffer;
int channels;
int samples_discard;
void* handle;
};
#endif

View File

@ -680,7 +680,7 @@ size_t atrac3plus_bytes_to_samples(size_t bytes, int full_block_align);
size_t ac3_bytes_to_samples(size_t bytes, int full_block_align, int channels); size_t ac3_bytes_to_samples(size_t bytes, int full_block_align, int channels);
size_t aac_get_samples(STREAMFILE* sf, off_t start_offset, size_t bytes); size_t aac_get_samples(STREAMFILE* sf, off_t start_offset, size_t bytes);
size_t mpeg_get_samples(STREAMFILE* sf, off_t start_offset, size_t bytes); size_t mpeg_get_samples(STREAMFILE* sf, off_t start_offset, size_t bytes);
int32_t mpeg_get_samples_clean(STREAMFILE* sf, off_t start, size_t size, size_t* p_loop_start, size_t* p_loop_end, int is_vbr); int32_t mpeg_get_samples_clean(STREAMFILE* sf, off_t start, size_t size, uint32_t* p_loop_start, uint32_t* p_loop_end, int is_vbr);
int mpc_get_samples(STREAMFILE* sf, off_t offset, int32_t* p_samples, int32_t* p_delay); int mpc_get_samples(STREAMFILE* sf, off_t offset, int32_t* p_samples, int32_t* p_delay);

View File

@ -55,7 +55,7 @@ struct ffmpeg_codec_data {
}; };
#define FFMPEG_DEFAULT_IO_BUFFER_SIZE 128 * 1024 #define FFMPEG_DEFAULT_IO_BUFFER_SIZE STREAMFILE_DEFAULT_BUFFER_SIZE
static volatile int g_ffmpeg_initialized = 0; static volatile int g_ffmpeg_initialized = 0;
@ -322,7 +322,7 @@ ffmpeg_codec_data* init_ffmpeg_header_offset_subsong(STREAMFILE* sf, uint8_t* he
goto fail; goto fail;
if (size == 0 || start + size > get_streamfile_size(sf)) { if (size == 0 || start + size > get_streamfile_size(sf)) {
vgm_asserti(size != 0, "FFMPEG: wrong start+size found: %x + %x > %x \n", (uint32_t)start, (uint32_t)size, get_streamfile_size(sf)); vgm_asserti(size != 0, "FFMPEG: wrong start+size found: %x + %x > %x \n", (uint32_t)start, (uint32_t)size, (uint32_t)get_streamfile_size(sf));
size = get_streamfile_size(sf) - start; size = get_streamfile_size(sf) - start;
} }

View File

@ -451,7 +451,7 @@ size_t mpeg_get_samples(STREAMFILE* sf, off_t start_offset, size_t bytes) {
/* variation of the above, for clean streams = no ID3/VBR headers /* variation of the above, for clean streams = no ID3/VBR headers
* (maybe should be fused in a single thing with config, API is kinda messy too) */ * (maybe should be fused in a single thing with config, API is kinda messy too) */
int32_t mpeg_get_samples_clean(STREAMFILE *sf, off_t start, size_t size, size_t* p_loop_start, size_t* p_loop_end, int is_vbr) { int32_t mpeg_get_samples_clean(STREAMFILE* sf, off_t start, size_t size, uint32_t* p_loop_start, uint32_t* p_loop_end, int is_vbr) {
mpeg_frame_info info; mpeg_frame_info info;
off_t offset = start; off_t offset = start;
int32_t num_samples = 0, loop_start = 0, loop_end = 0; int32_t num_samples = 0, loop_start = 0, loop_end = 0;

View File

@ -1,9 +1,9 @@
#include "vorbis_custom_decoder.h" #include "vorbis_custom_decoder.h"
#ifdef VGM_USE_VORBIS
#define BITSTREAM_READ_ONLY /* config */ #define BITSTREAM_READ_ONLY /* config */
#include "vorbis_bitreader.h" #include "vorbis_bitreader.h"
#ifdef VGM_USE_VORBIS
#include <vorbis/codec.h> #include <vorbis/codec.h>

View File

@ -1,7 +1,7 @@
#include "vorbis_custom_decoder.h" #include "vorbis_custom_decoder.h"
#include "vorbis_bitreader.h"
#ifdef VGM_USE_VORBIS #ifdef VGM_USE_VORBIS
#include "vorbis_bitreader.h"
#include <vorbis/codec.h> #include <vorbis/codec.h>
#define WWISE_VORBIS_USE_PRECOMPILED_WVC 1 /* if enabled vgmstream weights ~150kb more but doesn't need external .wvc packets */ #define WWISE_VORBIS_USE_PRECOMPILED_WVC 1 /* if enabled vgmstream weights ~150kb more but doesn't need external .wvc packets */

View File

@ -4,6 +4,9 @@
#include "coding/coding.h" #include "coding/coding.h"
#include "mixing.h" #include "mixing.h"
#include "plugins.h" #include "plugins.h"
#ifdef VGM_USE_MAIATRAC3PLUS
#include "at3plus_decoder.h"
#endif
/* custom codec handling, not exactly "decode" stuff but here to simplify adding new codecs */ /* custom codec handling, not exactly "decode" stuff but here to simplify adding new codecs */

File diff suppressed because it is too large Load Diff

View File

@ -11,7 +11,7 @@
#define ADX_KEY_MAX_TEST_FRAMES 32768 #define ADX_KEY_MAX_TEST_FRAMES 32768
#define ADX_KEY_TEST_BUFFER_SIZE 0x8000 #define ADX_KEY_TEST_BUFFER_SIZE 0x8000
static int find_adx_key(STREAMFILE *sf, uint8_t type, uint16_t *xor_start, uint16_t *xor_mult, uint16_t *xor_add, uint16_t subkey); static int find_adx_key(STREAMFILE* sf, uint8_t type, uint16_t* xor_start, uint16_t* xor_mult, uint16_t* xor_add, uint16_t subkey);
VGMSTREAM* init_vgmstream_adx(STREAMFILE* sf) { VGMSTREAM* init_vgmstream_adx(STREAMFILE* sf) {
return init_vgmstream_adx_subkey(sf, 0); return init_vgmstream_adx_subkey(sf, 0);
@ -34,14 +34,14 @@ VGMSTREAM* init_vgmstream_adx_subkey(STREAMFILE* sf, uint16_t subkey) {
/* checks*/ /* checks*/
if (read_u16be(0x00,sf) != 0x8000)
goto fail;
/* .adx: standard /* .adx: standard
* .adp: Headhunter (DC) */ * .adp: Headhunter (DC) */
if (!check_extensions(sf,"adx,adp")) if (!check_extensions(sf,"adx,adp"))
goto fail; goto fail;
if (read_u16be(0x00,sf) != 0x8000)
goto fail;
start_offset = read_u16be(0x02,sf) + 0x04; start_offset = read_u16be(0x02,sf) + 0x04;
if (read_u16be(start_offset - 0x06,sf) != 0x2863 || /* "(c" */ if (read_u16be(start_offset - 0x06,sf) != 0x2863 || /* "(c" */
read_u32be(start_offset - 0x04,sf) != 0x29435249) /* ")CRI" */ read_u32be(start_offset - 0x04,sf) != 0x29435249) /* ")CRI" */
@ -79,18 +79,19 @@ VGMSTREAM* init_vgmstream_adx_subkey(STREAMFILE* sf, uint16_t subkey) {
/* encryption */ /* encryption */
if (version == 0x0408) { if (version == 0x0408) {
if (find_adx_key(sf, 8, &xor_start, &xor_mult, &xor_add, 0)) { if (find_adx_key(sf, 8, &xor_start, &xor_mult, &xor_add, 0)) {
coding_type = coding_CRI_ADX_enc_8; vgm_logi("ADX: decryption keystring not found\n");
version = 0x0400;
} }
vgm_asserti(version != 0x0400, "ADX: decryption keystring not found\n"); coding_type = coding_CRI_ADX_enc_8;
version = 0x0400;
} }
else if (version == 0x0409) { else if (version == 0x0409) {
if (find_adx_key(sf, 9, &xor_start, &xor_mult, &xor_add, subkey)) { if (find_adx_key(sf, 9, &xor_start, &xor_mult, &xor_add, subkey)) {
coding_type = coding_CRI_ADX_enc_9; vgm_logi("ADX: decryption keycode not found\n");
version = 0x0400;
} }
vgm_asserti(version != 0x0400, "ADX: decryption keycode not found\n"); coding_type = coding_CRI_ADX_enc_9;
version = 0x0400;
} }
/* version + extra data */ /* version + extra data */

View File

@ -2,18 +2,17 @@
#include "../coding/coding.h" #include "../coding/coding.h"
/* .AO - from AlphaOgg lib [Cloudphobia (PC), GEO ~The Sword Millennia~ Kasumi no Tani no Kaibutsu (PC)] */ /* .AO - from AlphaOgg lib [Cloudphobia (PC), GEO ~The Sword Millennia~ Kasumi no Tani no Kaibutsu (PC)] */
VGMSTREAM* init_vgmstream_ao(STREAMFILE *sf) { VGMSTREAM* init_vgmstream_ao(STREAMFILE* sf) {
VGMSTREAM* vgmstream = NULL; VGMSTREAM* vgmstream = NULL;
off_t start_offset; off_t start_offset;
/* checks */ /* checks */
if (!check_extensions(sf,"ao"))
goto fail;
if (!is_id64be(0x00,sf, "ALPHAOGG")) if (!is_id64be(0x00,sf, "ALPHAOGG"))
goto fail; goto fail;
if (!check_extensions(sf,"ao"))
goto fail;
#ifdef VGM_USE_VORBIS
{ {
ogg_vorbis_meta_info_t ovmi = {0}; ogg_vorbis_meta_info_t ovmi = {0};
int sample_rate = read_u32le(0xF0, sf); /* Ogg header */ int sample_rate = read_u32le(0xF0, sf); /* Ogg header */
@ -29,9 +28,6 @@ VGMSTREAM* init_vgmstream_ao(STREAMFILE *sf) {
start_offset = 0xc8; start_offset = 0xc8;
vgmstream = init_vgmstream_ogg_vorbis_config(sf, start_offset, &ovmi); vgmstream = init_vgmstream_ogg_vorbis_config(sf, start_offset, &ovmi);
} }
#else
goto fail;
#endif
return vgmstream; return vgmstream;
fail: fail:

View File

@ -10,7 +10,7 @@ VGMSTREAM* init_vgmstream_atsl(STREAMFILE* sf) {
int type, big_endian = 0, entries; int type, big_endian = 0, entries;
uint32_t subfile_offset = 0, subfile_size = 0, header_size, entry_size; uint32_t subfile_offset = 0, subfile_size = 0, header_size, entry_size;
VGMSTREAM* (*init_vgmstream)(STREAMFILE* sf) = NULL; init_vgmstream_t init_vgmstream = NULL;
const char* fake_ext; const char* fake_ext;
@ -47,7 +47,6 @@ VGMSTREAM* init_vgmstream_atsl(STREAMFILE* sf) {
type = read_u16le(0x0c, sf); type = read_u16le(0x0c, sf);
switch(type) { switch(type) {
#ifdef VGM_USE_VORBIS
case 0x0100: /* KOVS */ case 0x0100: /* KOVS */
init_vgmstream = init_vgmstream_ogg_vorbis; init_vgmstream = init_vgmstream_ogg_vorbis;
fake_ext = "kvs"; fake_ext = "kvs";
@ -58,7 +57,6 @@ VGMSTREAM* init_vgmstream_atsl(STREAMFILE* sf) {
fake_ext = "kvs"; fake_ext = "kvs";
entry_size = 0x3c; entry_size = 0x3c;
break; break;
#endif
case 0x0200: /* ATRAC3 */ case 0x0200: /* ATRAC3 */
init_vgmstream = init_vgmstream_riff; init_vgmstream = init_vgmstream_riff;
fake_ext = "at3"; fake_ext = "at3";

View File

@ -87,7 +87,7 @@ VGMSTREAM* init_vgmstream_awb_memory(STREAMFILE* sf, STREAMFILE* sf_acb) {
VGMSTREAM* (*init_vgmstream_subkey)(STREAMFILE* sf, uint16_t subkey) = NULL; VGMSTREAM* (*init_vgmstream_subkey)(STREAMFILE* sf, uint16_t subkey) = NULL;
const char* extension = NULL; const char* extension = NULL;
if (read_u16be(subfile_offset, sf) == 0x8000) { /* (type 0=ADX) */ if (read_u16be(subfile_offset, sf) == 0x8000) { /* (type 0=ADX, also 3?) */
init_vgmstream_subkey = init_vgmstream_adx_subkey; /* Okami HD (PS4) */ init_vgmstream_subkey = init_vgmstream_adx_subkey; /* Okami HD (PS4) */
extension = "adx"; extension = "adx";
} }
@ -99,7 +99,7 @@ VGMSTREAM* init_vgmstream_awb_memory(STREAMFILE* sf, STREAMFILE* sf_acb) {
init_vgmstream = init_vgmstream_vag; /* Ukiyo no Roushi (Vita) */ init_vgmstream = init_vgmstream_vag; /* Ukiyo no Roushi (Vita) */
extension = "vag"; extension = "vag";
} }
else if (is_id32be(subfile_offset,sf, "RIFF")) { /* (type 8=ATRAC3, 11=ATRAC9) */ else if (is_id32be(subfile_offset,sf, "RIFF")) { /* (type 8=ATRAC3, 11=ATRAC9, also 18=ATRAC9?) */
init_vgmstream = init_vgmstream_riff; /* Ukiyo no Roushi (Vita) */ init_vgmstream = init_vgmstream_riff; /* Ukiyo no Roushi (Vita) */
extension = "wav"; extension = "wav";
subfile_size = read_u32le(subfile_offset + 0x04,sf) + 0x08; /* padded size, use RIFF's */ subfile_size = read_u32le(subfile_offset + 0x04,sf) + 0x08; /* padded size, use RIFF's */
@ -111,7 +111,7 @@ VGMSTREAM* init_vgmstream_awb_memory(STREAMFILE* sf, STREAMFILE* sf_acb) {
else if (read_u32be(subfile_offset + 0x08,sf) >= 8000 && read_u32be(subfile_offset + 0x08,sf) <= 48000 && else if (read_u32be(subfile_offset + 0x08,sf) >= 8000 && read_u32be(subfile_offset + 0x08,sf) <= 48000 &&
read_u16be(subfile_offset + 0x0e,sf) == 0 && read_u16be(subfile_offset + 0x0e,sf) == 0 &&
read_u32be(subfile_offset + 0x18,sf) == 2 && read_u32be(subfile_offset + 0x18,sf) == 2 &&
read_u32be(subfile_offset + 0x50,sf) == 0) { /* (type 13=DSP), probably should call some check function */ read_u32be(subfile_offset + 0x50,sf) == 0) { /* (type 13=DSP, also 4=Wii?, 5=NDS?), probably should call some check function */
init_vgmstream = init_vgmstream_ngc_dsp_std; /* Sonic: Lost World (WiiU) */ init_vgmstream = init_vgmstream_ngc_dsp_std; /* Sonic: Lost World (WiiU) */
extension = "dsp"; extension = "dsp";
} }
@ -125,7 +125,7 @@ VGMSTREAM* init_vgmstream_awb_memory(STREAMFILE* sf, STREAMFILE* sf_acb) {
extension = "m4a"; extension = "m4a";
} }
#endif #endif
else { else { /* 12=XMA? */
vgm_logi("AWB: unknown codec (report)\n"); vgm_logi("AWB: unknown codec (report)\n");
goto fail; goto fail;
} }

View File

@ -2,34 +2,31 @@
#include "../coding/coding.h" #include "../coding/coding.h"
/* FWAV - Nintendo streams */ /* FWAV - Nintendo streams */
VGMSTREAM * init_vgmstream_bfwav(STREAMFILE *streamFile) { VGMSTREAM* init_vgmstream_bfwav(STREAMFILE* sf) {
VGMSTREAM * vgmstream = NULL; VGMSTREAM* vgmstream = NULL;
off_t start_offset; off_t start_offset;
off_t info_offset, data_offset; off_t info_offset, data_offset;
int channel_count, loop_flag, codec; int channels, loop_flag, codec, sample_rate;
int big_endian; int big_endian;
size_t interleave = 0; size_t interleave = 0;
int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL; int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL;
int16_t (*read_16bit)(off_t,STREAMFILE*) = NULL; int16_t (*read_16bit)(off_t,STREAMFILE*) = NULL;
int nsmbu_flag = 0;
/* checks */ /* checks */
if (!is_id32be(0x00, sf, "FWAV"))
goto fail;
/* .bfwavnsmbu: fake extension to detect New Super Mario Bros U files with weird sample rate */ /* .bfwavnsmbu: fake extension to detect New Super Mario Bros U files with weird sample rate */
if (!check_extensions(streamFile, "bfwav,fwav,bfwavnsmbu")) if (!check_extensions(sf, "bfwav,fwav,bfwavnsmbu"))
goto fail; goto fail;
nsmbu_flag = check_extensions(streamFile, "bfwavnsmbu");
/* FWAV header */ /* BOM check */
if (read_32bitBE(0x00, streamFile) != 0x46574156) /* "FWAV" */ if (read_u16be(0x04, sf) == 0xFEFF) {
goto fail;
/* 0x06(2): header size (0x40), 0x08: version (0x00010200), 0x0c: file size 0x10(2): sections (2) */
if ((uint16_t)read_16bitBE(0x04, streamFile) == 0xFEFF) { /* BE BOM check */
read_32bit = read_32bitBE; read_32bit = read_32bitBE;
read_16bit = read_16bitBE; read_16bit = read_16bitBE;
big_endian = 1; big_endian = 1;
} else if ((uint16_t)read_16bitBE(0x04, streamFile) == 0xFFFE) { /* LE BOM check */ } else if (read_u16be(0x04, sf) == 0xFFFE) {
read_32bit = read_32bitLE; read_32bit = read_32bitLE;
read_16bit = read_16bitLE; read_16bit = read_16bitLE;
big_endian = 0; big_endian = 0;
@ -37,60 +34,74 @@ VGMSTREAM * init_vgmstream_bfwav(STREAMFILE *streamFile) {
goto fail; goto fail;
} }
info_offset = read_32bit(0x18, streamFile); /* 0x14(2): info mark (0x7000), 0x1c: info size */ /* FWAV header */
data_offset = read_32bit(0x24, streamFile); /* 0x20(2): data mark (0x7001), 0x28: data size */ /* 0x06(2): header size (0x40) */
/* 0x08: version (0x00010200) */
/* 0x0c: file size */
/* 0x10(2): sections (2) */
/* 0x14(2): info mark (0x7000) */
info_offset = read_32bit(0x18, sf);
/* 0x1c: info size */
/* 0x20(2): data mark (0x7001) */
data_offset = read_32bit(0x24, sf);
/* 0x28: data size */
/* INFO section */ /* INFO section */
if (read_32bitBE(info_offset, streamFile) != 0x494E464F) /* "INFO" */ if (!is_id32be(info_offset, sf, "INFO"))
goto fail; goto fail;
codec = read_8bit(info_offset + 0x08, streamFile); codec = read_u8(info_offset + 0x08, sf);
loop_flag = read_8bit(info_offset + 0x09, streamFile); loop_flag = read_u8(info_offset + 0x09, sf);
channel_count = read_32bit(info_offset + 0x1C, streamFile); sample_rate = read_32bit(info_offset + 0x0C, sf);
channels = read_32bit(info_offset + 0x1C, sf);
//TODO remove
if (check_extensions(sf, "bfwavnsmbu"))
sample_rate = 16000;
/* parse channel table */ /* parse channel table */
{ {
off_t channel1_info, data_start; off_t channel1_info, data_start;
int i; int i;
channel1_info = info_offset + 0x1c + read_32bit(info_offset+0x20+0x08*0+0x04, streamFile); channel1_info = info_offset + 0x1c + read_32bit(info_offset+0x20+0x08*0+0x04, sf);
data_start = read_32bit(channel1_info+0x04, streamFile); /* within "DATA" after 0x08 */ data_start = read_32bit(channel1_info+0x04, sf); /* within "DATA" after 0x08 */
/* channels use absolute offsets but should be ok as interleave */ /* channels use absolute offsets but should be ok as interleave */
interleave = 0; interleave = 0;
if (channel_count > 1) { if (channels > 1) {
off_t channel2_info = info_offset + 0x1c + read_32bit(info_offset+0x20+0x08*1+0x04, streamFile); off_t channel2_info = info_offset + 0x1c + read_32bit(info_offset+0x20+0x08*1+0x04, sf);
interleave = read_32bit(channel2_info+0x04, streamFile) - data_start; interleave = read_32bit(channel2_info+0x04, sf) - data_start;
} }
start_offset = data_offset + 0x08 + data_start; start_offset = data_offset + 0x08 + data_start;
/* validate all channels just in case of multichannel with non-constant interleave */ /* validate all channels just in case of multichannel with non-constant interleave */
for (i = 0; i < channel_count; i++) { for (i = 0; i < channels; i++) {
/* channel table, 0x00: flag (0x7100), 0x04: channel info offset */ /* 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); off_t channel_info = info_offset + 0x1c + read_32bit(info_offset+0x20+0x08*i+0x04, sf);
/* channel info, 0x00(2): flag (0x1f00), 0x04: offset, 0x08(2): ADPCM flag (0x0300), 0x0c: ADPCM offset */ /* 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) if ((uint16_t)read_16bit(channel_info+0x00, sf) != 0x1F00)
goto fail; goto fail;
if (read_32bit(channel_info+0x04, streamFile) != data_start + interleave*i) if (read_32bit(channel_info+0x04, sf) != data_start + interleave*i)
goto fail; goto fail;
} }
} }
/* build the VGMSTREAM */ /* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count, loop_flag); vgmstream = allocate_vgmstream(channels, loop_flag);
if (!vgmstream) goto fail; if (!vgmstream) goto fail;
vgmstream->sample_rate = read_32bit(info_offset + 0x0C, streamFile); vgmstream->sample_rate = sample_rate;
if (nsmbu_flag)
vgmstream->sample_rate = 16000;
vgmstream->num_samples = read_32bit(info_offset + 0x14, streamFile); vgmstream->num_samples = read_32bit(info_offset + 0x14, sf);
vgmstream->loop_start_sample = read_32bit(info_offset + 0x10, streamFile); vgmstream->loop_start_sample = read_32bit(info_offset + 0x10, sf);
vgmstream->loop_end_sample = vgmstream->num_samples; vgmstream->loop_end_sample = vgmstream->num_samples;
vgmstream->meta_type = meta_FWAV; vgmstream->meta_type = meta_FWAV;
vgmstream->layout_type = (channel_count == 1) ? layout_none : layout_interleave; vgmstream->layout_type = (channels == 1) ? layout_none : layout_interleave;
vgmstream->interleave_block_size = interleave; vgmstream->interleave_block_size = interleave;
switch (codec) { switch (codec) {
@ -110,9 +121,9 @@ VGMSTREAM * init_vgmstream_bfwav(STREAMFILE *streamFile) {
for (i = 0; i < vgmstream->channels; i++) { for (i = 0; i < vgmstream->channels; i++) {
for (c = 0; c < 16; c++) { for (c = 0; c < 16; c++) {
coef_header = info_offset + 0x1C + read_32bit(info_offset + 0x24 + (i*0x08), streamFile); coef_header = info_offset + 0x1C + read_32bit(info_offset + 0x24 + (i*0x08), sf);
coef_offset = read_32bit(coef_header + 0x0c, streamFile) + coef_header; coef_offset = read_32bit(coef_header + 0x0c, sf) + coef_header;
vgmstream->ch[i].adpcm_coef[c] = read_16bit(coef_offset + c*2, streamFile); vgmstream->ch[i].adpcm_coef[c] = read_16bit(coef_offset + c*2, sf);
} }
} }
} }
@ -123,7 +134,7 @@ VGMSTREAM * init_vgmstream_bfwav(STREAMFILE *streamFile) {
} }
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) if (!vgmstream_open_stream(vgmstream,sf,start_offset))
goto fail; goto fail;
return vgmstream; return vgmstream;

View File

@ -13,7 +13,7 @@ VGMSTREAM* init_vgmstream_csb(STREAMFILE* sf) {
utf_context *utf_sdl = NULL; utf_context *utf_sdl = NULL;
int total_subsongs, target_subsong = sf->stream_index; int total_subsongs, target_subsong = sf->stream_index;
uint8_t fmt = 0; uint8_t fmt = 0;
const char* stream_name; const char* stream_name = NULL;
/* checks */ /* checks */

View File

@ -38,9 +38,8 @@ VGMSTREAM* init_vgmstream_encrypted(STREAMFILE* sf) {
temp_sf = setup_ogg_vorbis_streamfile(sf, cfg); temp_sf = setup_ogg_vorbis_streamfile(sf, cfg);
if (!temp_sf) goto fail; if (!temp_sf) goto fail;
#ifdef VGM_USE_VORBIS
vgmstream = init_vgmstream_ogg_vorbis(temp_sf); vgmstream = init_vgmstream_ogg_vorbis(temp_sf);
#endif
close_streamfile(temp_sf); close_streamfile(temp_sf);
return vgmstream; return vgmstream;
} }

View File

@ -3,9 +3,9 @@
/* FFDL - Matrix Software wrapper [Final Fantasy Dimensions (Android/iOS)] */ /* FFDL - Matrix Software wrapper [Final Fantasy Dimensions (Android/iOS)] */
VGMSTREAM * init_vgmstream_ffdl(STREAMFILE *sf) { VGMSTREAM* init_vgmstream_ffdl(STREAMFILE* sf) {
VGMSTREAM * vgmstream = NULL; VGMSTREAM* vgmstream = NULL;
STREAMFILE *temp_sf = NULL; STREAMFILE* temp_sf = NULL;
int loop_flag = 0, is_ffdl = 0; int loop_flag = 0, is_ffdl = 0;
int32_t num_samples = 0, loop_start_sample = 0, loop_end_sample = 0; int32_t num_samples = 0, loop_start_sample = 0, loop_end_sample = 0;
off_t start_offset; off_t start_offset;
@ -13,6 +13,10 @@ VGMSTREAM * init_vgmstream_ffdl(STREAMFILE *sf) {
/* checks */ /* checks */
if (!is_id32be(0x00,sf, "FFDL") &&
!is_id32be(0x00,sf, "mtxs"))
goto fail;
/* .ogg/logg: probable extension for Android /* .ogg/logg: probable extension for Android
* .mp4/lmp4: probable extension for iOS * .mp4/lmp4: probable extension for iOS
* .bin: iOS FFDL extension * .bin: iOS FFDL extension
@ -24,16 +28,15 @@ VGMSTREAM * init_vgmstream_ffdl(STREAMFILE *sf) {
* Ogg/MP4 or "mtxs" w/ loops + Ogg/MP4, and may concatenate multiple of them * Ogg/MP4 or "mtxs" w/ loops + Ogg/MP4, and may concatenate multiple of them
* (without size in sight), so they should be split externally first. */ * (without size in sight), so they should be split externally first. */
start_offset = 0x00;
/* may start with wrapper (not split) */ /* may start with wrapper (not split) */
if (read_u32be(0x00,sf) == 0x4646444C) { /* "FFDL" */ start_offset = 0x00;
if (is_id32be(0x00,sf, "FFDL")) {
is_ffdl = 1; is_ffdl = 1;
start_offset += 0x04; start_offset += 0x04;
} }
/* may start with sample info (split) or after "FFDL" */ /* may start with sample info (split) or after "FFDL" */
if (read_u32be(start_offset+0x00,sf) == 0x6D747873) { /* "mtxs" */ if (is_id32be(start_offset+0x00,sf, "mtxs")) {
is_ffdl = 1; is_ffdl = 1;
num_samples = read_s32le(start_offset + 0x04,sf); num_samples = read_s32le(start_offset + 0x04,sf);
@ -51,15 +54,11 @@ VGMSTREAM * init_vgmstream_ffdl(STREAMFILE *sf) {
file_size = get_streamfile_size(sf) - start_offset; file_size = get_streamfile_size(sf) - start_offset;
if (read_u32be(start_offset + 0x00,sf) == 0x4F676753) { /* "OggS" */ if (read_u32be(start_offset + 0x00,sf) == 0x4F676753) { /* "OggS" */
#ifdef VGM_USE_VORBIS
temp_sf = setup_subfile_streamfile(sf, start_offset, file_size, "ogg"); temp_sf = setup_subfile_streamfile(sf, start_offset, file_size, "ogg");
if (!temp_sf) goto fail; if (!temp_sf) goto fail;
vgmstream = init_vgmstream_ogg_vorbis(temp_sf); vgmstream = init_vgmstream_ogg_vorbis(temp_sf);
if (!vgmstream) goto fail; if (!vgmstream) goto fail;
#else
goto fail;
#endif
} }
else if (read_u32be(start_offset + 0x04,sf) == 0x66747970) { /* "ftyp" after atom size */ else if (read_u32be(start_offset + 0x04,sf) == 0x66747970) { /* "ftyp" after atom size */
#ifdef VGM_USE_FFMPEG #ifdef VGM_USE_FFMPEG

View File

@ -18,10 +18,10 @@ typedef struct {
int32_t loop_end; int32_t loop_end;
int loop_flag; int loop_flag;
size_t sample_header_size; uint32_t sample_header_size;
size_t name_table_size; uint32_t name_table_size;
size_t sample_data_size; uint32_t sample_data_size;
size_t base_header_size; uint32_t base_header_size;
uint32_t extradata_offset; uint32_t extradata_offset;
uint32_t extradata_size; uint32_t extradata_size;
@ -46,15 +46,15 @@ VGMSTREAM* init_vgmstream_fsb5(STREAMFILE* sf) {
/* checks */ /* checks */
if (!is_id32be(0x00,sf, "FSB5"))
goto fail;
/* .fsb: standard /* .fsb: standard
* .snd: Alchemy engine (also Unity) */ * .snd: Alchemy engine (also Unity) */
if (!check_extensions(sf,"fsb,snd")) if (!check_extensions(sf,"fsb,snd"))
goto fail; goto fail;
if (!is_id32be(0x00,sf, "FSB5")) /* v0 is rare, seen in Tales from Space (Vita) */
goto fail;
/* v0 is rare (seen in Tales from Space Vita) */
fsb5.version = read_u32le(0x04,sf); fsb5.version = read_u32le(0x04,sf);
if (fsb5.version != 0x00 && fsb5.version != 0x01) if (fsb5.version != 0x00 && fsb5.version != 0x01)
goto fail; goto fail;
@ -80,7 +80,7 @@ VGMSTREAM* init_vgmstream_fsb5(STREAMFILE* sf) {
} }
if ((fsb5.sample_header_size + fsb5.name_table_size + fsb5.sample_data_size + fsb5.base_header_size) != get_streamfile_size(sf)) { if ((fsb5.sample_header_size + fsb5.name_table_size + fsb5.sample_data_size + fsb5.base_header_size) != get_streamfile_size(sf)) {
vgm_logi("FSB5: wrong size, expected %x + %x + %x + %x vs %x (re-rip)\n", fsb5.sample_header_size, fsb5.name_table_size, fsb5.sample_data_size, fsb5.base_header_size, get_streamfile_size(sf)); vgm_logi("FSB5: wrong size, expected %x + %x + %x + %x vs %x (re-rip)\n", fsb5.sample_header_size, fsb5.name_table_size, fsb5.sample_data_size, fsb5.base_header_size, (uint32_t)get_streamfile_size(sf));
goto fail; goto fail;
} }

View File

@ -191,10 +191,21 @@ done:
} }
#ifdef HCA_BRUTEFORCE #ifdef HCA_BRUTEFORCE
typedef enum {
HBF_TYPE_64LE_1,
HBF_TYPE_64BE_1,
HBF_TYPE_32LE_1,
HBF_TYPE_32BE_1,
HBF_TYPE_64LE_4,
HBF_TYPE_64BE_4,
HBF_TYPE_32LE_4,
HBF_TYPE_32BE_4,
} HBF_type_t;
/* Bruteforce binary keys in executables and similar files, mainly for some mobile games. /* Bruteforce binary keys in executables and similar files, mainly for some mobile games.
* Kinda slow but acceptable for ~20MB exes, not very optimized. Unity usually has keys * Kinda slow but acceptable for ~20MB exes, not very optimized. Unity usually has keys
* in plaintext (inside levelX or other base files) instead though. */ * in plaintext (inside levelX or other base files) instead though. */
static void bruteforce_hca_key_bin(STREAMFILE* sf, hca_codec_data* hca_data, unsigned long long* p_keycode, uint16_t subkey) { static void bruteforce_hca_key_bin_type(STREAMFILE* sf, hca_codec_data* hca_data, unsigned long long* p_keycode, uint16_t subkey, HBF_type_t type) {
STREAMFILE* sf_keys = NULL; STREAMFILE* sf_keys = NULL;
uint8_t* buf = NULL; uint8_t* buf = NULL;
int best_score = 0xFFFFFF, cur_score; int best_score = 0xFFFFFF, cur_score;
@ -203,7 +214,7 @@ static void bruteforce_hca_key_bin(STREAMFILE* sf, hca_codec_data* hca_data, uns
uint64_t old_key = 0; uint64_t old_key = 0;
VGM_LOG("HCA: test keys.bin\n"); VGM_LOG("HCA: test keys.bin (type %i)\n", type);
*p_keycode = 0; *p_keycode = 0;
@ -226,17 +237,18 @@ static void bruteforce_hca_key_bin(STREAMFILE* sf, hca_codec_data* hca_data, uns
uint64_t key; uint64_t key;
VGM_ASSERT(pos % 0x1000000 == 0, "HCA: pos %x...\n", pos); VGM_ASSERT(pos % 0x1000000 == 0, "HCA: pos %x...\n", pos);
/* keys are usually u32le lower, u32le upper (u64le) but other orders may exist */ /* keys are usually u64le but other orders may exist */
key = ((uint64_t)get_u32le(buf + pos + 0x00) << 0 ) | ((uint64_t)get_u32le(buf + pos + 0x04) << 32); switch(type) {
//key = ((uint64_t)get_u32le(buf + pos + 0x00) << 32) | ((uint64_t)get_u32le(buf + pos + 0x04) << 0); case HBF_TYPE_64LE_1: key = get_u64le(buf + pos); pos += 0x01; break;
//key = ((uint64_t)get_u32be(buf + pos + 0x00) << 0 ) | ((uint64_t)get_u32be(buf + pos + 0x04) << 32); case HBF_TYPE_64BE_1: key = get_u64be(buf + pos); pos += 0x01; break;
//key = ((uint64_t)get_u32be(buf + pos + 0x00) << 32) | ((uint64_t)get_u32be(buf + pos + 0x04) << 0); case HBF_TYPE_32LE_1: key = get_u32le(buf + pos); pos += 0x01; break;
//key = ((uint64_t)get_u32le(buf + pos + 0x00) << 0 ) | 0; /* upper bytes not set, ex. P5 */ case HBF_TYPE_32BE_1: key = get_u32be(buf + pos); pos += 0x01; break;
//key = ((uint64_t)get_u32be(buf + pos + 0x00) << 0 ) | 0; /* upper bytes not set, ex. P5 */ case HBF_TYPE_64LE_4: key = get_u64le(buf + pos); pos += 0x04; break;
case HBF_TYPE_64BE_4: key = get_u64be(buf + pos); pos += 0x04; break;
/* observed files have aligned keys, change if needed */ case HBF_TYPE_32LE_4: key = get_u32le(buf + pos); pos += 0x04; break;
pos += 0x04; case HBF_TYPE_32BE_4: key = get_u32be(buf + pos); pos += 0x04; break;
//pos++; default: key = 0; pos = keys_size; break;
}
if (key == 0 || key == old_key) if (key == 0 || key == old_key)
continue; continue;
@ -266,6 +278,18 @@ done:
free(buf); free(buf);
} }
static void bruteforce_hca_key_bin(STREAMFILE* sf, hca_codec_data* hca_data, unsigned long long* p_keycode, uint16_t subkey) {
bruteforce_hca_key_bin_type(sf, hca_data, p_keycode, subkey, HBF_TYPE_64LE_4);
bruteforce_hca_key_bin_type(sf, hca_data, p_keycode, subkey, HBF_TYPE_64BE_4);
bruteforce_hca_key_bin_type(sf, hca_data, p_keycode, subkey, HBF_TYPE_32LE_4);
bruteforce_hca_key_bin_type(sf, hca_data, p_keycode, subkey, HBF_TYPE_32BE_4);
bruteforce_hca_key_bin_type(sf, hca_data, p_keycode, subkey, HBF_TYPE_64LE_1);
bruteforce_hca_key_bin_type(sf, hca_data, p_keycode, subkey, HBF_TYPE_64BE_1);
bruteforce_hca_key_bin_type(sf, hca_data, p_keycode, subkey, HBF_TYPE_32LE_1);
bruteforce_hca_key_bin_type(sf, hca_data, p_keycode, subkey, HBF_TYPE_32BE_1);
}
#include <inttypes.h> #include <inttypes.h>
//#include <stdio.h> //#include <stdio.h>
@ -303,14 +327,13 @@ static void bruteforce_hca_key_txt(STREAMFILE* sf, hca_codec_data* hca_data, uns
uint64_t key = 0; uint64_t key = 0;
bytes_read = read_line(line, sizeof(line), pos, sf_keys, &line_ok); bytes_read = read_line(line, sizeof(line), pos, sf_keys, &line_ok);
if (!line_ok) continue; //???
pos += bytes_read; pos += bytes_read;
if (!line_ok) continue; /* line too long */
count = sscanf(line, "%" SCNd64, &key); count = sscanf(line, "%" SCNd64, &key);
if (count != 1) continue; if (count != 1) continue;
VGM_ASSERT(pos % 100000 == 0, "HCA: count %i...\n", i); VGM_ASSERT(pos % 10000 == 0, "HCA: count %i...\n", i);
if (key == 0) if (key == 0)
continue; continue;

View File

@ -435,6 +435,8 @@ static const hcakey_info hcakey_list[] = {
{0x2f778c736a8a4597}, //music_0110015 {0x2f778c736a8a4597}, //music_0110015
{0xa90c8ebf8463d05}, //music_0110016 {0xa90c8ebf8463d05}, //music_0110016
{0x750beaf22ddc700b}, //music_0110018 {0x750beaf22ddc700b}, //music_0110018
{0x16ccc93f976a8329}, //music_0110019
{0x9f7a0810034669fe}, //music_0110020
{0xfb647d074e53fab6}, //music_0120001 {0xfb647d074e53fab6}, //music_0120001
{0xc24049b9f7ed3105}, //music_0120002 {0xc24049b9f7ed3105}, //music_0120002
{0xdc128f2fd48bf4b}, //music_0120003 {0xdc128f2fd48bf4b}, //music_0120003
@ -450,7 +452,9 @@ static const hcakey_info hcakey_list[] = {
{0x6ba36cadf1e045cf}, //music_0120013 {0x6ba36cadf1e045cf}, //music_0120013
{0xb96786621e27daf3}, //music_0120014 {0xb96786621e27daf3}, //music_0120014
{0xa2c543b227b8e5e2}, //music_0120015 {0xa2c543b227b8e5e2}, //music_0120015
{0x845437ec4e367a13}, //music_0120016
{0x3674aba8da7bc84b}, //music_0120018 {0x3674aba8da7bc84b}, //music_0120018
{0xfd61f2c3b89f3888}, //music_0120019
{0x4fffee4065d22bec}, //music_0210001 {0x4fffee4065d22bec}, //music_0210001
{0x7678588b0adf59df}, //music_0210002 {0x7678588b0adf59df}, //music_0210002
{0xa0316b536c8b7540}, //music_0210003 {0xa0316b536c8b7540}, //music_0210003
@ -461,6 +465,7 @@ static const hcakey_info hcakey_list[] = {
{0x73621a0d321e60c2}, //music_0210008 {0x73621a0d321e60c2}, //music_0210008
{0xff04547fe629c8bf}, //music_0210009 {0xff04547fe629c8bf}, //music_0210009
{0x5ef795cdbcdcba91}, //music_0210010 {0x5ef795cdbcdcba91}, //music_0210010
{0x868acc0102c59a38}, //music_0210011
{0x15bb78c31db0a0b6}, //music_0220001 {0x15bb78c31db0a0b6}, //music_0220001
{0x59b1257242c40109}, //music_0220002 {0x59b1257242c40109}, //music_0220002
{0xdb402bd08d522f34}, //music_0220003 {0xdb402bd08d522f34}, //music_0220003
@ -474,6 +479,7 @@ static const hcakey_info hcakey_list[] = {
{0xf0c624dc0385adae}, //music_0220011 {0xf0c624dc0385adae}, //music_0220011
{0xce0796d2a956dc5a}, //music_0220012 {0xce0796d2a956dc5a}, //music_0220012
{0xf9d6fb07c0b4e967}, //music_0220013 {0xf9d6fb07c0b4e967}, //music_0220013
{0x4aa31e0c4f787a8}, //music_0220014
{0x94466db0d3c10f4b}, //music_0220015 {0x94466db0d3c10f4b}, //music_0220015
{0xe6d1fd6effa46736}, //music_0220017 {0xe6d1fd6effa46736}, //music_0220017
{0x6a15a9610d10d210}, //music_0310001 {0x6a15a9610d10d210}, //music_0310001
@ -502,6 +508,7 @@ static const hcakey_info hcakey_list[] = {
{0x244a92885ab77b7c}, //music_0320012 {0x244a92885ab77b7c}, //music_0320012
{0xfc3fa77fc33460d4}, //music_0320013 {0xfc3fa77fc33460d4}, //music_0320013
{0x26ee13598091b548}, //music_0320014 {0x26ee13598091b548}, //music_0320014
{0x2df608ef06aca41c}, //music_0320016
{0x776c4aded0bca5d1}, //music_0410001 {0x776c4aded0bca5d1}, //music_0410001
{0xb7bff4fbf66be43f}, //music_0410002 {0xb7bff4fbf66be43f}, //music_0410002
{0x904f50c5ce8ec6e4}, //music_0410003 {0x904f50c5ce8ec6e4}, //music_0410003
@ -527,6 +534,7 @@ static const hcakey_info hcakey_list[] = {
{0x52723f026d5238e8}, //music_0420012 {0x52723f026d5238e8}, //music_0420012
{0xd13a315c0005f0}, //music_0420013 {0xd13a315c0005f0}, //music_0420013
{0x35f2d3cec84aba1}, //music_0420014 {0x35f2d3cec84aba1}, //music_0420014
{0xdad11fe0e397ede}, //music_0420015
{0xdf31e26a7b036a2}, //music_0510001 {0xdf31e26a7b036a2}, //music_0510001
{0xb2770dced3cfd9a7}, //music_0510002 {0xb2770dced3cfd9a7}, //music_0510002
{0x6c6c1fd51e28a1e7}, //music_0510003 {0x6c6c1fd51e28a1e7}, //music_0510003
@ -540,6 +548,7 @@ static const hcakey_info hcakey_list[] = {
{0xc5c9bf138c9e28ce}, //music_0510011 {0xc5c9bf138c9e28ce}, //music_0510011
{0x1980271cfe0da9bd}, //music_0510012 {0x1980271cfe0da9bd}, //music_0510012
{0x75c5bd4e3a01a8a4}, //music_0510013 {0x75c5bd4e3a01a8a4}, //music_0510013
{0xec5f5fbe92bbb771}, //music_0510014
{0x15f82c1617013c36}, //music_0520001 {0x15f82c1617013c36}, //music_0520001
{0xc7da8e6f0e2fe399}, //music_0520002 {0xc7da8e6f0e2fe399}, //music_0520002
{0xe350bffcdc9cb686}, //music_0520003 {0xe350bffcdc9cb686}, //music_0520003
@ -552,6 +561,7 @@ static const hcakey_info hcakey_list[] = {
{0xde4959221bc2675}, //music_0520010 {0xde4959221bc2675}, //music_0520010
{0xeeaf8d2458ccdb36}, //music_0520011 {0xeeaf8d2458ccdb36}, //music_0520011
{0xb140168a47d55b92}, //music_0520012 {0xb140168a47d55b92}, //music_0520012
{0x1bf43def1e4b103a}, //music_0520014
{0xd2ce91dbfc209b10}, //music_0610001 {0xd2ce91dbfc209b10}, //music_0610001
{0xa662be1601e49476}, //music_0610002 {0xa662be1601e49476}, //music_0610002
{0xe5e83d31e64273f8}, //music_0610003 {0xe5e83d31e64273f8}, //music_0610003
@ -576,6 +586,7 @@ static const hcakey_info hcakey_list[] = {
{0x33848be13a2884a3}, //music_0620011 {0x33848be13a2884a3}, //music_0620011
{0xfab3596f11cc4d7a}, //music_0620012 {0xfab3596f11cc4d7a}, //music_0620012
{0xe35d52b6d2c094fb}, //music_0620013 {0xe35d52b6d2c094fb}, //music_0620013
{0xcdb9bc2ad7024ca2}, //music_0620014
{0x2a47feac8dc3ca9c}, //music_3010001 {0x2a47feac8dc3ca9c}, //music_3010001
{0x9ebbaf63ffe9d9ef}, //music_3010002 {0x9ebbaf63ffe9d9ef}, //music_3010002
{0xe553dba6592293d8}, //music_3010003 {0xe553dba6592293d8}, //music_3010003
@ -586,12 +597,16 @@ static const hcakey_info hcakey_list[] = {
{0x1ab266a4cbb5133a}, //music_3010008 {0x1ab266a4cbb5133a}, //music_3010008
{0x7d4719615fbb2f4d}, //music_3010009 {0x7d4719615fbb2f4d}, //music_3010009
{0x28aa75a01f26a853}, //music_3010010 {0x28aa75a01f26a853}, //music_3010010
{0x7555feeaa2a8fac4}, //music_3010011
{0xa42de67a89fb3175}, //music_3010012 {0xa42de67a89fb3175}, //music_3010012
{0xbdd0c58062c675d4}, //music_3010014
{0xfd3ea450350d666f}, //music_3020001 {0xfd3ea450350d666f}, //music_3020001
{0x5e91a3790c32e2b3}, //music_3020002 {0x5e91a3790c32e2b3}, //music_3020002
{0x358adfd1bbd3a95e}, //music_3020003 {0x358adfd1bbd3a95e}, //music_3020003
{0x1948edf7ff41e79b}, //music_3020004 {0x1948edf7ff41e79b}, //music_3020004
{0x100293729f35b4de}, //music_3020005 {0x100293729f35b4de}, //music_3020005
{0x140ac59d2b870a13}, //music_3020006
{0x402b13df5481d4e6}, //music_3020007
{0xdfad847a86a126bb}, //music_5030001 {0xdfad847a86a126bb}, //music_5030001
{0x711ef85045b8c26e}, //music_5030002 {0x711ef85045b8c26e}, //music_5030002
{0xff7640b46d72b337}, //music_5030003 {0xff7640b46d72b337}, //music_5030003
@ -623,6 +638,8 @@ static const hcakey_info hcakey_list[] = {
{0x24c0b49097e9ebff}, //music_5030029 {0x24c0b49097e9ebff}, //music_5030029
{0x2ecdf66c680f3a45}, //music_5030030 {0x2ecdf66c680f3a45}, //music_5030030
{0x54aaada4a1b8deef}, //music_5030031 {0x54aaada4a1b8deef}, //music_5030031
{0x46bed365593c560c}, //music_5030032
{0xa954b315630e3ed0}, //music_5030033
{0x8328668369631cc1}, //music_5030034 {0x8328668369631cc1}, //music_5030034
{0xa5c1adeb7919845f}, //music_5030035 {0xa5c1adeb7919845f}, //music_5030035
{0x8e35d68632fc0d77}, //music_5030036 {0x8e35d68632fc0d77}, //music_5030036
@ -631,6 +648,9 @@ static const hcakey_info hcakey_list[] = {
{0x6abcc90be62f2cec}, //music_5030039 {0x6abcc90be62f2cec}, //music_5030039
{0x7f617e396e9a1e5c}, //music_5030040 {0x7f617e396e9a1e5c}, //music_5030040
{0xd0471c163265ca1b}, //music_5030041 {0xd0471c163265ca1b}, //music_5030041
{0xd689966609595d7d}, //music_5030042
{0x32cb728ddab4d956}, //music_5030050
{0x52c5dfb61fe4c87a}, //music_5030054
{0x444dda6d55d76095}, //music_5040001 {0x444dda6d55d76095}, //music_5040001
{0xcbf4f1324081e0a6}, //music_5040002 {0xcbf4f1324081e0a6}, //music_5040002
{0xf1db3c1d9542063a}, //music_5040003 {0xf1db3c1d9542063a}, //music_5040003
@ -688,6 +708,10 @@ static const hcakey_info hcakey_list[] = {
{0xfa842bc07360137d}, //music_5050035 {0xfa842bc07360137d}, //music_5050035
{0xf8d72c405d3f0456}, //music_5050036 {0xf8d72c405d3f0456}, //music_5050036
{0xd4d5fa6c87342e6b}, //music_5050037 {0xd4d5fa6c87342e6b}, //music_5050037
{0xd8cbc946fa660944}, //music_5050038
{0xfac398719cd9e4a}, //music_5050039
{0x9c4ba796548a019}, //music_5050040
{0x7e7c462ba7d473cf}, //music_5050041
{0xe278eccf08eb2565}, //music_5050044 {0xe278eccf08eb2565}, //music_5050044
{0x1cf133b26d8160d1}, //music_5050045 {0x1cf133b26d8160d1}, //music_5050045
{0xda08e9d3961c93f2}, //music_5050046 {0xda08e9d3961c93f2}, //music_5050046
@ -702,6 +726,7 @@ static const hcakey_info hcakey_list[] = {
{0xbce9e85d31089fb2}, //music_5050056 {0xbce9e85d31089fb2}, //music_5050056
{0x817b919679c96d7}, //music_5050057 {0x817b919679c96d7}, //music_5050057
{0x3e0e51043bd7d5e5}, //music_5050058 {0x3e0e51043bd7d5e5}, //music_5050058
{0x86d17e28b2f2b91c}, //music_5050059
{0x115f906b6b7fb845}, //music_5050060 {0x115f906b6b7fb845}, //music_5050060
{0xa8d5e9b1c6cf1505}, //music_5050061 {0xa8d5e9b1c6cf1505}, //music_5050061
{0x69ffd3fefdf7ee71}, //music_5050062 {0x69ffd3fefdf7ee71}, //music_5050062
@ -710,10 +735,21 @@ static const hcakey_info hcakey_list[] = {
{0x27992dd621b8a07e}, //music_5050065 {0x27992dd621b8a07e}, //music_5050065
{0x8e2a8439f5628513}, //music_5050066 {0x8e2a8439f5628513}, //music_5050066
{0x8b5be21e70a84eed}, //music_5050067 {0x8b5be21e70a84eed}, //music_5050067
{0x227297416c6ccc7c}, //music_5050068
{0xb544dc8524419109}, //music_5050069 {0xb544dc8524419109}, //music_5050069
{0x6c2d9160672cbf95}, //music_5050070 {0x6c2d9160672cbf95}, //music_5050070
{0x7ff6630286d2d93b}, //music_5050071 {0x7ff6630286d2d93b}, //music_5050071
{0xc6deecd2d1391713}, //music_5050072
{0x78bec41dd27d8788}, //music_5050074 {0x78bec41dd27d8788}, //music_5050074
{0xf86991a3b9aec2b}, //music_5050075
{0x8f750fabaa794130}, //music_5050076
{0x3c68e8102dbec720}, //music_5050077
{0xf653b47bc8d4d1cd}, //music_5050079
{0xb50f482149140fda}, //music_5050080
{0xd61cc4e14e7073f4}, //music_5050081
{0x85a236b5270bac29}, //music_5050083
{0x598e133e0673b1e6}, //music_5050086
{0x3f8abfcd47711be2}, //music_5050088
{0x52c250eade92393b}, //music_9010001 {0x52c250eade92393b}, //music_9010001
{0xfea0d6adff136868}, //music_9050001 {0xfea0d6adff136868}, //music_9050001

View File

@ -5,47 +5,50 @@
/* HIS - Her Interactive games [Nancy Drew series (PC)] */ /* HIS - Her Interactive games [Nancy Drew series (PC)] */
VGMSTREAM * init_vgmstream_his(STREAMFILE *sf) { VGMSTREAM * init_vgmstream_his(STREAMFILE *sf) {
VGMSTREAM * vgmstream = NULL; VGMSTREAM * vgmstream = NULL;
int channel_count, loop_flag = 0, bps, sample_rate, num_samples, version; int channels, loop_flag = 0, bps, sample_rate, num_samples, version;
off_t start_offset; off_t start_offset;
/* checks */ /* checks */
if (!is_id32be(0x00,sf, "Her ") &&
!is_id32be(0x00,sf, "HIS\0"))
goto fail;
if (!check_extensions(sf, "his")) if (!check_extensions(sf, "his"))
goto fail; goto fail;
if (read_32bitBE(0x00,sf) == 0x48657220) { /* "Her Interactive Sound\x1a" */ if (is_id32be(0x00,sf, "Her ")) { /* "Her Interactive Sound\x1a" */
/* Nancy Drew: Secrets Can Kill (PC) */ /* Nancy Drew: Secrets Can Kill (PC) */
version = 0; version = 0;
channel_count = read_16bitLE(0x16,sf); channels = read_u16le(0x16,sf);
sample_rate = read_32bitLE(0x18,sf); sample_rate = read_u32le(0x18,sf);
/* 0x1c: bitrate */ /* 0x1c: bitrate */
/* 0x20: block size */ /* 0x20: block size */
bps = read_16bitLE(0x22,sf); bps = read_u16le(0x22,sf);
if (read_32bitBE(0x24,sf) != 0x64617461) /* "data" */ if (!is_id32be(0x24,sf, "data"))
goto fail; goto fail;
num_samples = pcm_bytes_to_samples(read_32bitLE(0x28,sf), channel_count, bps); num_samples = pcm_bytes_to_samples(read_u32le(0x28,sf), channels, bps);
start_offset = 0x2c; start_offset = 0x2c;
} }
else if (read_32bitBE(0x00,sf) == 0x48495300) { /* HIS\0 */ else if (is_id32be(0x00,sf, "HIS\0")) {
/* most(?) others */ /* most(?) others */
version = read_32bitLE(0x04,sf); version = read_u32le(0x04,sf);
/* 0x08: codec */ /* 0x08: codec */
channel_count = read_16bitLE(0x0a,sf); channels = read_u16le(0x0a,sf);
sample_rate = read_32bitLE(0x0c,sf); sample_rate = read_u32le(0x0c,sf);
/* 0x10: bitrate */ /* 0x10: bitrate */
/* 0x14: block size */ /* 0x14: block size */
bps = read_16bitLE(0x16,sf); bps = read_u16le(0x16,sf);
num_samples = pcm_bytes_to_samples(read_32bitLE(0x18,sf), channel_count, bps); /* true even for Ogg */ num_samples = pcm_bytes_to_samples(read_u32le(0x18,sf), channels, bps); /* true even for Ogg */
/* later games use "OggS" */ /* later games use "OggS" */
if (version == 1) if (version == 1)
start_offset = 0x1c; /* Nancy Drew: The Final Scene (PC) */ start_offset = 0x1c; /* Nancy Drew: The Final Scene (PC) */
else if (version == 2 && read_32bitBE(0x1e,sf) == 0x4F676753) else if (version == 2 && is_id32be(0x1e,sf, "OggS"))
start_offset = 0x1e; /* Nancy Drew: The Haunted Carousel (PC) */ start_offset = 0x1e; /* Nancy Drew: The Haunted Carousel (PC) */
else if (version == 2 && read_32bitBE(0x20,sf) == 0x4F676753) else if (version == 2 && is_id32be(0x20,sf, "OggS"))
start_offset = 0x20; /* Nancy Drew: The Silent Spy (PC) */ start_offset = 0x20; /* Nancy Drew: The Silent Spy (PC) */
else else
goto fail; goto fail;
@ -56,18 +59,14 @@ VGMSTREAM * init_vgmstream_his(STREAMFILE *sf) {
if (version == 2) { if (version == 2) {
#ifdef VGM_USE_VORBIS
ogg_vorbis_meta_info_t ovmi = {0}; ogg_vorbis_meta_info_t ovmi = {0};
ovmi.meta_type = meta_HIS; ovmi.meta_type = meta_HIS;
return init_vgmstream_ogg_vorbis_config(sf, start_offset, &ovmi); return init_vgmstream_ogg_vorbis_config(sf, start_offset, &ovmi);
#else
goto fail;
#endif
} }
/* build the VGMSTREAM */ /* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag); vgmstream = allocate_vgmstream(channels, loop_flag);
if (!vgmstream) goto fail; if (!vgmstream) goto fail;
vgmstream->meta_type = meta_HIS; vgmstream->meta_type = meta_HIS;
@ -89,7 +88,7 @@ VGMSTREAM * init_vgmstream_his(STREAMFILE *sf) {
goto fail; goto fail;
} }
if (!vgmstream_open_stream(vgmstream,sf,start_offset)) if (!vgmstream_open_stream(vgmstream, sf, start_offset))
goto fail; goto fail;
return vgmstream; return vgmstream;

View File

@ -10,14 +10,14 @@ VGMSTREAM* init_vgmstream_ikm_ps2(STREAMFILE* sf) {
/* checks */ /* checks */
if ( !check_extensions(sf,"ikm") ) if (!is_id32be(0x00,sf, "IKM\0"))
goto fail; goto fail;
if (read_u32be(0x00,sf) != 0x494B4D00) /* "IKM\0" */ if (!check_extensions(sf,"ikm"))
goto fail; goto fail;
if (read_u32be(0x40,sf) != 0x41535400) /* "AST\0" */
goto fail;
/* 0x20: type 03? */ /* 0x20: type 03? */
if (!is_id32be(0x40,sf, "AST\0"))
goto fail;
loop_flag = (read_s32le(0x14, sf) > 0); loop_flag = (read_s32le(0x14, sf) > 0);
channel_count = read_s32le(0x50, sf); channel_count = read_s32le(0x50, sf);
@ -53,23 +53,24 @@ VGMSTREAM* init_vgmstream_ikm_pc(STREAMFILE* sf) {
/* checks */ /* checks */
if ( !check_extensions(sf,"ikm") ) if (!is_id32be(0x00,sf, "IKM\0"))
goto fail; goto fail;
if (read_u32be(0x00,sf) != 0x494B4D00) /* "IKM\0" */ if (!check_extensions(sf,"ikm"))
goto fail; goto fail;
/* 0x20: type 01? */ /* 0x20: type 01? */
/* find "OggS" start */ /* find "OggS" start */
if (read_u32be(0x30,sf) == 0x4F676753) { if (is_id32be(0x30,sf, "OggS")) {
start_offset = 0x30; /* Chaos Legion (PC) */ start_offset = 0x30; /* Chaos Legion (PC) */
} else if (read_u32be(0x800,sf) == 0x4F676753) { }
else if (is_id32be(0x800,sf, "OggS")) {
start_offset = 0x800; /* Legend of Galactic Heroes (PC) */ start_offset = 0x800; /* Legend of Galactic Heroes (PC) */
} else { }
else {
goto fail; goto fail;
} }
#ifdef VGM_USE_VORBIS
{ {
ogg_vorbis_meta_info_t ovmi = {0}; ogg_vorbis_meta_info_t ovmi = {0};
@ -82,9 +83,6 @@ VGMSTREAM* init_vgmstream_ikm_pc(STREAMFILE* sf) {
vgmstream = init_vgmstream_ogg_vorbis_config(sf, start_offset, &ovmi); vgmstream = init_vgmstream_ogg_vorbis_config(sf, start_offset, &ovmi);
} }
#else
goto fail;
#endif
return vgmstream; return vgmstream;
@ -102,13 +100,14 @@ VGMSTREAM* init_vgmstream_ikm_psp(STREAMFILE* sf) {
/* checks */ /* checks */
if (!is_id32be(0x00,sf, "IKM\0"))
goto fail;
if (!check_extensions(sf,"ikm")) if (!check_extensions(sf,"ikm"))
goto fail; goto fail;
if (read_u32be(0x00,sf) != 0x494B4D00) /* "IKM\0" */
goto fail;
if (read_u32be(0x800,sf) != 0x52494646) /* "RIFF" */
goto fail;
/* 0x20: type 00? */ /* 0x20: type 00? */
if (!is_id32be(0x800,sf, "RIFF"))
goto fail;
/* loop values (pre-adjusted without encoder delay) at 0x14/18 are found in the RIFF too */ /* loop values (pre-adjusted without encoder delay) at 0x14/18 are found in the RIFF too */
data_size = read_s32le(0x24, sf); data_size = read_s32le(0x24, sf);

View File

@ -2,13 +2,6 @@
#include "../coding/coding.h" #include "../coding/coding.h"
static int is_id4(const char* test, off_t offset, STREAMFILE* sf) {
uint8_t buf[4];
if (read_streamfile(buf, offset, sizeof(buf), sf) != sizeof(buf))
return 0;
return memcmp(buf, test, sizeof(buf)) == 0; /* memcmp to allow "AB\0\0" */
}
/* LucasArts iMUSE (Interactive Music Streaming Engine) formats */ /* LucasArts iMUSE (Interactive Music Streaming Engine) formats */
VGMSTREAM* init_vgmstream_imuse(STREAMFILE* sf) { VGMSTREAM* init_vgmstream_imuse(STREAMFILE* sf) {
VGMSTREAM* vgmstream = NULL; VGMSTREAM* vgmstream = NULL;
@ -19,19 +12,13 @@ VGMSTREAM* init_vgmstream_imuse(STREAMFILE* sf) {
/* checks */ /* checks */
/* .imx: The Curse of Monkey Island (PC)
* .imc: Grim Fandango (multi)
* .wav: Grim Fandango (multi) RIFF sfx */
if (!check_extensions(sf, "imx,imc,wav,lwav"))
goto fail;
/* base decoder block table */ /* base decoder block table */
if (is_id4("COMP", 0x00, sf)) { /* The Curse of Monkey Island (PC), The Dig (PC) */ if (is_id32be(0x00, sf, "COMP")) { /* The Curse of Monkey Island (PC), The Dig (PC) */
int entries = read_u32be(0x04,sf); int entries = read_u32be(0x04,sf);
head_offset = 0x10 + entries * 0x10 + 0x02; /* base header + table + header size */ head_offset = 0x10 + entries * 0x10 + 0x02; /* base header + table + header size */
} }
else if (is_id4("MCMP", 0x00, sf)) { /* Grim Fandango (multi), Star Wars: X-Wing Alliance (PC) */ else if (is_id32be(0x00, sf, "MCMP")) { /* Grim Fandango (multi), Star Wars: X-Wing Alliance (PC) */
int entries = read_u16be(0x04,sf); int entries = read_u16be(0x04,sf);
head_offset = 0x06 + entries * 0x09; /* base header + table */ head_offset = 0x06 + entries * 0x09; /* base header + table */
head_offset += 0x02 + read_u16be(head_offset, sf); /* + mini text header */ head_offset += 0x02 + read_u16be(head_offset, sf); /* + mini text header */
@ -40,17 +27,23 @@ VGMSTREAM* init_vgmstream_imuse(STREAMFILE* sf) {
goto fail; goto fail;
} }
/* .imx: The Curse of Monkey Island (PC)
* .imc: Grim Fandango (multi)
* .wav: Grim Fandango (multi) RIFF sfx */
if (!check_extensions(sf, "imx,imc,wav,lwav"))
goto fail;
/* "offsets" below seem to count decoded data. Data is divided into variable-sized blocks that usually /* "offsets" below seem to count decoded data. Data is divided into variable-sized blocks that usually
* return 0x2000 bytes (starting from and including header). File starts with a block table to make * return 0x2000 bytes (starting from and including header). File starts with a block table to make
* this manageable. Most offsets don't seem to match block or data boundaries so not really sure. */ * this manageable. Most offsets don't seem to match block or data boundaries so not really sure. */
/* main header after table */ /* main header after table */
if (is_id4("iMUS", head_offset, sf)) { /* COMP/MCMP */ if (is_id32be(head_offset, sf, "iMUS")) { /* COMP/MCMP */
int header_found = 0; int header_found = 0;
/* 0x04: decompressed size (header size + pcm bytes) */ /* 0x04: decompressed size (header size + pcm bytes) */
if (!is_id4("MAP ", head_offset + 0x08, sf)) if (!is_id32be(head_offset + 0x08, sf, "MAP "))
goto fail; goto fail;
map_size = read_u32be(head_offset + 0x0c, sf); map_size = read_u32be(head_offset + 0x0c, sf);
map_offset = head_offset + 0x10; map_offset = head_offset + 0x10;
@ -105,12 +98,12 @@ VGMSTREAM* init_vgmstream_imuse(STREAMFILE* sf) {
if (!header_found) if (!header_found)
goto fail; goto fail;
if (!is_id4("DATA", head_offset + 0x10 + map_size + 0x00, sf)) if (!is_id32be(head_offset + 0x10 + map_size + 0x00, sf, "DATA"))
goto fail; goto fail;
data_bytes = read_u32be(head_offset + 0x10 + map_size + 0x04, sf); data_bytes = read_u32be(head_offset + 0x10 + map_size + 0x04, sf);
num_samples = data_bytes / channels / sizeof(int16_t); num_samples = data_bytes / channels / sizeof(int16_t);
} }
else if (is_id4("RIFF", head_offset, sf)) { /* MCMP voices */ else if (is_id32be(head_offset, sf, "RIFF")) { /* MCMP voices */
/* standard (LE), with fake codec 1 and sizes also in decoded bytes (see above), /* standard (LE), with fake codec 1 and sizes also in decoded bytes (see above),
* has standard RIFF chunks (may include extra), start offset in MCSC */ * has standard RIFF chunks (may include extra), start offset in MCSC */
@ -124,7 +117,8 @@ VGMSTREAM* init_vgmstream_imuse(STREAMFILE* sf) {
num_samples = data_bytes / channels / sizeof(int16_t); num_samples = data_bytes / channels / sizeof(int16_t);
} }
else { else {
goto fail; /* The Dig (PC) has no header, detect? */ vgm_logi("IMUSE: unsupported format\n");
goto fail; /* The Dig (PC) has no header, detect? (needs a bunch of sub-codecs) */
} }
loop_flag = 0; loop_flag = 0;

View File

@ -3,6 +3,8 @@
#include "../vgmstream.h" #include "../vgmstream.h"
typedef VGMSTREAM* (*init_vgmstream_t)(STREAMFILE* sf);
VGMSTREAM* init_vgmstream_silence(int channels, int sample_rate, int32_t num_samples); VGMSTREAM* init_vgmstream_silence(int channels, int sample_rate, int32_t num_samples);
VGMSTREAM* init_vgmstream_silence_container(int total_subsongs); VGMSTREAM* init_vgmstream_silence_container(int total_subsongs);
@ -116,8 +118,7 @@ VGMSTREAM * init_vgmstream_vpk(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_genh(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_genh(STREAMFILE *streamFile);
#ifdef VGM_USE_VORBIS VGMSTREAM* init_vgmstream_ogg_vorbis(STREAMFILE* sf);
VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile);
typedef struct { typedef struct {
int loop_flag; int loop_flag;
@ -142,16 +143,15 @@ typedef struct {
} ogg_vorbis_meta_info_t; } ogg_vorbis_meta_info_t;
VGMSTREAM* init_vgmstream_ogg_vorbis_config(STREAMFILE *sf, off_t start, const ogg_vorbis_meta_info_t* ovmi); VGMSTREAM* init_vgmstream_ogg_vorbis_config(STREAMFILE* sf, off_t start, const ogg_vorbis_meta_info_t* ovmi);
#endif
VGMSTREAM * init_vgmstream_hca(STREAMFILE *streamFile); VGMSTREAM* init_vgmstream_hca(STREAMFILE* sf);
VGMSTREAM * init_vgmstream_hca_subkey(STREAMFILE *streamFile, uint16_t subkey); VGMSTREAM* init_vgmstream_hca_subkey(STREAMFILE* sf, uint16_t subkey);
#ifdef VGM_USE_FFMPEG #ifdef VGM_USE_FFMPEG
VGMSTREAM * init_vgmstream_ffmpeg(STREAMFILE *streamFile); VGMSTREAM* init_vgmstream_ffmpeg(STREAMFILE* sf);
VGMSTREAM * init_vgmstream_mp4_aac_ffmpeg(STREAMFILE * streamFile); VGMSTREAM* init_vgmstream_mp4_aac_ffmpeg(STREAMFILE* sf);
#endif #endif
#if defined(VGM_USE_MP4V2) && defined(VGM_USE_FDKAAC) #if defined(VGM_USE_MP4V2) && defined(VGM_USE_FDKAAC)

View File

@ -1,39 +1,27 @@
/*
2017-12-10: Preliminary MOGG Support. As long as the stream is unencrypted, this should be fine.
This will also work on unconventional 5 channel Vorbis streams but some sound cards might not like it.
TODO (Eventually): Add decryption for encrypted MOGG types (Rock Band, etc.)
-bxaimc
*/
#include "meta.h" #include "meta.h"
#include "../coding/coding.h" #include "../coding/coding.h"
/* MOGG - Harmonix Music Systems (Guitar Hero)[Unencrypted Type] */ /* MOGG - Harmonix Music Systems's Ogg (unencrypted type) [Guitar Hero II (X360)] */
VGMSTREAM* init_vgmstream_mogg(STREAMFILE *sf) { VGMSTREAM* init_vgmstream_mogg(STREAMFILE* sf) {
#ifdef VGM_USE_VORBIS
off_t start_offset; off_t start_offset;
/* checks */ /* checks */
if (read_u32le(0x00, sf) != 0x0A) /* type? */
goto fail;
if (!check_extensions(sf, "mogg")) if (!check_extensions(sf, "mogg"))
goto fail; goto fail;
{ {
ogg_vorbis_meta_info_t ovmi = {0}; ogg_vorbis_meta_info_t ovmi = {0};
VGMSTREAM * result = NULL;
ovmi.meta_type = meta_MOGG; ovmi.meta_type = meta_MOGG;
start_offset = read_32bitLE(0x04, sf); start_offset = read_u32le(0x04, sf);
result = init_vgmstream_ogg_vorbis_config(sf, start_offset, &ovmi); return init_vgmstream_ogg_vorbis_config(sf, start_offset, &ovmi);
if (result != NULL) {
return result;
}
} }
fail: fail:
/* clean up anything we may have opened */
#endif
return NULL; return NULL;
} }

View File

@ -6,30 +6,27 @@
/* MUPS - from Watermelon/HUCARD games (same programmer) [Pier Solar and the Great Architects (PC), Ghost Blade HD (PC/Switch)] */ /* MUPS - from Watermelon/HUCARD games (same programmer) [Pier Solar and the Great Architects (PC), Ghost Blade HD (PC/Switch)] */
VGMSTREAM* init_vgmstream_mups(STREAMFILE* sf) { VGMSTREAM* init_vgmstream_mups(STREAMFILE* sf) {
VGMSTREAM* vgmstream = NULL; VGMSTREAM* vgmstream = NULL;
STREAMFILE *temp_sf = NULL; STREAMFILE* temp_sf = NULL;
/* checks */ /* checks */
if (!is_id32be(0x00,sf, "MUPS"))
goto fail;
/* mups: header id? /* mups: header id?
* (extensionless): default? */ * (extensionless): default? */
if (!check_extensions(sf, "mups,")) if (!check_extensions(sf, "mups,"))
goto fail; goto fail;
if (read_u32be(0x00,sf) != 0x4D555053) /* "MUPS" */ if (!is_id32be(0x08,sf, "PssH"))
goto fail;
if (read_u32be(0x08,sf) != 0x50737348) /* "PssH" */
goto fail; goto fail;
/* just an Ogg with changed OggS/vorbis words (see streamfile) */ /* just an Ogg with changed OggS/vorbis words (see streamfile) */
temp_sf = setup_mups_streamfile(sf, 0x08); temp_sf = setup_mups_streamfile(sf, 0x08);
if (!temp_sf) goto fail; if (!temp_sf) goto fail;
#ifdef VGM_USE_VORBIS
vgmstream = init_vgmstream_ogg_vorbis(temp_sf); vgmstream = init_vgmstream_ogg_vorbis(temp_sf);
if (!vgmstream) goto fail; if (!vgmstream) goto fail;
#else
goto fail;
#endif
close_streamfile(temp_sf); close_streamfile(temp_sf);

View File

@ -11,11 +11,11 @@
#endif #endif
static char** parse_mus(STREAMFILE *streamFile, int *out_file_count, int *out_loop_flag, int *out_loop_start_index, int *out_loop_end_index); static char** parse_mus(STREAMFILE* sf, int *out_file_count, int *out_loop_flag, int *out_loop_start_index, int *out_loop_end_index);
static void clean_mus(char** mus_filenames, int file_count); static void clean_mus(char** mus_filenames, int file_count);
/* .MUS - playlist for InterPlay games [Planescape: Torment (PC), Baldur's Gate Enhanced Edition (PC)] */ /* .MUS - playlist for InterPlay games [Planescape: Torment (PC), Baldur's Gate Enhanced Edition (PC)] */
VGMSTREAM * init_vgmstream_mus_acm(STREAMFILE *streamFile) { VGMSTREAM* init_vgmstream_mus_acm(STREAMFILE* sf) {
VGMSTREAM * vgmstream = NULL; VGMSTREAM * vgmstream = NULL;
segmented_layout_data *data = NULL; segmented_layout_data *data = NULL;
@ -27,11 +27,11 @@ VGMSTREAM * init_vgmstream_mus_acm(STREAMFILE *streamFile) {
/* checks */ /* checks */
if (!check_extensions(streamFile, "mus")) if (!check_extensions(sf, "mus"))
goto fail; goto fail;
/* get file paths from the .MUS text file */ /* get file paths from the .MUS text file */
mus_filenames = parse_mus(streamFile, &segment_count, &loop_flag, &loop_start_index, &loop_end_index); mus_filenames = parse_mus(sf, &segment_count, &loop_flag, &loop_start_index, &loop_end_index);
if (!mus_filenames) goto fail; if (!mus_filenames) goto fail;
/* init layout */ /* init layout */
@ -40,24 +40,22 @@ VGMSTREAM * init_vgmstream_mus_acm(STREAMFILE *streamFile) {
/* open each segment subfile */ /* open each segment subfile */
for (i = 0; i < segment_count; i++) { for (i = 0; i < segment_count; i++) {
STREAMFILE* temp_streamFile = streamFile->open(streamFile, mus_filenames[i], STREAMFILE_DEFAULT_BUFFER_SIZE); STREAMFILE* temp_sf = sf->open(sf, mus_filenames[i], STREAMFILE_DEFAULT_BUFFER_SIZE);
if (!temp_streamFile) goto fail; if (!temp_sf) goto fail;
/* find .ACM type */ /* find .ACM type */
switch(read_32bitBE(0x00,temp_streamFile)) { switch(read_32bitBE(0x00,temp_sf)) {
case 0x97280301: /* ACM header id [Planescape: Torment (PC)] */ case 0x97280301: /* ACM header id [Planescape: Torment (PC)] */
data->segments[i] = init_vgmstream_acm(temp_streamFile); data->segments[i] = init_vgmstream_acm(temp_sf);
break; break;
#ifdef VGM_USE_VORBIS
case 0x4F676753: /* "OggS" [Planescape: Torment Enhanced Edition (PC)] */ case 0x4F676753: /* "OggS" [Planescape: Torment Enhanced Edition (PC)] */
data->segments[i] = init_vgmstream_ogg_vorbis(temp_streamFile); data->segments[i] = init_vgmstream_ogg_vorbis(temp_sf);
break; break;
#endif
default: default:
data->segments[i] = NULL; data->segments[i] = NULL;
break; break;
} }
close_streamfile(temp_streamFile); close_streamfile(temp_sf);
if (!data->segments[i]) goto fail; if (!data->segments[i]) goto fail;
@ -184,7 +182,7 @@ fail:
return 1; return 1;
} }
static char** parse_mus(STREAMFILE *streamFile, int *out_file_count, int *out_loop_flag, int *out_loop_start_index, int *out_loop_end_index) { static char** parse_mus(STREAMFILE *sf, int *out_file_count, int *out_loop_flag, int *out_loop_start_index, int *out_loop_end_index) {
char** names = NULL; char** names = NULL;
char filename[NAME_LENGTH]; char filename[NAME_LENGTH];
@ -204,7 +202,7 @@ static char** parse_mus(STREAMFILE *streamFile, int *out_file_count, int *out_lo
/* read file name base */ /* read file name base */
bytes_read = read_line(line, sizeof(line), mus_offset, streamFile, &line_ok); bytes_read = read_line(line, sizeof(line), mus_offset, sf, &line_ok);
if (!line_ok) goto fail; if (!line_ok) goto fail;
mus_offset += bytes_read; mus_offset += bytes_read;
memcpy(name_base,line,sizeof(name_base)); memcpy(name_base,line,sizeof(name_base));
@ -217,7 +215,7 @@ static char** parse_mus(STREAMFILE *streamFile, int *out_file_count, int *out_lo
} }
/* read track entry count */ /* read track entry count */
bytes_read = read_line(line, sizeof(line), mus_offset, streamFile, &line_ok); bytes_read = read_line(line, sizeof(line), mus_offset, sf, &line_ok);
if (!line_ok) goto fail; if (!line_ok) goto fail;
if (line[0] == '\0') goto fail; if (line[0] == '\0') goto fail;
mus_offset += bytes_read; mus_offset += bytes_read;
@ -235,7 +233,7 @@ static char** parse_mus(STREAMFILE *streamFile, int *out_file_count, int *out_lo
} }
dir_name[0]='\0'; dir_name[0]='\0';
streamFile->get_name(streamFile,filename,sizeof(filename)); sf->get_name(sf,filename,sizeof(filename));
concatn(sizeof(dir_name),dir_name,filename); concatn(sizeof(dir_name),dir_name,filename);
/* find directory name for the directory contianing the MUS */ /* find directory name for the directory contianing the MUS */
@ -262,7 +260,7 @@ static char** parse_mus(STREAMFILE *streamFile, int *out_file_count, int *out_lo
for (i = 0; i < file_count; i++) for (i = 0; i < file_count; i++)
{ {
int fields_matched; int fields_matched;
bytes_read = read_line(line,sizeof(line), mus_offset, streamFile, &line_ok); bytes_read = read_line(line,sizeof(line), mus_offset, sf, &line_ok);
if (!line_ok) goto fail; if (!line_ok) goto fail;
mus_offset += bytes_read; mus_offset += bytes_read;
@ -308,13 +306,13 @@ static char** parse_mus(STREAMFILE *streamFile, int *out_file_count, int *out_lo
concatn(NAME_LENGTH,names[i],name); concatn(NAME_LENGTH,names[i],name);
concatn(NAME_LENGTH,names[i],".ACM"); concatn(NAME_LENGTH,names[i],".ACM");
if (!exists(names[i],streamFile)) { if (!exists(names[i],sf)) {
/* We can't test for the directory until we have a file name /* We can't test for the directory until we have a file name
* to look for, so we do it here with the first file that seems to * to look for, so we do it here with the first file that seems to
* be in a subdirectory */ * be in a subdirectory */
if (subdir_name[0]=='\0') { if (subdir_name[0]=='\0') {
if (find_directory_name(name_base, dir_name, sizeof(subdir_name), subdir_name, name, filename, streamFile)) if (find_directory_name(name_base, dir_name, sizeof(subdir_name), subdir_name, name, filename, sf))
goto fail; goto fail;
} }
@ -325,7 +323,7 @@ static char** parse_mus(STREAMFILE *streamFile, int *out_file_count, int *out_lo
concatn(NAME_LENGTH,names[i],name); concatn(NAME_LENGTH,names[i],name);
concatn(NAME_LENGTH,names[i],".ACM"); concatn(NAME_LENGTH,names[i],".ACM");
if (!exists(names[i],streamFile)) goto fail; if (!exists(names[i],sf)) goto fail;
} }
} }

View File

@ -2,41 +2,43 @@
#include "../util.h" #include "../util.h"
/* STRM - common Nintendo NDS streaming format */ /* STRM - common Nintendo NDS streaming format */
VGMSTREAM * init_vgmstream_nds_strm(STREAMFILE *streamFile) { VGMSTREAM* init_vgmstream_nds_strm(STREAMFILE* sf) {
VGMSTREAM * vgmstream = NULL; VGMSTREAM* vgmstream = NULL;
off_t start_offset; off_t start_offset;
int channel_count, loop_flag, codec; int channels, loop_flag, codec, sample_rate;
/* checks */ /* checks */
if (!check_extensions(streamFile, "strm")) if (!is_id32be(0x00,sf, "STRM"))
goto fail; goto fail;
if (read_32bitBE(0x00,streamFile) != 0x5354524D) /* "STRM" */ if (!check_extensions(sf, "strm"))
goto fail;
if (read_32bitBE(0x04,streamFile) != 0xFFFE0001 && /* Old Header Check */
(read_32bitBE(0x04,streamFile) != 0xFEFF0001)) /* Some newer games have a new flag */
goto fail; goto fail;
if (read_32bitBE(0x10,streamFile) != 0x48454144 && /* "HEAD" */ /* BOM check? */
read_32bitLE(0x14,streamFile) != 0x50) /* 0x50-sized head is all I've seen */ if (read_u32be(0x04,sf) != 0xFFFE0001 &&
read_u32be(0x04,sf) != 0xFEFF0001) /* newer games? */
goto fail; goto fail;
codec = read_8bit(0x18,streamFile); if (!is_id32be(0x10,sf, "HEAD") &&
loop_flag = read_8bit(0x19,streamFile); read_u32le(0x14,sf) != 0x50)
channel_count = read_8bit(0x1a,streamFile); goto fail;
if (channel_count > 2) goto fail;
start_offset = read_32bitLE(0x28,streamFile); codec = read_u8(0x18,sf);
loop_flag = read_u8(0x19,sf);
sample_rate = read_u16le(0x1c,sf);
channels = read_u8(0x1a,sf);
if (channels > 2) goto fail;
start_offset = read_u32le(0x28,sf);
/* build the VGMSTREAM */ /* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag); vgmstream = allocate_vgmstream(channels, loop_flag);
if (!vgmstream) goto fail; if (!vgmstream) goto fail;
vgmstream->sample_rate = (uint16_t)read_16bitLE(0x1c,streamFile); vgmstream->sample_rate = sample_rate;
vgmstream->num_samples = read_32bitLE(0x24,streamFile); vgmstream->num_samples = read_32bitLE(0x24,sf);
vgmstream->loop_start_sample = read_32bitLE(0x20,streamFile); vgmstream->loop_start_sample = read_32bitLE(0x20,sf);
vgmstream->loop_end_sample = vgmstream->num_samples; vgmstream->loop_end_sample = vgmstream->num_samples;
vgmstream->meta_type = meta_STRM; vgmstream->meta_type = meta_STRM;
@ -55,11 +57,11 @@ VGMSTREAM * init_vgmstream_nds_strm(STREAMFILE *streamFile) {
goto fail; goto fail;
} }
vgmstream->layout_type = layout_interleave; vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = read_32bitLE(0x30,streamFile); vgmstream->interleave_block_size = read_32bitLE(0x30,sf);
vgmstream->interleave_last_block_size = read_32bitLE(0x38,streamFile); vgmstream->interleave_last_block_size = read_32bitLE(0x38,sf);
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) if (!vgmstream_open_stream(vgmstream, sf, start_offset))
goto fail; goto fail;
return vgmstream; return vgmstream;

View File

@ -2,37 +2,35 @@
#include "../coding/coding.h" #include "../coding/coding.h"
/* NWAV - from Chunsoft games [Fuurai no Shiren Gaiden: Onnakenshi Asuka Kenzan! (PC)] */ /* NWAV - from Chunsoft games [Fuurai no Shiren Gaiden: Onnakenshi Asuka Kenzan! (PC)] */
VGMSTREAM * init_vgmstream_nwav(STREAMFILE *sf) { VGMSTREAM* init_vgmstream_nwav(STREAMFILE* sf) {
VGMSTREAM * vgmstream = NULL;
off_t start_offset; off_t start_offset;
/* checks */ /* checks */
if (!is_id32be(0x00,sf, "NWAV"))
goto fail;
/* .nwav: header id (no filenames in bigfiles) */ /* .nwav: header id (no filenames in bigfiles) */
if ( !check_extensions(sf,"nwav") ) if (!check_extensions(sf,"nwav,") )
goto fail;
if (read_32bitBE(0x00,sf) != 0x4E574156) /* "NWAV" */
goto fail; goto fail;
#ifdef VGM_USE_VORBIS
{ {
ogg_vorbis_meta_info_t ovmi = {0}; ogg_vorbis_meta_info_t ovmi = {0};
int channels; int channels;
/* 0x04: version? */ /* 0x04: version? */
/* 0x08: crc? */ /* 0x08: crc? */
ovmi.stream_size = read_32bitLE(0x0c, sf); ovmi.stream_size = read_u32le(0x0c, sf);
ovmi.loop_end = read_32bitLE(0x10, sf); /* num_samples, actually */ ovmi.loop_end = read_u32le(0x10, sf); /* num_samples, actually */
/* 0x14: sample rate */ /* 0x14: sample rate */
/* 0x18: bps? (16) */ /* 0x18: bps? (16) */
channels = read_8bit(0x19, sf); channels = read_u8(0x19, sf);
start_offset = read_16bitLE(0x1a, sf); start_offset = read_u16le(0x1a, sf);
ovmi.loop_flag = read_16bitLE(0x1c, sf) != 0; /* loop count? -1 = loops */ ovmi.loop_flag = read_u16le(0x1c, sf) != 0; /* loop count? -1 = loops */
/* 0x1e: always 2? */ /* 0x1e: always 2? */
/* 0x20: always 1? */ /* 0x20: always 1? */
ovmi.loop_start = read_32bitLE(0x24, sf); ovmi.loop_start = read_u32le(0x24, sf);
/* 0x28: always 1? */ /* 0x28: always 1? */
/* 0x2a: always 1? */ /* 0x2a: always 1? */
/* 0x2c: always null? */ /* 0x2c: always null? */
@ -43,15 +41,9 @@ VGMSTREAM * init_vgmstream_nwav(STREAMFILE *sf) {
ovmi.loop_start = ovmi.loop_start / sizeof(int16_t) / channels; ovmi.loop_start = ovmi.loop_start / sizeof(int16_t) / channels;
ovmi.loop_end = ovmi.loop_end / sizeof(int16_t) / channels; ovmi.loop_end = ovmi.loop_end / sizeof(int16_t) / channels;
vgmstream = init_vgmstream_ogg_vorbis_config(sf, start_offset, &ovmi); return init_vgmstream_ogg_vorbis_config(sf, start_offset, &ovmi);
} }
#else
goto fail;
#endif
return vgmstream;
fail: fail:
close_vgmstream(vgmstream);
return NULL; return NULL;
} }

View File

@ -1,4 +1,3 @@
#ifdef VGM_USE_VORBIS
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include "meta.h" #include "meta.h"
@ -6,6 +5,28 @@
#include "ogg_vorbis_streamfile.h" #include "ogg_vorbis_streamfile.h"
#ifdef VGM_USE_VORBIS
static VGMSTREAM* _init_vgmstream_ogg_vorbis(STREAMFILE* sf);
static VGMSTREAM* _init_vgmstream_ogg_vorbis_config(STREAMFILE* sf, off_t start, const ogg_vorbis_meta_info_t* ovmi);
#endif
VGMSTREAM* init_vgmstream_ogg_vorbis(STREAMFILE* sf) {
#ifdef VGM_USE_VORBIS
return _init_vgmstream_ogg_vorbis(sf);
#else
return NULL;
#endif
}
VGMSTREAM* init_vgmstream_ogg_vorbis_config(STREAMFILE* sf, off_t start, const ogg_vorbis_meta_info_t* ovmi) {
#ifdef VGM_USE_VORBIS
return _init_vgmstream_ogg_vorbis_config(sf, start, ovmi);
#else
return NULL;
#endif
}
#ifdef VGM_USE_VORBIS
static void um3_ogg_decryption_callback(void* ptr, size_t size, size_t nmemb, void* datasource) { static void um3_ogg_decryption_callback(void* ptr, size_t size, size_t nmemb, void* datasource) {
uint8_t *ptr8 = ptr; uint8_t *ptr8 = ptr;
size_t bytes_read = size * nmemb; size_t bytes_read = size * nmemb;
@ -110,8 +131,8 @@ static const uint32_t xiph_mappings[] = {
}; };
/* Ogg Vorbis, may contain loop comments */ /* Ogg Vorbis - standard .ogg with (possibly) loop comments/metadata */
VGMSTREAM* init_vgmstream_ogg_vorbis(STREAMFILE* sf) { static VGMSTREAM* _init_vgmstream_ogg_vorbis(STREAMFILE* sf) {
VGMSTREAM* vgmstream = NULL; VGMSTREAM* vgmstream = NULL;
STREAMFILE* temp_sf = NULL; STREAMFILE* temp_sf = NULL;
ogg_vorbis_io_config_data cfg = {0}; ogg_vorbis_io_config_data cfg = {0};
@ -416,7 +437,7 @@ VGMSTREAM* init_vgmstream_ogg_vorbis(STREAMFILE* sf) {
ovmi.meta_type = meta_OGG_VORBIS; ovmi.meta_type = meta_OGG_VORBIS;
} }
vgmstream = init_vgmstream_ogg_vorbis_config(temp_sf != NULL ? temp_sf : sf, start_offset, &ovmi); vgmstream = _init_vgmstream_ogg_vorbis_config(temp_sf != NULL ? temp_sf : sf, start_offset, &ovmi);
close_streamfile(temp_sf); close_streamfile(temp_sf);
return vgmstream; return vgmstream;
@ -426,7 +447,7 @@ fail:
return NULL; return NULL;
} }
VGMSTREAM* init_vgmstream_ogg_vorbis_config(STREAMFILE* sf, off_t start, const ogg_vorbis_meta_info_t* ovmi) { static VGMSTREAM* _init_vgmstream_ogg_vorbis_config(STREAMFILE* sf, off_t start, const ogg_vorbis_meta_info_t* ovmi) {
VGMSTREAM* vgmstream = NULL; VGMSTREAM* vgmstream = NULL;
ogg_vorbis_codec_data* data = NULL; ogg_vorbis_codec_data* data = NULL;
ogg_vorbis_io io = {0}; ogg_vorbis_io io = {0};

View File

@ -3,15 +3,14 @@
/* OGV - .ogg container (not related to ogv video) [Bloody Rondo (PC)] */ /* OGV - .ogg container (not related to ogv video) [Bloody Rondo (PC)] */
VGMSTREAM* init_vgmstream_ogv_3rdeye(STREAMFILE* sf) { VGMSTREAM* init_vgmstream_ogv_3rdeye(STREAMFILE* sf) {
VGMSTREAM* vgmstream = NULL; uint32_t subfile_offset, subfile_size;
off_t subfile_offset, subfile_size;
/* checks */ /* checks */
if (!check_extensions(sf,"ogv"))
goto fail;
if (!is_id32be(0x00,sf, "OGV\0")) if (!is_id32be(0x00,sf, "OGV\0"))
goto fail; goto fail;
if (!check_extensions(sf,"ogv"))
goto fail;
/* 0x04: PCM size */ /* 0x04: PCM size */
subfile_size = read_u32le(0x08, sf); subfile_size = read_u32le(0x08, sf);
@ -20,21 +19,15 @@ VGMSTREAM* init_vgmstream_ogv_3rdeye(STREAMFILE* sf) {
/* no loops (files bgm does full loops but sfx doesn't) */ /* no loops (files bgm does full loops but sfx doesn't) */
#ifdef VGM_USE_VORBIS
{ {
ogg_vorbis_meta_info_t ovmi = {0}; ogg_vorbis_meta_info_t ovmi = {0};
ovmi.meta_type = meta_OGV_3RDEYE; ovmi.meta_type = meta_OGV_3RDEYE;
ovmi.stream_size = subfile_size; ovmi.stream_size = subfile_size;
vgmstream = init_vgmstream_ogg_vorbis_config(sf, subfile_offset, &ovmi); return init_vgmstream_ogg_vorbis_config(sf, subfile_offset, &ovmi);
} }
#else
goto fail;
#endif
return vgmstream;
fail: fail:
close_vgmstream(vgmstream);
return NULL; return NULL;
} }

View File

@ -132,15 +132,27 @@ VGMSTREAM* init_vgmstream_psb(STREAMFILE* sf) {
switch(psb.codec) { switch(psb.codec) {
case PCM: case PCM:
if (psb.layers > 1) {
/* somehow R offset can go before L, use layered */
vgmstream->layout_data = build_layered_psb(sf, &psb);
if (!vgmstream->layout_data) goto fail;
vgmstream->layout_type = layout_layered;
if (!vgmstream->num_samples)
vgmstream->num_samples = pcm_bytes_to_samples(psb.stream_size[0], 1, psb.bps);
}
else {
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = psb.block_size / psb.channels;
if (!vgmstream->num_samples)
vgmstream->num_samples = pcm_bytes_to_samples(psb.stream_size[0], psb.channels, psb.bps);
}
switch(psb.bps) { switch(psb.bps) {
case 16: vgmstream->coding_type = coding_PCM16LE; break; /* Legend of Mana (PC), Namco Museum Archives Vol.1 (PC) */ case 16: vgmstream->coding_type = coding_PCM16LE; break; /* Legend of Mana (PC), Namco Museum Archives Vol.1 (PC) */
case 24: vgmstream->coding_type = coding_PCM24LE; break; /* Legend of Mana (PC) */ case 24: vgmstream->coding_type = coding_PCM24LE; break; /* Legend of Mana (PC) */
default: goto fail; default: goto fail;
} }
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = psb.block_size / psb.channels;
if (!vgmstream->num_samples)
vgmstream->num_samples = pcm_bytes_to_samples(psb.stream_size[0], psb.channels, psb.bps);
break; break;
case MSADPCM: /* [Senxin Aleste (AC)] */ case MSADPCM: /* [Senxin Aleste (AC)] */
@ -279,6 +291,21 @@ fail:
return NULL; return NULL;
} }
static VGMSTREAM* try_init_vgmstream(STREAMFILE* sf, init_vgmstream_t init_vgmstream, const char* extension, uint32_t offset, uint32_t size) {
STREAMFILE* temp_sf = NULL;
VGMSTREAM* v = NULL;
temp_sf = setup_subfile_streamfile(sf, offset, size, extension);
if (!temp_sf) goto fail;
v = init_vgmstream(temp_sf);
close_streamfile(temp_sf);
return v;
fail:
return NULL;
}
static layered_layout_data* build_layered_psb(STREAMFILE* sf, psb_header_t* psb) { static layered_layout_data* build_layered_psb(STREAMFILE* sf, psb_header_t* psb) {
layered_layout_data* data = NULL; layered_layout_data* data = NULL;
int i; int i;
@ -289,25 +316,39 @@ static layered_layout_data* build_layered_psb(STREAMFILE* sf, psb_header_t* psb)
if (!data) goto fail; if (!data) goto fail;
for (i = 0; i < psb->layers; i++) { for (i = 0; i < psb->layers; i++) {
STREAMFILE* temp_sf = NULL;
VGMSTREAM* (*init_vgmstream)(STREAMFILE* sf) = NULL;
const char* extension = NULL;
switch (psb->codec) { switch (psb->codec) {
case DSP: case PCM: {
extension = "adpcm"; VGMSTREAM* v = allocate_vgmstream(1, 0);
init_vgmstream = init_vgmstream_ngc_dsp_std_le; if (!v) goto fail;
data->layers[i] = v;
v->sample_rate = psb->sample_rate;
v->num_samples = psb->num_samples;
switch(psb->bps) {
case 16: v->coding_type = coding_PCM16LE; break;
case 24: v->coding_type = coding_PCM24LE; break;
default: goto fail;
}
v->layout_type = layout_none;
if (!v->num_samples)
v->num_samples = pcm_bytes_to_samples(psb->stream_size[i], 1, psb->bps);
if (!vgmstream_open_stream(v, sf, psb->stream_offset[i]))
goto fail;
break; break;
}
case DSP:
data->layers[i] = try_init_vgmstream(sf, init_vgmstream_ngc_dsp_std_le, "adpcm", psb->stream_offset[i], psb->stream_size[i]);
if (!data->layers[i]) goto fail;
break;
default: default:
VGM_LOG("psb: layer not implemented\n");
goto fail; goto fail;
} }
temp_sf = setup_subfile_streamfile(sf, psb->stream_offset[i], psb->stream_size[i], extension);
if (!temp_sf) goto fail;
data->layers[i] = init_vgmstream(temp_sf);
close_streamfile(temp_sf);
if (!data->layers[i]) goto fail;
} }
/* setup layered VGMSTREAMs */ /* setup layered VGMSTREAMs */
@ -397,6 +438,7 @@ static int prepare_codec(STREAMFILE* sf, psb_header_t* psb) {
if (!ext) if (!ext)
goto fail; goto fail;
/* common, multichannel */
if (strcmp(ext, ".opus") == 0) { if (strcmp(ext, ".opus") == 0) {
psb->codec = OPUSNX; psb->codec = OPUSNX;
@ -409,12 +451,22 @@ static int prepare_codec(STREAMFILE* sf, psb_header_t* psb) {
return 1; return 1;
} }
/* Legend of Mana (Switch), layered */
if (strcmp(ext, ".adpcm") == 0) { if (strcmp(ext, ".adpcm") == 0) {
psb->codec = DSP; psb->codec = DSP;
psb->channels = psb->layers; psb->channels = psb->layers;
return 1; return 1;
} }
/* Castlevania Advance Collection (Switch), layered */
if (strcmp(ext, ".p16") == 0) {
psb->codec = PCM;
psb->bps = 16;
psb->channels = psb->layers;
return 1;
}
} }
if (strcmp(spec, "ps3") == 0) { if (strcmp(spec, "ps3") == 0) {

View File

@ -215,7 +215,13 @@ static int read_fmt(int big_endian, STREAMFILE* sf, off_t offset, riff_fmt_chunk
break; break;
#ifdef VGM_USE_VORBIS #ifdef VGM_USE_VORBIS
case 0x6771: /* Ogg Vorbis (mode 3+) */ //case 0x674f: /* Ogg Vorbis (mode 1) */
//case 0x6750: /* Ogg Vorbis (mode 2) */
//case 0x6751: /* Ogg Vorbis (mode 3) */
case 0x676f: /* Ogg Vorbis (mode 1+) [Only One 2 (PC)] */
//case 0x6770: /* Ogg Vorbis (mode 2+) */
case 0x6771: /* Ogg Vorbis (mode 3+) [Liar-soft games] */
/* vorbis.acm codecs (official-ish, "+" = CBR-style modes?) */
fmt->coding_type = coding_OGG_VORBIS; fmt->coding_type = coding_OGG_VORBIS;
break; break;
#endif #endif
@ -233,7 +239,7 @@ static int read_fmt(int big_endian, STREAMFILE* sf, off_t offset, riff_fmt_chunk
(read_u16 (offset+0x26,sf)); (read_u16 (offset+0x26,sf));
uint32_t guid3 = read_u32be(offset+0x28,sf); uint32_t guid3 = read_u32be(offset+0x28,sf);
uint32_t guid4 = read_u32be(offset+0x2c,sf); uint32_t guid4 = read_u32be(offset+0x2c,sf);
//;VGM_LOG("RIFF: guid %08x %08x %08x %08x\n", guid1, guid2, guid3, guid4); //;VGM_LOG("riff: guid %08x %08x %08x %08x\n", guid1, guid2, guid3, guid4);
/* PCM GUID (0x00000001,0000,0010,80,00,00,AA,00,38,9B,71) */ /* PCM GUID (0x00000001,0000,0010,80,00,00,AA,00,38,9B,71) */
if (guid1 == 0x00000001 && guid2 == 0x00000010 && guid3 == 0x800000AA && guid4 == 0x00389B71) { if (guid1 == 0x00000001 && guid2 == 0x00000010 && guid3 == 0x800000AA && guid4 == 0x00389B71) {
@ -280,7 +286,7 @@ static int read_fmt(int big_endian, STREAMFILE* sf, off_t offset, riff_fmt_chunk
default: default:
/* FFmpeg may play it */ /* FFmpeg may play it */
//vgm_logi("WWISE: unknown codec 0x%04x (report)\n", fmt->format); //vgm_logi("RIFF: unknown codec 0x%04x (report)\n", fmt->format);
goto fail; goto fail;
} }
@ -318,7 +324,10 @@ VGMSTREAM* init_vgmstream_riff(STREAMFILE* sf) {
off_t mwv_ctrl_offset = -1; off_t mwv_ctrl_offset = -1;
/* check extension */ /* checks*/
if (!is_id32be(0x00,sf,"RIFF"))
goto fail;
/* .lwav: to avoid hijacking .wav /* .lwav: to avoid hijacking .wav
* .xwav: fake for Xbox games (not needed anymore) * .xwav: fake for Xbox games (not needed anymore)
* .da: The Great Battle VI (PS1) * .da: The Great Battle VI (PS1)
@ -355,21 +364,19 @@ VGMSTREAM* init_vgmstream_riff(STREAMFILE* sf) {
goto fail; goto fail;
} }
/* check header */ riff_size = read_u32le(0x04,sf);
if (!is_id32be(0x00,sf,"RIFF"))
goto fail;
if (!is_id32be(0x08,sf, "WAVE")) if (!is_id32be(0x08,sf, "WAVE"))
goto fail; goto fail;
riff_size = read_u32le(0x04,sf);
file_size = get_streamfile_size(sf); file_size = get_streamfile_size(sf);
/* some games have wonky sizes, selectively fix to catch bad rips and new mutations */ /* some games have wonky sizes, selectively fix to catch bad rips and new mutations */
if (file_size != riff_size + 0x08) { if (file_size != riff_size + 0x08) {
uint16_t codec = read_u16le(0x14,sf); uint16_t codec = read_u16le(0x14,sf);
if (codec == 0x6771 && riff_size + 0x08 + 0x01 == file_size) if ((codec & 0xFF00) == 0x6700 && riff_size + 0x08 + 0x01 == file_size)
riff_size += 0x01; /* [Shikkoku no Sharnoth (PC)] (Sony Sound Forge?) */ riff_size += 0x01; /* [Shikkoku no Sharnoth (PC), Only One 2 (PC)] (Sony Sound Forge?) */
else if (codec == 0x0069 && riff_size == file_size) else if (codec == 0x0069 && riff_size == file_size)
riff_size -= 0x08; /* [Dynasty Warriors 3 (Xbox), BloodRayne (Xbox)] */ riff_size -= 0x08; /* [Dynasty Warriors 3 (Xbox), BloodRayne (Xbox)] */
@ -422,6 +429,7 @@ VGMSTREAM* init_vgmstream_riff(STREAMFILE* sf) {
/* check for truncated RIFF */ /* check for truncated RIFF */
if (file_size != riff_size + 0x08) { if (file_size != riff_size + 0x08) {
vgm_logi("RIFF: wrong expected size (report/re-rip?)\n"); vgm_logi("RIFF: wrong expected size (report/re-rip?)\n");
VGM_LOG("riff: file_size = %x, riff_size+8 = %x\n", file_size, riff_size + 0x08); /* don't log to user */
goto fail; goto fail;
} }
@ -774,8 +782,9 @@ VGMSTREAM* init_vgmstream_riff(STREAMFILE* sf) {
#endif #endif
#ifdef VGM_USE_VORBIS #ifdef VGM_USE_VORBIS
case coding_OGG_VORBIS: { case coding_OGG_VORBIS: {
/* special handling of Liar-soft's buggy RIFF+Ogg made with Soundforge [Shikkoku no Sharnoth (PC)] */ /* special handling of Liar-soft's buggy RIFF+Ogg made with Soundforge/vorbis.acm [Shikkoku no Sharnoth (PC)],
STREAMFILE *temp_sf = setup_riff_ogg_streamfile(sf, start_offset, data_size); * and rarely other devs, not always buggy [Kirara Kirara NTR (PC), No One 2 (PC)] */
STREAMFILE* temp_sf = setup_riff_ogg_streamfile(sf, start_offset, data_size);
if (!temp_sf) goto fail; if (!temp_sf) goto fail;
vgmstream->codec_data = init_ogg_vorbis(temp_sf, 0x00, get_streamfile_size(temp_sf), NULL); vgmstream->codec_data = init_ogg_vorbis(temp_sf, 0x00, get_streamfile_size(temp_sf), NULL);
@ -983,13 +992,13 @@ VGMSTREAM* init_vgmstream_rifx(STREAMFILE* sf) {
int FormatChunkFound = 0, DataChunkFound = 0; int FormatChunkFound = 0, DataChunkFound = 0;
/* check extension, case insensitive */ /* checks */
if (!is_id32be(0x00,sf, "RIFX"))
goto fail;
if (!check_extensions(sf, "wav,lwav")) if (!check_extensions(sf, "wav,lwav"))
goto fail; goto fail;
/* check header */
if (!is_id32be(0x00,sf, "RIFX"))
goto fail;
if (!is_id32be(0x08,sf, "WAVE")) if (!is_id32be(0x08,sf, "WAVE"))
goto fail; goto fail;

View File

@ -3,22 +3,22 @@
#include "deblock_streamfile.h" #include "deblock_streamfile.h"
typedef struct { typedef struct {
off_t patch_offset; uint32_t patch_offset;
} riff_ogg_io_data; } riff_ogg_io_data;
static size_t riff_ogg_io_read(STREAMFILE *sf, uint8_t *dest, off_t offset, size_t length, riff_ogg_io_data* data) { static size_t riff_ogg_io_read(STREAMFILE* sf, uint8_t* dst, uint32_t offset, size_t length, riff_ogg_io_data* data) {
size_t bytes = read_streamfile(dest, offset, length, sf); size_t bytes = read_streamfile(dst, offset, length, sf);
/* has garbage init Oggs pages, patch bad flag */ /* has garbage init Oggs pages, patch bad flag */
if (data->patch_offset && data->patch_offset >= offset && data->patch_offset < offset + bytes) { if (data->patch_offset && data->patch_offset >= offset && data->patch_offset < offset + bytes) {
VGM_ASSERT(dest[data->patch_offset - offset] != 0x02, "RIFF Ogg: bad patch offset at %lx\n", data->patch_offset); VGM_ASSERT(dst[data->patch_offset - offset] != 0x02, "RIFF Ogg: bad patch offset at %x\n", data->patch_offset);
dest[data->patch_offset - offset] = 0x00; dst[data->patch_offset - offset] = 0x00;
} }
return bytes; return bytes;
} }
static size_t ogg_get_page(uint8_t *buf, size_t bufsize, off_t offset, STREAMFILE *sf) { static size_t ogg_get_page(uint8_t* buf, size_t bufsize, uint32_t offset, STREAMFILE* sf) {
size_t segments, bytes, page_size; size_t segments, bytes, page_size;
int i; int i;
@ -43,8 +43,8 @@ fail:
} }
/* patches Ogg with weirdness */ /* patches Ogg with weirdness */
static STREAMFILE* setup_riff_ogg_streamfile(STREAMFILE *sf, off_t start, size_t size) { static STREAMFILE* setup_riff_ogg_streamfile(STREAMFILE* sf, uint32_t start, size_t size) {
off_t patch_offset = 0; uint32_t patch_offset = 0;
size_t real_size = size; size_t real_size = size;
uint8_t buf[0x1000]; uint8_t buf[0x1000];
@ -52,11 +52,11 @@ static STREAMFILE* setup_riff_ogg_streamfile(STREAMFILE *sf, off_t start, size_t
/* initial page flag is repeated and causes glitches in decoders, find bad offset */ /* initial page flag is repeated and causes glitches in decoders, find bad offset */
//todo callback could patch on-the-fly by analyzing all "OggS", but is problematic due to arbitrary offsets //todo callback could patch on-the-fly by analyzing all "OggS", but is problematic due to arbitrary offsets
{ {
off_t offset = start; uint32_t offset = start;
size_t page_size; size_t page_size;
off_t offset_limit = start + size; /* usually in the first 0x3000 but can be +0x100000 */ uint32_t offset_limit = start + size; /* usually in the first 0x3000 but can be +0x100000 */
//todo this doesn't seem to help much //todo this doesn't seem to help much
STREAMFILE *temp_sf = reopen_streamfile(sf, 0x100); /* use small-ish sf to avoid reading the whole thing */ STREAMFILE* temp_sf = reopen_streamfile(sf, 0x100); /* use small-ish sf to avoid reading the whole thing */
/* first page is ok */ /* first page is ok */
page_size = ogg_get_page(buf, sizeof(buf), offset, temp_sf); page_size = ogg_get_page(buf, sizeof(buf), offset, temp_sf);
@ -66,7 +66,7 @@ static STREAMFILE* setup_riff_ogg_streamfile(STREAMFILE *sf, off_t start, size_t
page_size = ogg_get_page(buf, sizeof(buf), offset, temp_sf); page_size = ogg_get_page(buf, sizeof(buf), offset, temp_sf);
if (page_size == 0) break; if (page_size == 0) break;
if (get_u32be(buf + 0x00) != 0x4f676753) /* "OggS" */ if (get_u32be(buf + 0x00) != get_id32be("OggS"))
break; break;
if (get_u16be(buf + 0x04) == 0x0002) { /* start page flag */ if (get_u16be(buf + 0x04) == 0x0002) { /* start page flag */
@ -80,8 +80,9 @@ static STREAMFILE* setup_riff_ogg_streamfile(STREAMFILE *sf, off_t start, size_t
close_streamfile(temp_sf); close_streamfile(temp_sf);
if (patch_offset == 0) /* no need to patch initial flag */
return NULL; //if (patch_offset == 0)
// return NULL;
} }
/* has a bunch of padding(?) pages at the end with no data nor flag that confuse decoders, find actual end */ /* has a bunch of padding(?) pages at the end with no data nor flag that confuse decoders, find actual end */
@ -89,14 +90,14 @@ static STREAMFILE* setup_riff_ogg_streamfile(STREAMFILE *sf, off_t start, size_t
size_t chunk_size = sizeof(buf); /* not worth testing more */ size_t chunk_size = sizeof(buf); /* not worth testing more */
size_t max_size = size; size_t max_size = size;
size_t pos; size_t pos;
off_t read_offset = start + size - chunk_size; uint32_t read_offset = start + size - chunk_size;
pos = read_streamfile(buf, read_offset, chunk_size, sf); pos = read_streamfile(buf, read_offset, chunk_size, sf);
if (read_offset < 0 || pos <= 0x1a) return NULL; if (read_offset < 0 || pos <= 0x1a) return NULL;
pos -= 0x1a; /* at least one OggS page */ pos -= 0x1a; /* at least one OggS page */
while (pos > 0) { while (pos > 0) {
if (get_u32be(buf + pos + 0x00) == 0x4f676753) { /* "OggS" */ if (get_u32be(buf + pos + 0x00) == get_id32be("OggS")) {
if (get_u16be(buf + pos + 0x04) == 0x0004) { /* last page flag is ok */ if (get_u16be(buf + pos + 0x04) == 0x0004) { /* last page flag is ok */
real_size = max_size; real_size = max_size;
@ -112,7 +113,7 @@ static STREAMFILE* setup_riff_ogg_streamfile(STREAMFILE *sf, off_t start, size_t
/* actual custom streamfile init */ /* actual custom streamfile init */
{ {
STREAMFILE *new_sf = NULL; STREAMFILE* new_sf = NULL;
riff_ogg_io_data io_data = {0}; riff_ogg_io_data io_data = {0};
io_data.patch_offset = patch_offset; io_data.patch_offset = patch_offset;

View File

@ -12,8 +12,8 @@ VGMSTREAM* init_vgmstream_sps_n1(STREAMFILE* sf) {
off_t subfile_offset; off_t subfile_offset;
size_t subfile_size; size_t subfile_size;
VGMSTREAM* (*init_vgmstream_subfile)(STREAMFILE*) = NULL; init_vgmstream_t init_vgmstream = NULL;
const char* extension; const char* extension = NULL;
uint32_t (*read_u32)(off_t,STREAMFILE*); uint32_t (*read_u32)(off_t,STREAMFILE*);
uint16_t (*read_u16)(off_t,STREAMFILE*); uint16_t (*read_u16)(off_t,STREAMFILE*);
@ -38,12 +38,12 @@ VGMSTREAM* init_vgmstream_sps_n1(STREAMFILE* sf) {
switch(type) { switch(type) {
case 1: case 1:
init_vgmstream_subfile = init_vgmstream_vag; init_vgmstream = init_vgmstream_vag;
extension = "vag"; extension = "vag";
break; break;
case 2: case 2:
init_vgmstream_subfile = init_vgmstream_riff; init_vgmstream = init_vgmstream_riff;
extension = "at3"; extension = "at3";
break; break;
@ -59,7 +59,7 @@ VGMSTREAM* init_vgmstream_sps_n1(STREAMFILE* sf) {
temp_sf = setup_subfile_streamfile(sf, subfile_offset, subfile_size, extension); temp_sf = setup_subfile_streamfile(sf, subfile_offset, subfile_size, extension);
if (!temp_sf) goto fail; if (!temp_sf) goto fail;
vgmstream = init_vgmstream_subfile(temp_sf); vgmstream = init_vgmstream(temp_sf);
if (!vgmstream) goto fail; if (!vgmstream) goto fail;
vgmstream->sample_rate = sample_rate; /* .vag header doesn't match */ vgmstream->sample_rate = sample_rate; /* .vag header doesn't match */
@ -81,7 +81,7 @@ VGMSTREAM* init_vgmstream_sps_n1_segmented(STREAMFILE* sf) {
int loop_flag, type, sample_rate; int loop_flag, type, sample_rate;
int i, segment; int i, segment;
VGMSTREAM* (*init_vgmstream_subfile)(STREAMFILE*) = NULL; init_vgmstream_t init_vgmstream = NULL;
const char* extension; const char* extension;
segmented_layout_data* data = NULL; segmented_layout_data* data = NULL;
int segment_count, loop_start_segment, loop_end_segment; int segment_count, loop_start_segment, loop_end_segment;
@ -101,15 +101,13 @@ VGMSTREAM* init_vgmstream_sps_n1_segmented(STREAMFILE* sf) {
/* 0x0c: num_samples (slightly smaller than added samples?) */ /* 0x0c: num_samples (slightly smaller than added samples?) */
switch(type) { switch(type) {
#ifdef VGM_USE_VORBIS
case 7: case 7:
init_vgmstream_subfile = init_vgmstream_ogg_vorbis; init_vgmstream = init_vgmstream_ogg_vorbis;
extension = "ogg"; extension = "ogg";
break; break;
#endif
case 9: case 9:
init_vgmstream_subfile = init_vgmstream_opus_std; init_vgmstream = init_vgmstream_opus_std;
extension = "opus"; extension = "opus";
break; break;
@ -155,7 +153,7 @@ VGMSTREAM* init_vgmstream_sps_n1_segmented(STREAMFILE* sf) {
temp_sf = setup_subfile_streamfile(sf, segment_offset,segment_size, extension); temp_sf = setup_subfile_streamfile(sf, segment_offset,segment_size, extension);
if (!temp_sf) goto fail; if (!temp_sf) goto fail;
data->segments[segment] = init_vgmstream_subfile(temp_sf); data->segments[segment] = init_vgmstream(temp_sf);
close_streamfile(temp_sf); close_streamfile(temp_sf);
if (!data->segments[segment]) goto fail; if (!data->segments[segment]) goto fail;

View File

@ -213,11 +213,6 @@ static int parse_header(STREAMFILE* sf_h, STREAMFILE* sf_b, strwav_header* strwa
* table entries don't need to match (table2 may be slightly bigger) * table entries don't need to match (table2 may be slightly bigger)
*/ */
//breaking ta rules full test again, fuse with Pac-Man World 3
//same on xbox and pc
//same with zapper + pw3 gc
//todo loop start/end values may be off for some headers //todo loop start/end values may be off for some headers
/* Fuzion Frenzy (Xbox)[2001] wma */ /* Fuzion Frenzy (Xbox)[2001] wma */
@ -251,7 +246,7 @@ static int parse_header(STREAMFILE* sf_h, STREAMFILE* sf_b, strwav_header* strwa
/* Cubix Robots for Everyone: Showdown (GC)[2003] */ /* Cubix Robots for Everyone: Showdown (GC)[2003] */
if ( read_u32be(0x04,sf_h) == 0x00000900 && if ( read_u32be(0x04,sf_h) == 0x00000900 &&
read_u32be(0x0c,sf_h) != header_size && read_u32be(0x0c,sf_h) != header_size &&
read_u32le(0x24,sf_h) != 0 && read_u32be(0x24,sf_h) != 0 &&
read_u32be(0x24,sf_h) == read_u32be(0x90,sf_h) && /* sample rate repeat */ read_u32be(0x24,sf_h) == read_u32be(0x90,sf_h) && /* sample rate repeat */
read_u32be(0xa0,sf_h) == header_size /* ~0x3C0 */ read_u32be(0xa0,sf_h) == header_size /* ~0x3C0 */
) { ) {
@ -367,9 +362,11 @@ static int parse_header(STREAMFILE* sf_h, STREAMFILE* sf_b, strwav_header* strwa
return 1; return 1;
} }
/* Zapper: One Wicked Cricket! (GC)[2005] */ /* Zapper: One Wicked Cricket! Beta (GC)[2002] */
/* Zapper: One Wicked Cricket! (GC)[2002] */
if ( read_u32be(0x04,sf_h) == 0x00000900 && if ( read_u32be(0x04,sf_h) == 0x00000900 &&
read_u32be(0x24,sf_h) == read_u32be(0xB0,sf_h) && /* sample rate repeat */ read_u32be(0x24,sf_h) == read_u32be(0xB0,sf_h) && /* sample rate repeat */
read_u32be(0x88,sf_h) != 0 &&
read_u32le(0xc0,sf_h) == header_size /* LE! */ read_u32le(0xc0,sf_h) == header_size /* LE! */
) { ) {
/* 0x08: null */ /* 0x08: null */
@ -399,10 +396,36 @@ static int parse_header(STREAMFILE* sf_h, STREAMFILE* sf_b, strwav_header* strwa
return 1; return 1;
} }
/* Zapper: One Wicked Cricket! Beta (PS2)[2002] */
if ( read_u32be(0x04,sf_h) == 0x00000900 &&
read_u32le(0x2c,sf_h) == 44100 && /* sample rate */
read_u32le(0x70,sf_h) == 0 && /* sample rate repeat? */
header_size == 0x78
) {
/* 0x08: null */
/* 0x0c: hashname */
/* 0x28: loop start? */
strwav->sample_rate = read_s32le(0x2c,sf_h);
/* 0x30: number of 0x800 sectors */
strwav->flags = read_u32le(0x34,sf_h);
strwav->num_samples = read_s32le(0x5c,sf_h);
strwav->tracks = read_s32le(0x60,sf_h);
strwav->loop_start = 0;
strwav->loop_end = 0;
strwav->codec = PSX;
strwav->interleave = strwav->tracks > 1 ? 0x8000 : 0x8000;
//todo: tracks are stereo blocks of size 0x20000*tracks, containing 4 interleaves of 0x8000:
// | 1 2 1 2 | 3 4 3 4 | 5 6 5 6 | 1 2 1 2 | 3 4 3 4 | 5 6 5 6 | ...
;VGM_LOG("STR+WAV: header ZPb (PS2)\n");
return 1;
}
/* Zapper: One Wicked Cricket! (PS2)[2002] */
/* The Fairly OddParents - Breakin' da Rules (PS2)[2003] */ /* The Fairly OddParents - Breakin' da Rules (PS2)[2003] */
/* The Fairly OddParents! - Shadow Showdown (PS2)[2004] */ /* The Fairly OddParents! - Shadow Showdown (PS2)[2004] */
/* Bad Boys II (PS2)[2004] */ /* Bad Boys II (PS2)[2004] */
/* Zapper: One Wicked Cricket! (PS2)[2005] */
if ((read_u32be(0x04,sf_h) == 0x00000800 || /* BB2 */ if ((read_u32be(0x04,sf_h) == 0x00000800 || /* BB2 */
read_u32be(0x04,sf_h) == 0x00000900) && /* FOP, ZP */ read_u32be(0x04,sf_h) == 0x00000900) && /* FOP, ZP */
read_u32le(0x24,sf_h) == read_u32le(0x70,sf_h) && /* sample rate repeat */ read_u32le(0x24,sf_h) == read_u32le(0x70,sf_h) && /* sample rate repeat */
@ -456,7 +479,62 @@ static int parse_header(STREAMFILE* sf_h, STREAMFILE* sf_b, strwav_header* strwa
return 1; return 1;
} }
/* Zapper: One Wicked Cricket! (PC)[2005] */ /* Zapper: One Wicked Cricket! Beta (Xbox)[2002] */
if ( read_u32be(0x04,sf_h) == 0x00000900 &&
read_u32le(0x0c,sf_h) != header_size &&
read_u32le(0x24,sf_h) != 0 &&
read_u32le(0x24,sf_h) == read_u32le(0x90,sf_h) && /* sample rate repeat */
read_u32le(0xa0,sf_h) == header_size /* ~0xC0 */
) {
/* 0x08: null */
/* 0x0c: hashname */
strwav->num_samples = read_s32le(0x20,sf_h);
strwav->sample_rate = read_s32le(0x24,sf_h);
/* 0x28: 16 bps */
strwav->flags = read_u32le(0x2c,sf_h);
strwav->loop_start = read_s32le(0x38,sf_h);
strwav->tracks = read_s32le(0x50,sf_h);
/* 0x58: number of chunks? */
/* 0x90: sample rate 2 */
/* 0xb8: total frames? */
strwav->loop_end = strwav->num_samples;
strwav->codec = XBOX;
strwav->interleave = strwav->tracks > 1 ? 0xD800/2 : 0xD800;
;VGM_LOG("STR+WAV: header ZPb (Xbox)\n");
return 1;
}
/* Zapper: One Wicked Cricket! (Xbox)[2002] */
if ( read_u32be(0x04,sf_h) == 0x00000900 &&
read_u32le(0x0c,sf_h) != header_size &&
read_u32le(0x24,sf_h) != 0 &&
read_u32le(0x24,sf_h) == read_u32le(0xb0,sf_h) && /* sample rate repeat */
read_u32le(0xc0,sf_h) == header_size
) {
/* 0x08: null */
/* 0x0c: hashname */
strwav->num_samples = read_s32le(0x20,sf_h);
strwav->sample_rate = read_s32le(0x24,sf_h);
/* 0x28: 16 bps */
strwav->flags = read_u32le(0x2c,sf_h);
strwav->loop_start = read_s32le(0x38,sf_h);
strwav->tracks = read_s32le(0x70,sf_h);
/* 0x78: number of chunks? */
/* 0xb0: sample rate 2 */
/* 0xc0: header size*/
/* 0xd8: total frames? */
strwav->loop_end = strwav->num_samples;
strwav->codec = XBOX;
strwav->interleave = strwav->tracks > 1 ? 0xD800/2 : 0xD800;
;VGM_LOG("STR+WAV: header ZP (Xbox)\n");
return 1;
}
/* Zapper: One Wicked Cricket! (PC)[2002] */
if ( read_u32be(0x04,sf_h) == 0x00000900 && if ( read_u32be(0x04,sf_h) == 0x00000900 &&
read_u32le(0x24,sf_h) == read_u32le(0x114,sf_h) && /* sample rate repeat */ read_u32le(0x24,sf_h) == read_u32le(0x114,sf_h) && /* sample rate repeat */
read_u32le(0x12c,sf_h) == header_size /* ~0x130 */ read_u32le(0x12c,sf_h) == header_size /* ~0x130 */
@ -514,32 +592,6 @@ static int parse_header(STREAMFILE* sf_h, STREAMFILE* sf_b, strwav_header* strwa
return 1; return 1;
} }
/* Zapper: One Wicked Cricket! Beta (PS2)[2005] */
if ( read_u32be(0x04,sf_h) == 0x00000900 &&
read_u32le(0x2c,sf_h) == 44100 && /* sample rate */
read_u32le(0x70,sf_h) == 0 && /* sample rate repeat? */
header_size == 0x78
) {
/* 0x08: null */
/* 0x0c: hashname */
/* 0x28: loop start? */
strwav->sample_rate = read_s32le(0x2c,sf_h);
/* 0x30: number of 0x800 sectors */
strwav->flags = read_u32le(0x34,sf_h);
strwav->num_samples = read_s32le(0x5c,sf_h);
strwav->tracks = read_s32le(0x60,sf_h);
strwav->loop_start = 0;
strwav->loop_end = 0;
strwav->codec = PSX;
strwav->interleave = strwav->tracks > 1 ? 0x8000 : 0x8000;
//todo: tracks are stereo blocks of size 0x20000*tracks, containing 4 interleaves of 0x8000:
// | 1 2 1 2 | 3 4 3 4 | 5 6 5 6 | 1 2 1 2 | 3 4 3 4 | 5 6 5 6 | ...
;VGM_LOG("STR+WAV: header ZPb (PS2)\n");
return 1;
}
/* Pac-Man World 3 (GC)[2005] */ /* Pac-Man World 3 (GC)[2005] */
/* SpongeBob SquarePants: Creature from the Krusty Krab (GC)[2006] */ /* SpongeBob SquarePants: Creature from the Krusty Krab (GC)[2006] */
/* SpongeBob SquarePants: Creature from the Krusty Krab (Wii)[2006] */ /* SpongeBob SquarePants: Creature from the Krusty Krab (Wii)[2006] */

View File

@ -1,6 +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 "../util/endianness.h"
typedef enum { PCM, UBI, PSX, DSP, XIMA, ATRAC3, XMA2, MP3 } ubi_hx_codec; typedef enum { PCM, UBI, PSX, DSP, XIMA, ATRAC3, XMA2, MP3 } ubi_hx_codec;
@ -12,14 +13,14 @@ typedef struct {
int codec_id; int codec_id;
ubi_hx_codec codec; /* unified codec */ ubi_hx_codec codec; /* unified codec */
int header_index; /* entry number within section2 */ int header_index; /* entry number within section2 */
off_t header_offset; /* entry offset within internal .HXx */ uint32_t header_offset; /* entry offset within internal .HXx */
size_t header_size; /* entry offset within internal .HXx */ uint32_t header_size; /* entry offset within internal .HXx */
char class_name[255]; char class_name[255];
size_t class_size; size_t class_size;
size_t stream_mode; size_t stream_mode;
off_t stream_offset; /* data offset within external stream */ uint32_t stream_offset; /* data offset within external stream */
size_t stream_size; /* data size within external stream */ uint32_t stream_size; /* data size within external stream */
uint32_t cuuid1; /* usually "Res" id1: class (1=Event, 3=Wave), id2: group id+sound id, */ uint32_t cuuid1; /* usually "Res" id1: class (1=Event, 3=Wave), id2: group id+sound id, */
uint32_t cuuid2; /* others have some complex id (not hash), id1: parent id?, id2: file id? */ uint32_t cuuid2; /* others have some complex id (not hash), id1: parent id?, id2: file id? */
@ -47,6 +48,12 @@ VGMSTREAM* init_vgmstream_ubi_hx(STREAMFILE* sf) {
/* checks */ /* checks */
{
uint32_t name_size = read_u32be(0x04, sf); /* BE/LE, should always be < 0xFF */
if (name_size == 0 || (name_size & 0x00FFFF00) != 0)
goto fail;
}
/* .hxd: Rayman M/Arena (all), PK: Out of Shadows (all) /* .hxd: Rayman M/Arena (all), PK: Out of Shadows (all)
* .hxc: Rayman 3 (PC), XIII (PC) * .hxc: Rayman 3 (PC), XIII (PC)
* .hx2: Rayman 3 (PS2), XIII (PS2) * .hx2: Rayman 3 (PS2), XIII (PS2)
@ -60,10 +67,8 @@ VGMSTREAM* init_vgmstream_ubi_hx(STREAMFILE* sf) {
* then an index to those types. Some games leave a companion .bnh with text info, probably leftover from their tools. * then an index to those types. Some games leave a companion .bnh with text info, probably leftover from their tools.
* Game seems to play files by calling linked ids: EventResData (play/stop/etc) > Random/Program/Wav ResData (1..N refs) > FileIdObj */ * Game seems to play files by calling linked ids: EventResData (play/stop/etc) > Random/Program/Wav ResData (1..N refs) > FileIdObj */
/* HX CONFIG */
hx.big_endian = guess_endianness32bit(0x00, sf);
/* HX HEADER */ /* HX HEADER */
hx.big_endian = guess_endianness32bit(0x00, sf);
if (!parse_hx(&hx, sf, target_subsong)) if (!parse_hx(&hx, sf, target_subsong))
goto fail; goto fail;
@ -133,8 +138,8 @@ fail:
/* get referenced name from WavRes, using the index again (abridged) */ /* get referenced name from WavRes, using the index again (abridged) */
static int parse_name(ubi_hx_header* hx, STREAMFILE* sf) { static int parse_name(ubi_hx_header* hx, STREAMFILE* sf) {
uint32_t (*read_u32)(off_t,STREAMFILE*) = hx->big_endian ? read_u32be : read_u32le; read_u32_t read_u32 = hx->big_endian ? read_u32be : read_u32le;
int32_t (*read_s32)(off_t,STREAMFILE*) = hx->big_endian ? read_s32be : read_s32le; read_s32_t read_s32 = hx->big_endian ? read_s32be : read_s32le;
off_t index_offset, offset; off_t index_offset, offset;
int i, index_entries; int i, index_entries;
char class_name[255]; char class_name[255];
@ -228,17 +233,17 @@ fail:
/* parse a single known header resource at offset */ /* parse a single known header resource at offset */
static int parse_header(ubi_hx_header* hx, STREAMFILE* sf, off_t offset, size_t size, int index) { static int parse_header(ubi_hx_header* hx, STREAMFILE* sf, uint32_t offset, uint32_t size, int index) {
uint32_t (*read_u32)(off_t,STREAMFILE*) = hx->big_endian ? read_u32be : read_u32le; read_u32_t read_u32 = hx->big_endian ? read_u32be : read_u32le;
int32_t (*read_s32)(off_t,STREAMFILE*) = hx->big_endian ? read_s32be : read_s32le; read_s32_t read_s32 = hx->big_endian ? read_s32be : read_s32le;
uint16_t (*read_u16)(off_t,STREAMFILE*) = hx->big_endian ? read_u16be : read_u16le; read_u16_t read_u16 = hx->big_endian ? read_u16be : read_u16le;
off_t riff_offset, riff_size, chunk_offset, stream_adjust = 0, resource_size; off_t riff_offset, riff_size, chunk_offset, stream_adjust = 0, resource_size;
size_t chunk_size; size_t chunk_size;
int cue_flag = 0; int cue_flag = 0;
//todo cleanup/unify common readings //todo cleanup/unify common readings
//;VGM_LOG("UBI HX: header o=%lx, s=%x\n\n", offset, size); //;VGM_LOG("ubi hx: header o=%x, s=%x\n\n", offset, size);
hx->header_index = index; hx->header_index = index;
hx->header_offset = offset; hx->header_offset = offset;
@ -284,7 +289,7 @@ static int parse_header(ubi_hx_header* hx, STREAMFILE* sf, off_t offset, size_t
} }
} }
else { else {
VGM_LOG("UBI HX: unknown flag-type\n"); VGM_LOG("ubi hx: unknown flag-type\n");
goto fail; goto fail;
} }
@ -316,7 +321,7 @@ static int parse_header(ubi_hx_header* hx, STREAMFILE* sf, off_t offset, size_t
break; break;
default: default:
VGM_LOG("UBI HX: %x\n", hx->stream_mode); VGM_LOG("ubi hx: %x\n", hx->stream_mode);
goto fail; goto fail;
} }
@ -333,7 +338,7 @@ static int parse_header(ubi_hx_header* hx, STREAMFILE* sf, off_t offset, size_t
case 0x05: hx->codec = XIMA; break; case 0x05: hx->codec = XIMA; break;
case 0x55: hx->codec = MP3; break; /* Largo Winch: Empire Under Threat (PC) */ case 0x55: hx->codec = MP3; break; /* Largo Winch: Empire Under Threat (PC) */
default: default:
VGM_LOG("UBI HX: unknown codec %x\n", hx->codec_id); VGM_LOG("ubi hx: unknown codec %x\n", hx->codec_id);
goto fail; goto fail;
} }
hx->channels = read_u16(riff_offset + 0x16, sf); hx->channels = read_u16(riff_offset + 0x16, sf);
@ -383,21 +388,22 @@ static int parse_header(ubi_hx_header* hx, STREAMFILE* sf, off_t offset, size_t
/* 0x04: some kind of parent id shared by multiple Waves, or 0 */ /* 0x04: some kind of parent id shared by multiple Waves, or 0 */
offset += 0x08; offset += 0x08;
hx->stream_mode = read_8bit(offset, sf); hx->stream_mode = read_u8(offset, sf);
offset += 0x01; offset += 0x01;
if ((strcmp(hx->class_name, "CXBoxStaticHWWaveFileIdObj") == 0 || if ((strcmp(hx->class_name, "CXBoxStaticHWWaveFileIdObj") == 0 ||
strcmp(hx->class_name, "CXBoxStreamHWWaveFileIdObj") == 0) && !hx->big_endian) { strcmp(hx->class_name, "CXBoxStreamHWWaveFileIdObj") == 0) && !hx->big_endian) {
/* micro header: some mix of channels + block size + sample rate + flags, unsure of which bits */ /* micro header: some mix of channels + block size + sample rate + flags, unsure of which bits */
hx->codec = XIMA; hx->codec = XIMA;
hx->channels = (uint8_t)read_8bit(offset + 0x01, sf); /* 0x00: ? */
switch(hx->channels) { /* upper 2 bits? */ hx->channels = read_u8(offset + 0x01, sf); /* upper 2 bits? */
switch(hx->channels) {
case 0x48: hx->channels = 1; break; case 0x48: hx->channels = 1; break;
case 0x90: hx->channels = 2; break; case 0x90: hx->channels = 2; break;
default: goto fail; default: goto fail;
} }
hx->sample_rate = (read_u16(offset + 0x02, sf) & 0x7FFFu) << 1u; /* ??? */ hx->sample_rate = (read_u16(offset + 0x02, sf) & 0x7FFFu) << 1u; /* ??? */
cue_flag = read_u8(offset + 0x03, sf) & (1<<7); cue_flag = read_u8(offset + 0x03, sf) & (1 << 7);
offset += 0x04; offset += 0x04;
} }
else if ((strcmp(hx->class_name, "CXBoxStaticHWWaveFileIdObj") == 0 || else if ((strcmp(hx->class_name, "CXBoxStaticHWWaveFileIdObj") == 0 ||
@ -435,6 +441,10 @@ static int parse_header(ubi_hx_header* hx, STREAMFILE* sf, off_t offset, size_t
switch(hx->stream_mode) { switch(hx->stream_mode) {
case 0x00: /* static (smaller internal file) [XIII (Xbox)] */
hx->stream_offset += offset;
break;
case 0x01: /* static (smaller external file) */ case 0x01: /* static (smaller external file) */
case 0x03: /* stream (bigger external file) */ case 0x03: /* stream (bigger external file) */
case 0x07: /* stream? */ case 0x07: /* stream? */
@ -446,6 +456,7 @@ static int parse_header(ubi_hx_header* hx, STREAMFILE* sf, off_t offset, size_t
break; break;
default: default:
VGM_LOG("ubi hx: unknown stream mode %x\n", hx->stream_mode);
goto fail; goto fail;
} }
} }
@ -455,22 +466,22 @@ static int parse_header(ubi_hx_header* hx, STREAMFILE* sf, off_t offset, size_t
return 1; return 1;
fail: fail:
VGM_LOG("UBI HX: error parsing header at %lx\n", hx->header_offset); vgm_logi("UBI HX: error parsing header at %x (report)\n", hx->header_offset);
return 0; return 0;
} }
/* parse a bank index and its possible audio headers (some info from Droolie's .bms) */ /* parse a bank index and its possible audio headers (some info from Droolie's .bms) */
static int parse_hx(ubi_hx_header* hx, STREAMFILE* sf, int target_subsong) { static int parse_hx(ubi_hx_header* hx, STREAMFILE* sf, int target_subsong) {
uint32_t (*read_u32)(off_t,STREAMFILE*) = hx->big_endian ? read_u32be : read_u32le; read_u32_t read_u32 = hx->big_endian ? read_u32be : read_u32le;
int32_t (*read_s32)(off_t,STREAMFILE*) = hx->big_endian ? read_s32be : read_s32le; read_s32_t read_s32 = hx->big_endian ? read_s32be : read_s32le;
off_t index_offset, offset; uint32_t index_offset, offset;
int i, index_entries; int i, index_entries;
char class_name[255]; char class_name[255];
index_offset = read_u32(0x00, sf); index_offset = read_u32(0x00, sf);
if (read_u32(index_offset + 0x00, sf) != 0x58444E49) /* "XDNI" (INDX in given endianness) */ if (read_u32(index_offset + 0x00, sf) != get_id32be("XDNI")) /* (INDX in given endianness) */
goto fail; goto fail;
if (read_u32(index_offset + 0x04, sf) != 0x02) /* type? */ if (read_u32(index_offset + 0x04, sf) != 0x02) /* type? */
goto fail; goto fail;
@ -480,11 +491,10 @@ static int parse_hx(ubi_hx_header* hx, STREAMFILE* sf, int target_subsong) {
index_entries = read_s32(index_offset + 0x08, sf); index_entries = read_s32(index_offset + 0x08, sf);
offset = index_offset + 0x0c; offset = index_offset + 0x0c;
for (i = 0; i < index_entries; i++) { for (i = 0; i < index_entries; i++) {
off_t header_offset; uint32_t header_offset, class_size, header_size;
size_t class_size, header_size;
int j, unknown_count, link_count, language_count; int j, unknown_count, link_count, language_count;
//;VGM_LOG("UBI HX: index %i at %lx\n", i, offset); //;VGM_LOG("ubi hx: index %i at %x\n", i, offset);
/* parse index entries: offset to actual header plus some extra info also in the header */ /* parse index entries: offset to actual header plus some extra info also in the header */
@ -502,7 +512,7 @@ static int parse_hx(ubi_hx_header* hx, STREAMFILE* sf, int target_subsong) {
/* not seen */ /* not seen */
unknown_count = read_s32(offset + 0x00, sf); unknown_count = read_s32(offset + 0x00, sf);
if (unknown_count != 0) { if (unknown_count != 0) {
VGM_LOG("UBI HX: found unknown near %lx\n", offset); VGM_LOG("ubi hx: found unknown near %x\n", offset);
goto fail; goto fail;
} }
offset += 0x04; offset += 0x04;
@ -520,7 +530,7 @@ static int parse_hx(ubi_hx_header* hx, STREAMFILE* sf, int target_subsong) {
/* 0x08: id1+2 */ /* 0x08: id1+2 */
if (read_u32(offset + 0x04, sf) != 1) { if (read_u32(offset + 0x04, sf) != 1) {
VGM_LOG("UBI HX: wrong lang count near %lx\n", offset); VGM_LOG("ubi hx: wrong lang count near %x\n", offset);
goto fail; /* WavRes doesn't have this field */ goto fail; /* WavRes doesn't have this field */
} }
offset += 0x10; offset += 0x10;
@ -557,12 +567,12 @@ static int parse_hx(ubi_hx_header* hx, STREAMFILE* sf, int target_subsong) {
; ;
} }
else { else {
VGM_LOG("UBI HX: unknown type: %s\n", class_name); vgm_logi("UBI HX: unknown type: %s (report)\n", class_name);
goto fail; goto fail;
} }
if (link_count != 0) { if (link_count != 0) {
VGM_LOG("UBI HX: found links in wav object\n"); vgm_logi("UBI HX: found links in wav object (report)\n");
goto fail; goto fail;
} }
@ -596,7 +606,7 @@ static STREAMFILE* open_hx_streamfile(ubi_hx_header* hx, STREAMFILE* sf) {
sb = open_streamfile_by_filename(sf, hx->resource_name); sb = open_streamfile_by_filename(sf, hx->resource_name);
if (sb == NULL) { if (sb == NULL) {
VGM_LOG("UBI HX: external stream '%s' not found\n", hx->resource_name); vgm_logi("UBI HX: external file '%s' not found (put together)\n", hx->resource_name);
goto fail; goto fail;
} }

View File

@ -1,6 +1,8 @@
#include "meta.h" #include "meta.h"
#include "../util.h" #include "../util.h"
#include "../coding/coding.h" #include "../coding/coding.h"
#include "../util/chunks.h"
#include "../util/endianness.h"
/* Wwise uses a custom RIFF/RIFX header, non-standard enough that it's parsed it here. /* Wwise uses a custom RIFF/RIFX header, non-standard enough that it's parsed it here.
@ -14,6 +16,7 @@ typedef struct {
int big_endian; int big_endian;
size_t file_size; size_t file_size;
int truncated; int truncated;
int is_wem;
/* chunks references */ /* chunks references */
off_t fmt_offset; off_t fmt_offset;
@ -55,22 +58,28 @@ static int parse_wwise(STREAMFILE* sf, wwise_header* ww);
static int is_dsp_full_interleave(STREAMFILE* sf, wwise_header* ww, off_t coef_offset); static int is_dsp_full_interleave(STREAMFILE* sf, wwise_header* ww, off_t coef_offset);
/* Wwise - Audiokinetic Wwise (Wave Works Interactive Sound Engine) middleware */
/* Wwise - Audiokinetic Wwise (WaveWorks Interactive Sound Engine) middleware */
VGMSTREAM* init_vgmstream_wwise(STREAMFILE* sf) { VGMSTREAM* init_vgmstream_wwise(STREAMFILE* sf) {
VGMSTREAM* vgmstream = NULL; VGMSTREAM* vgmstream = NULL;
wwise_header ww = {0}; wwise_header ww = {0};
off_t start_offset; off_t start_offset;
uint32_t (*read_u32)(off_t,STREAMFILE*) = NULL; read_u32_t read_u32 = NULL;
int32_t (*read_s32)(off_t,STREAMFILE*) = NULL; read_s32_t read_s32 = NULL;
uint16_t (*read_u16)(off_t,STREAMFILE*) = NULL; read_u16_t read_u16 = NULL;
/* checks */ /* checks */
/* .wem: newer "Wwise Encoded Media" used after the 2011.2 SDK (~july 2011) if (!is_id32be(0x00,sf, "RIFF") && /* LE */
!is_id32be(0x00,sf, "RIFX")) /* BE */
goto fail;
/* note that Wwise allows those extensions only, so custom engine exts shouldn't be added
* .wem: newer "Wwise Encoded Media" used after the 2011.2 SDK (~july 2011)
* .wav: older PCM/ADPCM files [Spider-Man: Web of Shadows (PC), Punch Out!! (Wii)] * .wav: older PCM/ADPCM files [Spider-Man: Web of Shadows (PC), Punch Out!! (Wii)]
* .xma: older XMA files [Too Human (X360), Tron Evolution (X360)] * .xma: older XMA files [Too Human (X360), Tron Evolution (X360)]
* .ogg: older Vorbis files [The King of Fighters XII (X360)] * .ogg: older Vorbis files [The King of Fighters XII (X360)]
* .bnk: Wwise banks for memory .wem detection */ * .bnk: Wwise banks for memory .wem detection (hack) */
if (!check_extensions(sf,"wem,wav,lwav,ogg,logg,xma,bnk")) if (!check_extensions(sf,"wem,wav,lwav,ogg,logg,xma,bnk"))
goto fail; goto fail;
@ -92,6 +101,7 @@ VGMSTREAM* init_vgmstream_wwise(STREAMFILE* sf) {
vgmstream->loop_start_sample = ww.loop_start_sample; vgmstream->loop_start_sample = ww.loop_start_sample;
vgmstream->loop_end_sample = ww.loop_end_sample; vgmstream->loop_end_sample = ww.loop_end_sample;
vgmstream->channel_layout = ww.channel_layout; vgmstream->channel_layout = ww.channel_layout;
vgmstream->stream_size = ww.data_size;
switch(ww.codec) { switch(ww.codec) {
case PCM: /* common */ case PCM: /* common */
@ -234,7 +244,6 @@ VGMSTREAM* init_vgmstream_wwise(STREAMFILE* sf) {
else { else {
/* newer Wwise (>2012) */ /* newer Wwise (>2012) */
off_t extra_offset = ww.fmt_offset + 0x18; /* after flag + channels */ off_t extra_offset = ww.fmt_offset + 0x18; /* after flag + channels */
int is_wem = check_extensions(sf,"wem,bnk"); /* use extension as a guide for faster vorbis inits */
switch(ww.extra_size) { switch(ww.extra_size) {
case 0x30: case 0x30:
@ -246,7 +255,7 @@ VGMSTREAM* init_vgmstream_wwise(STREAMFILE* sf) {
/* setup not detectable by header, so we'll try both; libvorbis should reject wrong codebooks /* setup not detectable by header, so we'll try both; libvorbis should reject wrong codebooks
* - standard: early (<2012), ex. The King of Fighters XIII (X360)-2011/11, .ogg (cbs are from aoTuV, too) * - standard: early (<2012), ex. The King of Fighters XIII (X360)-2011/11, .ogg (cbs are from aoTuV, too)
* - aoTuV603: later (>2012), ex. Sonic & All-Stars Racing Transformed (PC)-2012/11, .wem */ * - aoTuV603: later (>2012), ex. Sonic & All-Stars Racing Transformed (PC)-2012/11, .wem */
cfg.setup_type = is_wem ? WWV_AOTUV603_CODEBOOKS : WWV_EXTERNAL_CODEBOOKS; /* aoTuV came along .wem */ cfg.setup_type = ww.is_wem ? WWV_AOTUV603_CODEBOOKS : WWV_EXTERNAL_CODEBOOKS; /* aoTuV came along .wem */
break; break;
default: default:
@ -278,7 +287,7 @@ VGMSTREAM* init_vgmstream_wwise(STREAMFILE* sf) {
vgmstream->codec_data = init_vorbis_custom(sf, start_offset + setup_offset, VORBIS_WWISE, &cfg); vgmstream->codec_data = init_vorbis_custom(sf, start_offset + setup_offset, VORBIS_WWISE, &cfg);
if (!vgmstream->codec_data) { if (!vgmstream->codec_data) {
/* codebooks failed: try again with the other type */ /* codebooks failed: try again with the other type */
cfg.setup_type = is_wem ? WWV_EXTERNAL_CODEBOOKS : WWV_AOTUV603_CODEBOOKS; cfg.setup_type = ww.is_wem ? WWV_EXTERNAL_CODEBOOKS : WWV_AOTUV603_CODEBOOKS;
vgmstream->codec_data = init_vorbis_custom(sf, start_offset + setup_offset, VORBIS_WWISE, &cfg); vgmstream->codec_data = init_vorbis_custom(sf, start_offset + setup_offset, VORBIS_WWISE, &cfg);
if (!vgmstream->codec_data) goto fail; if (!vgmstream->codec_data) goto fail;
} }
@ -690,18 +699,12 @@ static int is_dsp_full_interleave(STREAMFILE* sf, wwise_header* ww, off_t coef_o
static int parse_wwise(STREAMFILE* sf, wwise_header* ww) { static int parse_wwise(STREAMFILE* sf, wwise_header* ww) {
uint32_t (*read_u32)(off_t,STREAMFILE*) = NULL; read_u32_t read_u32;
uint16_t (*read_u16)(off_t,STREAMFILE*) = NULL; read_u16_t read_u16;
if (read_u32be(0x00,sf) != 0x52494646 && /* "RIFF" (LE) */ /* Wwise honors machine's endianness (PC=RIFF, X360=RIFX --unlike XMA) */
read_u32be(0x00,sf) != 0x52494658) /* "RIFX" (BE) */ ww->big_endian = is_id32be(0x00,sf, "RIFX"); /* RIFF size not useful to detect, see below */
goto fail; if (ww->big_endian) {
if (read_u32be(0x08,sf) != 0x57415645 && /* "WAVE" */
read_u32be(0x08,sf) != 0x58574D41) /* "XWMA" */
goto fail;
ww->big_endian = read_u32be(0x00,sf) == 0x52494658; /* RIFX */
if (ww->big_endian) { /* Wwise honors machine's endianness (PC=RIFF, X360=RIFX --unlike XMA) */
read_u32 = read_u32be; read_u32 = read_u32be;
read_u16 = read_u16be; read_u16 = read_u16be;
} else { } else {
@ -727,50 +730,56 @@ static int parse_wwise(STREAMFILE* sf, wwise_header* ww) {
} }
#endif #endif
if (!is_id32be(0x08,sf, "WAVE") &&
!is_id32be(0x08,sf, "XWMA"))
goto fail;
/* parse chunks (reads once linearly) */ /* parse chunks (reads once linearly) */
{ {
off_t offset = 0x0c; chunk_t rc = {0};
while (offset < ww->file_size) {
uint32_t type = read_u32be(offset + 0x00,sf);
uint32_t size = read_u32 (offset + 0x04,sf);
offset += 0x08;
switch(type) { /* chunks are even-aligned and don't need to add padding byte, unlike real RIFFs */
rc.be_size = ww->big_endian;
rc.current = 0x0c;
while (next_chunk(&rc, sf)) {
switch(rc.type) {
case 0x666d7420: /* "fmt " */ case 0x666d7420: /* "fmt " */
ww->fmt_offset = offset; ww->fmt_offset = rc.offset;
ww->fmt_size = size; ww->fmt_size = rc.size;
break; break;
case 0x584D4132: /* "XMA2" */ case 0x584D4132: /* "XMA2" */
ww->xma2_offset = offset; ww->xma2_offset = rc.offset;
ww->xma2_size = size; ww->xma2_size = rc.size;
break; break;
case 0x64617461: /* "data" */ case 0x64617461: /* "data" */
ww->data_offset = offset; ww->data_offset = rc.offset;
ww->data_size = size; ww->data_size = rc.size;
break; break;
case 0x766F7262: /* "vorb" */ case 0x766F7262: /* "vorb" */
ww->vorb_offset = offset; ww->vorb_offset = rc.offset;
ww->vorb_size = size; ww->vorb_size = rc.size;
break; break;
case 0x57696948: /* "WiiH" */ case 0x57696948: /* "WiiH" */
ww->wiih_offset = offset; ww->wiih_offset = rc.offset;
ww->wiih_size = size; ww->wiih_size = rc.size;
break; break;
case 0x7365656B: /* "seek" */ case 0x7365656B: /* "seek" */
ww->seek_offset = offset; ww->seek_offset = rc.offset;
ww->seek_size = size; ww->seek_size = rc.size;
break; break;
case 0x736D706C: /* "smpl" */ case 0x736D706C: /* "smpl" */
ww->smpl_offset = offset; ww->smpl_offset = rc.offset;
ww->smpl_size = size; ww->smpl_size = rc.size;
break; break;
case 0x6D657461: /* "meta" */ case 0x6D657461: /* "meta" */
ww->meta_offset = offset; ww->meta_offset = rc.offset;
ww->meta_size = size; ww->meta_size = rc.size;
break; break;
case 0x66616374: /* "fact" */ case 0x66616374: /* "fact" */
/* Wwise shouldn't use fact, but if somehow some file does uncomment the following: */ /* Wwise never uses fact, but if somehow some file does uncomment the following: */
//if (size == 0x10 && read_u32be(offset + 0x04, sf) == 0x4C794E20) /* "LyN " */ //if (size == 0x10 && read_u32be(offset + 0x04, sf) == 0x4C794E20) /* "LyN " */
// goto fail; /* ignore LyN RIFF */ // goto fail; /* ignore LyN RIFF */
goto fail; goto fail;
@ -783,12 +792,11 @@ static int parse_wwise(STREAMFILE* sf, wwise_header* ww) {
default: default:
break; break;
} }
/* chunks are even-aligned and don't need to add padding byte, unlike real RIFFs */
offset += size;
} }
} }
/* use extension as a guide for certain cases */
ww->is_wem = check_extensions(sf,"wem,bnk");
/* parse format (roughly spec-compliant but some massaging is needed) */ /* parse format (roughly spec-compliant but some massaging is needed) */
if (ww->xma2_offset) { if (ww->xma2_offset) {
@ -806,7 +814,7 @@ static int parse_wwise(STREAMFILE* sf, wwise_header* ww) {
ww->channels = read_u16(ww->fmt_offset + 0x02,sf); ww->channels = read_u16(ww->fmt_offset + 0x02,sf);
ww->sample_rate = read_u32(ww->fmt_offset + 0x04,sf); ww->sample_rate = read_u32(ww->fmt_offset + 0x04,sf);
ww->avg_bitrate = read_u32(ww->fmt_offset + 0x08,sf); ww->avg_bitrate = read_u32(ww->fmt_offset + 0x08,sf);
ww->block_size = read_u16(ww->fmt_offset + 0x0c,sf); ww->block_size = read_u16(ww->fmt_offset + 0x0c,sf);
ww->bits_per_sample = read_u16(ww->fmt_offset + 0x0e,sf); ww->bits_per_sample = read_u16(ww->fmt_offset + 0x0e,sf);
if (ww->fmt_size > 0x10 && ww->format != 0x0165 && ww->format != 0x0166) /* ignore XMAWAVEFORMAT */ if (ww->fmt_size > 0x10 && ww->format != 0x0165 && ww->format != 0x0166) /* ignore XMAWAVEFORMAT */
ww->extra_size = read_u16(ww->fmt_offset + 0x10,sf); ww->extra_size = read_u16(ww->fmt_offset + 0x10,sf);
@ -864,7 +872,9 @@ static int parse_wwise(STREAMFILE* sf, wwise_header* ww) {
case 0x3041: ww->codec = OPUSWW; break; /* "OPUS_WEM", added on Wwise 2019.2.3, replaces OPUS */ case 0x3041: ww->codec = OPUSWW; break; /* "OPUS_WEM", added on Wwise 2019.2.3, replaces OPUS */
case 0x8311: ww->codec = PTADPCM; break; /* added on Wwise 2019.1, replaces IMA */ case 0x8311: ww->codec = PTADPCM; break; /* added on Wwise 2019.1, replaces IMA */
default: default:
vgm_logi("WWISE: unknown codec 0x%04x (report)\n", ww->format); /* some .wav may end up here, only report in .wem cases (newer codecs) */
if (ww->is_wem)
vgm_logi("WWISE: unknown codec 0x%04x (report)\n", ww->format);
goto fail; goto fail;
} }

View File

@ -17,10 +17,10 @@ VGMSTREAM* init_vgmstream_xnb(STREAMFILE* sf) {
/* checks */ /* checks */
if (!check_extensions(sf,"xnb"))
goto fail;
if ((read_u32be(0x00, sf) & 0xFFFFFF00) != get_id32be("XNB\0")) if ((read_u32be(0x00, sf) & 0xFFFFFF00) != get_id32be("XNB\0"))
goto fail; goto fail;
if (!check_extensions(sf,"xnb"))
goto fail;
/* XNA Studio platforms: 'w' = Windows, 'm' = Windows Phone 7, 'x' = X360 /* XNA Studio platforms: 'w' = Windows, 'm' = Windows Phone 7, 'x' = X360
* MonoGame extensions: 'i' = iOS, 'a' = Android, 'X' = MacOSX, 'P' = PS4, 'S' = Switch, etc */ * MonoGame extensions: 'i' = iOS, 'a' = Android, 'X' = MacOSX, 'P' = PS4, 'S' = Switch, etc */
@ -184,10 +184,9 @@ VGMSTREAM* init_vgmstream_xnb(STREAMFILE* sf) {
if (!temp_sf) goto fail; if (!temp_sf) goto fail;
if (is_ogg) { if (is_ogg) {
#ifdef VGM_USE_VORBIS
vgmstream = init_vgmstream_ogg_vorbis(temp_sf); vgmstream = init_vgmstream_ogg_vorbis(temp_sf);
#endif }
} else { else {
vgmstream = init_vgmstream_riff(temp_sf); vgmstream = init_vgmstream_riff(temp_sf);
} }
close_streamfile(temp_sf); close_streamfile(temp_sf);

View File

@ -16,11 +16,11 @@
* - Clang: seems only defined on Linux/GNU environments, somehow emscripten is out * - Clang: seems only defined on Linux/GNU environments, somehow emscripten is out
* (unsure about Clang Win since apparently they define _MSC_VER) * (unsure about Clang Win since apparently they define _MSC_VER)
* - Android: API +24 if not using __USE_FILE_OFFSET64 * - Android: API +24 if not using __USE_FILE_OFFSET64
* Not sure if fopen64 is needed in some cases. May be worth adding some compiler flag to enable 64 versions manually. * Not sure if fopen64 is needed in some cases.
*/ */
/* MSVC fixes (though mingw uses MSVCRT but not MSC_VER, maybe use AND?) */ #if defined(_MSC_VER) //&& defined(__MSVCRT__)
#if defined(__MSVCRT__) || defined(_MSC_VER) /* MSVC fixes (MinG64 seems to set MSVCRT too, but we want it below) */
#include <io.h> #include <io.h>
#define fopen_v fopen #define fopen_v fopen
@ -43,14 +43,22 @@
// #define off_t/off64_t __int64 // #define off_t/off64_t __int64
//#endif //#endif
#elif defined(VGMSTREAM_USE_IO64) || defined(__MINGW32__) || defined(__MINGW64__)
/* force, or known to work */
#define fopen_v fopen
#define fseek_v fseeko64 //fseeko
#define ftell_v ftello64 //ftello
#elif defined(XBMC) || defined(__EMSCRIPTEN__) || defined (__ANDROID__) || defined(__APPLE__) #elif defined(XBMC) || defined(__EMSCRIPTEN__) || defined (__ANDROID__) || defined(__APPLE__)
/* may depend on version */
#define fopen_v fopen #define fopen_v fopen
#define fseek_v fseek #define fseek_v fseek
#define ftell_v ftell #define ftell_v ftell
#else #else
/* other Linux systems may already use off64_t in fseeko/ftello? */
#define fopen_v fopen #define fopen_v fopen
#define fseek_v fseeko64 //fseeko #define fseek_v fseeko
#define ftell_v ftello64 //ftello #define ftell_v ftello
#endif #endif
@ -952,22 +960,12 @@ STREAMFILE* open_streamfile(STREAMFILE* sf, const char* pathname) {
STREAMFILE* open_streamfile_by_ext(STREAMFILE* sf, const char* ext) { STREAMFILE* open_streamfile_by_ext(STREAMFILE* sf, const char* ext) {
char filename[PATH_LIMIT]; char filename[PATH_LIMIT];
int filename_len, fileext_len;
sf->get_name(sf, filename, sizeof(filename)); get_streamfile_name(sf, filename, sizeof(filename));
filename_len = strlen(filename); swap_extension(filename, sizeof(filename), ext);
fileext_len = strlen(filename_extension(filename));
if (fileext_len == 0) {/* extensionless */ return open_streamfile(sf, filename);
strcat(filename,".");
strcat(filename,ext);
}
else {
strcpy(filename + filename_len - fileext_len, ext);
}
return sf->open(sf, filename, STREAMFILE_DEFAULT_BUFFER_SIZE);
} }
STREAMFILE* open_streamfile_by_filename(STREAMFILE* sf, const char* filename) { STREAMFILE* open_streamfile_by_filename(STREAMFILE* sf, const char* filename) {
@ -977,7 +975,7 @@ STREAMFILE* open_streamfile_by_filename(STREAMFILE* sf, const char* filename) {
if (!sf || !filename || !filename[0]) return NULL; if (!sf || !filename || !filename[0]) return NULL;
sf->get_name(sf, fullname, sizeof(fullname)); get_streamfile_name(sf, fullname, sizeof(fullname));
//todo normalize separators in a better way, safeops, improve copying //todo normalize separators in a better way, safeops, improve copying
@ -1026,7 +1024,7 @@ STREAMFILE* open_streamfile_by_filename(STREAMFILE* sf, const char* filename) {
strcpy(fullname, filename); strcpy(fullname, filename);
} }
return sf->open(sf, fullname, STREAMFILE_DEFAULT_BUFFER_SIZE); return open_streamfile(sf, fullname);
} }
STREAMFILE* reopen_streamfile(STREAMFILE* sf, size_t buffer_size) { STREAMFILE* reopen_streamfile(STREAMFILE* sf, size_t buffer_size) {
@ -1036,7 +1034,7 @@ STREAMFILE* reopen_streamfile(STREAMFILE* sf, size_t buffer_size) {
if (buffer_size == 0) if (buffer_size == 0)
buffer_size = STREAMFILE_DEFAULT_BUFFER_SIZE; buffer_size = STREAMFILE_DEFAULT_BUFFER_SIZE;
sf->get_name(sf, pathname,sizeof(pathname)); get_streamfile_name(sf, pathname, sizeof(pathname));
return sf->open(sf, pathname, buffer_size); return sf->open(sf, pathname, buffer_size);
} }
@ -1262,6 +1260,11 @@ STREAMFILE* read_filemap_file_pos(STREAMFILE* sf, int file_num, int* p_pos) {
/* get key/val (ignores lead/trailing spaces, stops at comment/separator) */ /* get key/val (ignores lead/trailing spaces, stops at comment/separator) */
ok = sscanf(line, " %[^\t#:] : %[^\t#\r\n] ", key, val); ok = sscanf(line, " %[^\t#:] : %[^\t#\r\n] ", key, val);
if (ok != 2) { /* ignore line if no key=val (comment or garbage) */ if (ok != 2) { /* ignore line if no key=val (comment or garbage) */
/* better way? */
if (strcmp(line, "#@reset-pos") == 0) {
file_pos = 0;
VGM_LOG("pos =%i\n", file_pos);
}
continue; continue;
} }

View File

@ -2,34 +2,40 @@
#include "util.h" #include "util.h"
#include "streamtypes.h" #include "streamtypes.h"
const char * filename_extension(const char * pathname) { const char* filename_extension(const char* pathname) {
const char * filename; const char* extension;
const char * extension;
/* favor strrchr (optimized/aligned) rather than homemade loops */ /* favor strrchr (optimized/aligned) rather than homemade loops */
extension = strrchr(pathname,'.');
/* find possible separator first to avoid misdetecting folders with dots + extensionless files if (extension != NULL) {
* (allow both slashes as plugin could pass normalized '/') */ /* probably has extension */
filename = strrchr(pathname, '/'); extension++; /* skip dot */
if (filename != NULL)
filename++; /* skip separator */ /* find possible separators to avoid misdetecting folders with dots + extensionless files
else { * (after the above to reduce search space, allows both slashes in case of non-normalized names) */
filename = strrchr(pathname, '\\'); if (strchr(extension, '/') == NULL && strchr(extension, '\\') == NULL)
if (filename != NULL) return extension; /* no slashes = really has extension */
filename++; /* skip separator */
else
filename = pathname; /* pathname has no separators (single filename) */
} }
extension = strrchr(filename,'.'); /* extensionless: point to null after current name
if (extension != NULL) * (could return NULL but prev code expects with to return an actual c-string) */
extension++; /* skip dot */ return pathname + strlen(pathname);
else
extension = filename + strlen(filename); /* point to null (empty "" string for extensionless files) */
return extension;
} }
void swap_extension(char* pathname, int pathname_len, const char* swap) {
char* extension = (char*)filename_extension(pathname);
//todo safeops
if (extension[0] == '\0') {
strcat(pathname, ".");
strcat(pathname, swap);
}
else {
strcpy(extension, swap);
}
}
/* unused */ /* unused */
/* /*
void interleave_channel(sample_t * outbuffer, sample_t * inbuffer, int32_t sample_count, int channel_count, int channel_number) { void interleave_channel(sample_t * outbuffer, sample_t * inbuffer, int32_t sample_count, int channel_count, int channel_number) {

View File

@ -175,7 +175,10 @@ int round10(int val);
/* return a file's extension (a pointer to the first character of the /* return a file's extension (a pointer to the first character of the
* extension in the original filename or the ending null byte if no extension */ * extension in the original filename or the ending null byte if no extension */
const char * filename_extension(const char * filename); const char* filename_extension(const char* pathname);
/* change pathname's extension to another (or add it if extensionless) */
void swap_extension(char* pathname, /*size_t*/ int pathname_len, const char* swap);
/* swap samples in machine endianness to little endian (useful to write .wav) */ /* swap samples in machine endianness to little endian (useful to write .wav) */
void swap_samples_le(sample_t *buf, int count); void swap_samples_le(sample_t *buf, int count);

View File

@ -26,8 +26,8 @@ int next_chunk(chunk_t* chunk, STREAMFILE* sf) {
if (chunk->type == 0xFFFFFFFF || chunk->size == 0xFFFFFFFF) if (chunk->type == 0xFFFFFFFF || chunk->size == 0xFFFFFFFF)
return 0; return 0;
/* empty chunk with 0 size, seen in some formats (XVAG uses it as end marker, Wwise doesn't) */ /* empty chunk with 0 size is ok, seen in some formats (XVAG uses it as end marker, Wwise in JUNK) */
if (chunk->type == 0 || chunk->size == 0) if (chunk->type == 0 /*|| chunk->size == 0*/)
return 0; return 0;
/* more chunks remain */ /* more chunks remain */

View File

@ -13,6 +13,7 @@ typedef struct {
int le_type; /* read type as LE instead of more common BE */ int le_type; /* read type as LE instead of more common BE */
int be_size; /* read type as BE instead of more common LE */ int be_size; /* read type as BE instead of more common LE */
int full_size; /* chunk size includes type+size */ int full_size; /* chunk size includes type+size */
int alignment; /* chunks with odd size need to be aligned to even, per RIFF spec */
} chunk_t; } chunk_t;
int next_chunk(chunk_t* chunk, STREAMFILE* sf); int next_chunk(chunk_t* chunk, STREAMFILE* sf);

View File

@ -0,0 +1,10 @@
#ifndef _UTIL_ENDIAN_H
#define _UTIL_ENDIAN_H
#include "../streamfile.h"
typedef uint32_t (*read_u32_t)(off_t, STREAMFILE*);
typedef int32_t (*read_s32_t)(off_t, STREAMFILE*);
typedef uint16_t (*read_u16_t)(off_t, STREAMFILE*);
#endif

View File

@ -22,8 +22,6 @@ VGMSTREAM* (*init_vgmstream_functions[])(STREAMFILE* sf) = {
init_vgmstream_adx, init_vgmstream_adx,
init_vgmstream_brstm, init_vgmstream_brstm,
init_vgmstream_bfwav, init_vgmstream_bfwav,
init_vgmstream_bfstm,
init_vgmstream_mca,
init_vgmstream_nds_strm, init_vgmstream_nds_strm,
init_vgmstream_agsc, init_vgmstream_agsc,
init_vgmstream_ngc_adpdtk, init_vgmstream_ngc_adpdtk,
@ -61,17 +59,9 @@ VGMSTREAM* (*init_vgmstream_functions[])(STREAMFILE* sf) = {
init_vgmstream_caf, init_vgmstream_caf,
init_vgmstream_vpk, init_vgmstream_vpk,
init_vgmstream_genh, init_vgmstream_genh,
#ifdef VGM_USE_VORBIS
init_vgmstream_ogg_vorbis, init_vgmstream_ogg_vorbis,
#endif
init_vgmstream_sli_ogg, init_vgmstream_sli_ogg,
init_vgmstream_sfl_ogg, init_vgmstream_sfl_ogg,
#if 0
init_vgmstream_mp4_aac,
#endif
#if defined(VGM_USE_MP4V2) && defined(VGM_USE_FDKAAC)
init_vgmstream_akb_mp4,
#endif
init_vgmstream_sadb, init_vgmstream_sadb,
init_vgmstream_ps2_bmdx, init_vgmstream_ps2_bmdx,
init_vgmstream_wsi, init_vgmstream_wsi,
@ -294,6 +284,14 @@ VGMSTREAM* (*init_vgmstream_functions[])(STREAMFILE* sf) = {
init_vgmstream_idsp_namco, init_vgmstream_idsp_namco,
init_vgmstream_kt_g1l, init_vgmstream_kt_g1l,
init_vgmstream_kt_wiibgm, init_vgmstream_kt_wiibgm,
init_vgmstream_bfstm,
init_vgmstream_mca,
#if 0
init_vgmstream_mp4_aac,
#endif
#if defined(VGM_USE_MP4V2) && defined(VGM_USE_FDKAAC)
init_vgmstream_akb_mp4,
#endif
init_vgmstream_ktss, init_vgmstream_ktss,
init_vgmstream_hca, init_vgmstream_hca,
init_vgmstream_svag_snk, init_vgmstream_svag_snk,

View File

@ -11,17 +11,21 @@
#import "PlaylistController.h" #import "PlaylistController.h"
#include <stdlib.h>
static NSString* get_description_tag(const char* description, const char *tag, char delimiter) { static NSString* get_description_tag(const char* description, const char *tag, char delimiter) {
// extract a "tag" from the description string // extract a "tag" from the description string
if (!delimiter) delimiter = '\n';
const char* pos = strstr(description, tag); const char* pos = strstr(description, tag);
const char* eos = NULL; const char* eos = NULL;
if (pos != NULL) { if (pos != NULL) {
pos += strlen(tag); pos += strlen(tag);
eos = strchr(pos, delimiter); eos = strchr(pos, delimiter);
if (eos == NULL) eos = pos + strlen(pos); if (eos == NULL) eos = pos + strlen(pos);
NSMutableData* data = [NSData dataWithBytes:pos length:(eos - pos + 1)]; char temp[eos - pos + 1];
((char *)[data mutableBytes])[eos - pos] = '\0'; memcpy(temp, pos, eos - pos);
return [NSString stringWithUTF8String:[data bytes]]; temp[eos - pos] = '\0';
return [NSString stringWithUTF8String:temp];
} }
return nil; return nil;
} }