diff --git a/doc/TXTH.md b/doc/TXTH.md
index f26e417f..516b55d1 100644
--- a/doc/TXTH.md
+++ b/doc/TXTH.md
@@ -845,7 +845,7 @@ JIN003.XAG: 0x180
```
-**Grandia (PS1) bgm.txth**
+#### Grandia (PS1) bgm.txth
```
header_file = GM1.IDX
body_file = GM1.STZ
diff --git a/src/coding/coding.h b/src/coding/coding.h
index 7367d292..f12fd89a 100644
--- a/src/coding/coding.h
+++ b/src/coding/coding.h
@@ -348,6 +348,7 @@ uint32_t ffmpeg_get_channel_layout(ffmpeg_codec_data * data);
void ffmpeg_set_channel_remapping(ffmpeg_codec_data * data, int *channels_remap);
const char* ffmpeg_get_codec_name(ffmpeg_codec_data * data);
void ffmpeg_set_force_seek(ffmpeg_codec_data * data);
+const char* ffmpeg_get_metadata_value(ffmpeg_codec_data* data, const char* key);
/* ffmpeg_decoder_utils.c (helper-things) */
diff --git a/src/coding/coding_utils.c b/src/coding/coding_utils.c
index f0e48243..0b6b3bfa 100644
--- a/src/coding/coding_utils.c
+++ b/src/coding/coding_utils.c
@@ -155,6 +155,8 @@ int ffmpeg_make_riff_xma1(uint8_t * buf, size_t buf_size, size_t sample_count, s
put_16bitLE(buf+off+0x12, speakers);
}
+ /* xmaencode decoding rejects XMA1 without "seek" chunk, though it doesn't seem to use it */
+
memcpy(buf+riff_size-4-4, "data", 4);
put_32bitLE(buf+riff_size-4, data_size); /* data size */
diff --git a/src/coding/ffmpeg_decoder.c b/src/coding/ffmpeg_decoder.c
index 13a3a02e..1070921f 100644
--- a/src/coding/ffmpeg_decoder.c
+++ b/src/coding/ffmpeg_decoder.c
@@ -941,4 +941,22 @@ void ffmpeg_set_force_seek(ffmpeg_codec_data * data) {
//stream = data->formatCtx->streams[data->streamIndex];
}
+const char* ffmpeg_get_metadata_value(ffmpeg_codec_data* data, const char* key) {
+ AVDictionary* avd;
+ AVDictionaryEntry* avde;
+
+ if (!data || !data->codec)
+ return NULL;
+
+ avd = data->formatCtx->streams[data->streamIndex]->metadata;
+ if (!avd)
+ return NULL;
+
+ avde = av_dict_get(avd, key, NULL, AV_DICT_IGNORE_SUFFIX);
+ if (!avde)
+ return NULL;
+
+ return avde->value;
+}
+
#endif
diff --git a/src/libvgmstream.vcproj b/src/libvgmstream.vcproj
index 5ebf07f7..e35b8e49 100644
--- a/src/libvgmstream.vcproj
+++ b/src/libvgmstream.vcproj
@@ -276,6 +276,10 @@
RelativePath=".\meta\ea_schl_streamfile.h"
>
+
+
diff --git a/src/libvgmstream.vcxproj b/src/libvgmstream.vcxproj
index 5b9a64f0..20580e58 100644
--- a/src/libvgmstream.vcxproj
+++ b/src/libvgmstream.vcxproj
@@ -111,6 +111,7 @@
+
diff --git a/src/libvgmstream.vcxproj.filters b/src/libvgmstream.vcxproj.filters
index c142dc96..91fe276f 100644
--- a/src/libvgmstream.vcxproj.filters
+++ b/src/libvgmstream.vcxproj.filters
@@ -104,6 +104,9 @@
meta\Header Files
+
+ meta\Header Files
+
meta\Header Files
diff --git a/src/meta/ffmpeg.c b/src/meta/ffmpeg.c
index 690e57b4..a0b60e7f 100644
--- a/src/meta/ffmpeg.c
+++ b/src/meta/ffmpeg.c
@@ -3,34 +3,28 @@
#ifdef VGM_USE_FFMPEG
-static int read_pos_file(uint8_t * buf, size_t bufsize, STREAMFILE *streamFile);
+static int read_pos_file(uint8_t* buf, size_t bufsize, STREAMFILE* sf);
+static int find_ogg_loops(ffmpeg_codec_data* data, int32_t* p_loop_start, int32_t* p_loop_end);
-/**
- * Generic init FFmpeg and vgmstream for any file supported by FFmpeg.
- * Called by vgmstream when trying to identify the file type (if the player allows it).
- */
-VGMSTREAM * init_vgmstream_ffmpeg(STREAMFILE *streamFile) {
- return init_vgmstream_ffmpeg_offset( streamFile, 0, streamFile->get_size(streamFile) );
-}
-
-VGMSTREAM * init_vgmstream_ffmpeg_offset(STREAMFILE *streamFile, uint64_t start, uint64_t size) {
- VGMSTREAM *vgmstream = NULL;
- ffmpeg_codec_data *data = NULL;
+/* parses any file supported by FFmpeg and not handled elsewhere (mainly: MP4/AAC, MP3, MPC, FLAC) */
+VGMSTREAM* init_vgmstream_ffmpeg(STREAMFILE* sf) {
+ VGMSTREAM* vgmstream = NULL;
+ ffmpeg_codec_data* data = NULL;
int loop_flag = 0;
- int32_t loop_start = -1, loop_end = -1, num_samples = 0;
- int total_subsongs, target_subsong = streamFile->stream_index;
+ int32_t loop_start = 0, loop_end = 0, num_samples = 0;
+ int total_subsongs, target_subsong = sf->stream_index;
/* no checks */
- //if (!check_extensions(streamFile, "..."))
+ //if (!check_extensions(sf, "..."))
// goto fail;
/* don't try to open headers and other mini files */
- if (get_streamfile_size(streamFile) <= 0x1000)
+ if (get_streamfile_size(sf) <= 0x1000)
goto fail;
/* init ffmpeg */
- data = init_ffmpeg_offset(streamFile, start, size);
+ data = init_ffmpeg_offset(sf, 0, get_streamfile_size(sf));
if (!data) return NULL;
total_subsongs = data->streamCount;
@@ -41,80 +35,43 @@ VGMSTREAM * init_vgmstream_ffmpeg_offset(STREAMFILE *streamFile, uint64_t start,
{
uint8_t posbuf[4+4+4];
- if ( read_pos_file(posbuf, 4+4+4, streamFile) ) {
- loop_start = get_32bitLE(posbuf+0);
- loop_end = get_32bitLE(posbuf+4);
+ if (read_pos_file(posbuf, 4+4+4, sf)) {
+ loop_start = get_s32le(posbuf+0);
+ loop_end = get_s32le(posbuf+4);
loop_flag = 1; /* incorrect looping will be validated outside */
/* FFmpeg can't always determine totalSamples correctly so optionally load it (can be 0/NULL)
* won't crash and will output silence if no loop points and bigger than actual stream's samples */
- num_samples = get_32bitLE(posbuf+8);
- } else {
- char* endptr;
- AVDictionary *streamMetadata = data->formatCtx->streams[streamFile->stream_index]->metadata;
-
- // Try to detect the loop flags based on current file metadata
- AVDictionaryEntry *avLoopStart = av_dict_get(streamMetadata, "LoopStart", NULL, AV_DICT_IGNORE_SUFFIX);
- if (avLoopStart != NULL) {
- loop_start = strtol(avLoopStart->value, &endptr, 10);
- loop_flag = 1;
- }
-
- AVDictionaryEntry *avLoopEnd = av_dict_get(streamMetadata, "LoopEnd", NULL, AV_DICT_IGNORE_SUFFIX);
- if (avLoopEnd != NULL) {
- loop_end = strtol(avLoopEnd->value, &endptr, 10);
- loop_flag = 1;
- }
-
- if (loop_flag) {
- if (loop_end <= 0) {
- // Detected a loop, but loop_end is still undefined or wrong. Try to calculate it.
- AVDictionaryEntry *avLoopLength = av_dict_get(streamMetadata, "LoopLength", NULL, AV_DICT_IGNORE_SUFFIX);
- if (avLoopLength != NULL) {
- int loop_length = strtol(avLoopLength->value, &endptr, 10);
-
- if (loop_start != -1) loop_end = loop_start + loop_length;
- }
- }
-
- if (loop_end <= 0) {
- // Looks a calculation was not possible, or tag value is wrongly set. Use the end of track as end value
- loop_end = data->totalSamples;
- }
-
- if (loop_start <= 0) {
- // Weird edge case: loopend is defined and there's a loop, but loopstart was never defined. Reset to sane value
- loop_start = 0;
- }
- } else {
- // Every other attempt to detect loop information failed, reset start/end flags to sane values
- loop_start = 0;
- loop_end = 0;
- }
+ num_samples = get_s32le(posbuf+8);
}
}
+ /* try to read Ogg loop tags (abridged) */
+ if (loop_flag == 0 && read_u32be(0x00, sf) == 0x4F676753) { /* "OggS" */
+ loop_flag = find_ogg_loops(data, &loop_start, &loop_end);
+ }
+
/* hack for AAC files (will return 0 samples if not an actual file) */
- if (!num_samples && check_extensions(streamFile, "aac,laac")) {
- num_samples = aac_get_samples(streamFile, 0x00, get_streamfile_size(streamFile));
+ if (!num_samples && check_extensions(sf, "aac,laac")) {
+ num_samples = aac_get_samples(sf, 0x00, get_streamfile_size(sf));
}
#ifdef VGM_USE_MPEG
/* hack for MP3 files (will return 0 samples if not an actual file)
* .mus: Marc Ecko's Getting Up (PC) */
- if (!num_samples && check_extensions(streamFile, "mp3,lmp3,mus")) {
- num_samples = mpeg_get_samples(streamFile, 0x00, get_streamfile_size(streamFile));
+ if (!num_samples && check_extensions(sf, "mp3,lmp3,mus")) {
+ num_samples = mpeg_get_samples(sf, 0x00, get_streamfile_size(sf));
}
#endif
/* hack for MPC, that seeks/resets incorrectly due to seek table shenanigans */
- if (read_32bitBE(0x00, streamFile) == 0x4D502B07 || /* "MP+\7" (Musepack V7) */
- read_32bitBE(0x00, streamFile) == 0x4D50434B) { /* "MPCK" (Musepack V8) */
+ if (read_u32be(0x00, sf) == 0x4D502B07 || /* "MP+\7" (Musepack V7) */
+ read_u32be(0x00, sf) == 0x4D50434B) { /* "MPCK" (Musepack V8) */
ffmpeg_set_force_seek(data);
}
/* default but often inaccurate when calculated using bitrate (wrong for VBR) */
if (!num_samples) {
- num_samples = data->totalSamples;
+ num_samples = data->totalSamples; /* may be 0 if FFmpeg can't precalculate it */
}
@@ -129,15 +86,8 @@ VGMSTREAM * init_vgmstream_ffmpeg_offset(STREAMFILE *streamFile, uint64_t start,
vgmstream->layout_type = layout_none;
vgmstream->num_samples = num_samples;
- if (loop_flag) {
- vgmstream->loop_start_sample = loop_start;
- vgmstream->loop_end_sample = loop_end;
- }
-
- /* this may happen for some streams if FFmpeg can't determine it (ex. AAC) */
- if (vgmstream->num_samples <= 0)
- goto fail;
-
+ vgmstream->loop_start_sample = loop_start;
+ vgmstream->loop_end_sample = loop_end;
vgmstream->channel_layout = ffmpeg_get_channel_layout(vgmstream->codec_data);
return vgmstream;
@@ -153,46 +103,94 @@ fail:
}
-/**
- * open file containing looping data and copy to buffer
- *
- * returns true if found and copied
- */
-int read_pos_file(uint8_t * buf, size_t bufsize, STREAMFILE *streamFile) {
+/* open file containing looping data and copy to buffer, returns true if found and copied */
+int read_pos_file(uint8_t* buf, size_t bufsize, STREAMFILE* sf) {
char posname[PATH_LIMIT];
char filename[PATH_LIMIT];
/*size_t bytes_read;*/
- STREAMFILE * streamFilePos= NULL;
+ STREAMFILE* sf_pos = NULL;
- streamFile->get_name(streamFile,filename,sizeof(filename));
+ get_streamfile_name(sf,filename,sizeof(filename));
- if (strlen(filename)+4 > sizeof(posname)) goto fail;
+ if (strlen(filename)+4 > sizeof(posname))
+ goto fail;
/* try to open a posfile using variations: "(name.ext).pos" */
{
strcpy(posname, filename);
strcat(posname, ".pos");
- streamFilePos = streamFile->open(streamFile,posname,STREAMFILE_DEFAULT_BUFFER_SIZE);
- if (streamFilePos) goto found;
+ sf_pos = open_streamfile(sf, posname);;
+ if (sf_pos) goto found;
goto fail;
}
found:
- //if (get_streamfile_size(streamFilePos) != bufsize) goto fail;
+ //if (get_streamfile_size(sf_pos) != bufsize) goto fail;
/* allow pos files to be of different sizes in case of new features, just fill all we can */
memset(buf, 0, bufsize);
- read_streamfile(buf, 0, bufsize, streamFilePos);
+ read_streamfile(buf, 0, bufsize, sf_pos);
- close_streamfile(streamFilePos);
+ close_streamfile(sf_pos);
return 1;
fail:
- if (streamFilePos) close_streamfile(streamFilePos);
-
+ close_streamfile(sf_pos);
return 0;
}
+
+/* loop tag handling could be unified with ogg_vorbis.c, but that one has a extra features too */
+static int find_ogg_loops(ffmpeg_codec_data* data, int32_t* p_loop_start, int32_t* p_loop_end) {
+ char* endptr;
+ const char* value;
+ int loop_flag = 0;
+ int32_t loop_start = -1, loop_end = -1;
+
+ // Try to detect the loop flags based on current file metadata
+ value = ffmpeg_get_metadata_value(data, "LoopStart");
+ if (value != NULL) {
+ loop_start = strtol(value, &endptr, 10);
+ loop_flag = 1;
+ }
+
+ value = ffmpeg_get_metadata_value(data, "LoopEnd");
+ if (value != NULL) {
+ loop_end = strtol(value, &endptr, 10);
+ loop_flag = 1;
+ }
+
+ if (loop_flag) {
+ if (loop_end <= 0) {
+ // Detected a loop, but loop_end is still undefined or wrong. Try to calculate it.
+ value = ffmpeg_get_metadata_value(data, "LoopLength");
+ if (value != NULL) {
+ int loop_length = strtol(value, &endptr, 10);
+
+ if (loop_start != -1) loop_end = loop_start + loop_length;
+ }
+ }
+
+ if (loop_end <= 0) {
+ // Looks a calculation was not possible, or tag value is wrongly set. Use the end of track as end value
+ loop_end = data->totalSamples;
+ }
+
+ if (loop_start <= 0) {
+ // Weird edge case: loopend is defined and there's a loop, but loopstart was never defined. Reset to sane value
+ loop_start = 0;
+ }
+ } else {
+ // Every other attempt to detect loop information failed, reset start/end flags to sane values
+ loop_start = 0;
+ loop_end = 0;
+ }
+
+ *p_loop_start = loop_start;
+ *p_loop_end = loop_end;
+ return loop_flag;
+}
+
#endif
diff --git a/src/meta/fsb.c b/src/meta/fsb.c
index 9d4572d3..ae6fcc64 100644
--- a/src/meta/fsb.c
+++ b/src/meta/fsb.c
@@ -67,7 +67,7 @@
/* simplified struct based on the original definitions */
-typedef enum { MPEG, IMA, PSX, XMA2, DSP, CELT, PCM8, PCM16 } fsb_codec_t;
+typedef enum { MPEG, IMA, PSX, XMA, DSP, CELT, PCM8, PCM16 } fsb_codec_t;
typedef struct {
/* main header */
uint32_t id;
@@ -269,7 +269,7 @@ VGMSTREAM* init_vgmstream_fsb(STREAMFILE* sf) {
if (fsb.mode & FSOUND_MPEG) fsb.codec = MPEG;
else if (fsb.mode & FSOUND_IMAADPCM) fsb.codec = IMA;
else if (fsb.mode & FSOUND_VAG) fsb.codec = PSX;
- else if (fsb.mode & FSOUND_XMA) fsb.codec = XMA2;
+ else if (fsb.mode & FSOUND_XMA) fsb.codec = XMA;
else if (fsb.mode & FSOUND_GCADPCM) fsb.codec = DSP;
else if (fsb.mode & FSOUND_CELT) fsb.codec = CELT;
else if (fsb.mode & FSOUND_8BITS) fsb.codec = PCM8;
@@ -382,14 +382,19 @@ VGMSTREAM* init_vgmstream_fsb(STREAMFILE* sf) {
break;
#ifdef VGM_USE_FFMPEG
- case XMA2: { /* FSB3: The Bourne Conspiracy 2008 (X360), FSB4: Armored Core V (X360), Hard Corps (X360) */
+ case XMA: { /* FSB3: The Bourne Conspiracy 2008 (X360), FSB4: Armored Core V (X360), Hard Corps (X360) */
uint8_t buf[0x100];
size_t bytes, block_size, block_count;
- block_size = 0x8000; /* FSB default */
- block_count = fsb.stream_size / block_size; /* not accurate but not needed (custom_data_offset+0x14 -1?) */
+ if (fsb.version != FMOD_FSB_VERSION_4_0) { /* 3.x, though no actual output changes [ex. Guitar Hero III (X360)] */
+ bytes = ffmpeg_make_riff_xma1(buf, sizeof(buf), fsb.num_samples, fsb.stream_size, fsb.channels, fsb.sample_rate, 0);
+ }
+ else {
+ block_size = 0x8000; /* FSB default */
+ block_count = fsb.stream_size / block_size; /* not accurate but not needed (custom_data_offset+0x14 -1?) */
- bytes = ffmpeg_make_riff_xma2(buf,0x100, fsb.num_samples, fsb.stream_size, fsb.channels, fsb.sample_rate, block_count, block_size);
+ bytes = ffmpeg_make_riff_xma2(buf, sizeof(buf), fsb.num_samples, fsb.stream_size, fsb.channels, fsb.sample_rate, block_count, block_size);
+ }
vgmstream->codec_data = init_ffmpeg_header_offset(sf, buf,bytes, fsb.stream_offset,fsb.stream_size);
if (!vgmstream->codec_data) goto fail;
vgmstream->coding_type = coding_FFmpeg;
diff --git a/src/meta/fsb_encrypted.c b/src/meta/fsb_encrypted.c
index 69564a45..289287b8 100644
--- a/src/meta/fsb_encrypted.c
+++ b/src/meta/fsb_encrypted.c
@@ -1,164 +1,85 @@
-#include "meta.h"
-#include "fsb_keys.h"
-
-#define FSB_KEY_MAX 128 /* probably 32 */
-
-static STREAMFILE* setup_fsb_streamfile(STREAMFILE *streamFile, const uint8_t * key, size_t key_size, int is_alt);
-
-
-/* fully encrypted FSBs */
-VGMSTREAM * init_vgmstream_fsb_encrypted(STREAMFILE * streamFile) {
- VGMSTREAM * vgmstream = NULL;
-
- /* checks */
- /* .fsb: standard
- * .fsb.xen: various Guitar Hero (X360) */
- if ( !check_extensions(streamFile, "fsb,xen") )
- goto fail;
-
- /* ignore non-encrypted FSB */
- if ((read_32bitBE(0x00,streamFile) & 0xFFFFFF00) == 0x46534200) /* "FSB\0" */
- goto fail;
-
-
- /* try fsbkey + all combinations of FSB4/5 and decryption algorithms */
- {
- STREAMFILE *temp_streamFile = NULL;
- uint8_t key[FSB_KEY_MAX];
- size_t key_size = read_key_file(key, FSB_KEY_MAX, streamFile);
-
- if (key_size) {
- {
- temp_streamFile = setup_fsb_streamfile(streamFile, key,key_size, 0);
- if (!temp_streamFile) goto fail;
-
- if (!vgmstream) vgmstream = init_vgmstream_fsb(temp_streamFile);
- if (!vgmstream) vgmstream = init_vgmstream_fsb5(temp_streamFile);
-
- close_streamfile(temp_streamFile);
- }
-
- if (!vgmstream) {
- temp_streamFile = setup_fsb_streamfile(streamFile, key,key_size, 1);
- if (!temp_streamFile) goto fail;
-
- if (!vgmstream) vgmstream = init_vgmstream_fsb(temp_streamFile);
- if (!vgmstream) vgmstream = init_vgmstream_fsb5(temp_streamFile);
-
- close_streamfile(temp_streamFile);
- }
- }
- }
-
-
- /* try all keys until one works */
- if (!vgmstream) {
- int i;
- STREAMFILE *temp_streamFile = NULL;
-
- for (i = 0; i < fsbkey_list_count; i++) {
- fsbkey_info entry = fsbkey_list[i];
- //;VGM_LOG("fsbkey: size=%i, is_fsb5=%i, is_alt=%i\n", entry.fsbkey_size,entry.is_fsb5, entry.is_alt);
-
- temp_streamFile = setup_fsb_streamfile(streamFile, entry.fsbkey, entry.fsbkey_size, entry.is_alt);
- if (!temp_streamFile) goto fail;
-
- if (fsbkey_list[i].is_fsb5) {
- vgmstream = init_vgmstream_fsb5(temp_streamFile);
- } else {
- vgmstream = init_vgmstream_fsb(temp_streamFile);
- }
-
- close_streamfile(temp_streamFile);
- if (vgmstream) break;
- }
- }
-
- if (!vgmstream)
- goto fail;
-
- return vgmstream;
-
-fail:
- close_vgmstream(vgmstream);
- return NULL;
-}
-
-
-typedef struct {
- uint8_t key[FSB_KEY_MAX];
- size_t key_size;
- int is_alt;
-} fsb_decryption_data;
-
-/* Encrypted FSB info from guessfsb and fsbext */
-static size_t fsb_decryption_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length, fsb_decryption_data* data) {
- static const unsigned char reverse_bits_table[] = { /* LUT to simplify, could use some bitswap function */
- 0x00,0x80,0x40,0xC0,0x20,0xA0,0x60,0xE0,0x10,0x90,0x50,0xD0,0x30,0xB0,0x70,0xF0,
- 0x08,0x88,0x48,0xC8,0x28,0xA8,0x68,0xE8,0x18,0x98,0x58,0xD8,0x38,0xB8,0x78,0xF8,
- 0x04,0x84,0x44,0xC4,0x24,0xA4,0x64,0xE4,0x14,0x94,0x54,0xD4,0x34,0xB4,0x74,0xF4,
- 0x0C,0x8C,0x4C,0xCC,0x2C,0xAC,0x6C,0xEC,0x1C,0x9C,0x5C,0xDC,0x3C,0xBC,0x7C,0xFC,
- 0x02,0x82,0x42,0xC2,0x22,0xA2,0x62,0xE2,0x12,0x92,0x52,0xD2,0x32,0xB2,0x72,0xF2,
- 0x0A,0x8A,0x4A,0xCA,0x2A,0xAA,0x6A,0xEA,0x1A,0x9A,0x5A,0xDA,0x3A,0xBA,0x7A,0xFA,
- 0x06,0x86,0x46,0xC6,0x26,0xA6,0x66,0xE6,0x16,0x96,0x56,0xD6,0x36,0xB6,0x76,0xF6,
- 0x0E,0x8E,0x4E,0xCE,0x2E,0xAE,0x6E,0xEE,0x1E,0x9E,0x5E,0xDE,0x3E,0xBE,0x7E,0xFE,
- 0x01,0x81,0x41,0xC1,0x21,0xA1,0x61,0xE1,0x11,0x91,0x51,0xD1,0x31,0xB1,0x71,0xF1,
- 0x09,0x89,0x49,0xC9,0x29,0xA9,0x69,0xE9,0x19,0x99,0x59,0xD9,0x39,0xB9,0x79,0xF9,
- 0x05,0x85,0x45,0xC5,0x25,0xA5,0x65,0xE5,0x15,0x95,0x55,0xD5,0x35,0xB5,0x75,0xF5,
- 0x0D,0x8D,0x4D,0xCD,0x2D,0xAD,0x6D,0xED,0x1D,0x9D,0x5D,0xDD,0x3D,0xBD,0x7D,0xFD,
- 0x03,0x83,0x43,0xC3,0x23,0xA3,0x63,0xE3,0x13,0x93,0x53,0xD3,0x33,0xB3,0x73,0xF3,
- 0x0B,0x8B,0x4B,0xCB,0x2B,0xAB,0x6B,0xEB,0x1B,0x9B,0x5B,0xDB,0x3B,0xBB,0x7B,0xFB,
- 0x07,0x87,0x47,0xC7,0x27,0xA7,0x67,0xE7,0x17,0x97,0x57,0xD7,0x37,0xB7,0x77,0xF7,
- 0x0F,0x8F,0x4F,0xCF,0x2F,0xAF,0x6F,0xEF,0x1F,0x9F,0x5F,0xDF,0x3F,0xBF,0x7F,0xFF
- };
- size_t bytes_read;
- int i;
-
- bytes_read = streamfile->read(streamfile, dest, offset, length);
-
- /* decrypt data (inverted bits and xor) */
- for (i = 0; i < bytes_read; i++) {
- uint8_t xor = data->key[(offset + i) % data->key_size];
- uint8_t val = dest[i];
- if (data->is_alt) {
- dest[i] = reverse_bits_table[val ^ xor];
- }
- else {
- dest[i] = reverse_bits_table[val] ^ xor;
- }
- }
-
- return bytes_read;
-}
-
-static STREAMFILE* setup_fsb_streamfile(STREAMFILE *streamFile, const uint8_t * key, size_t key_size, int is_alt) {
- STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL;
- fsb_decryption_data io_data = {0};
- size_t io_data_size = sizeof(fsb_decryption_data);
-
- /* setup decryption with key (external) */
- if (!key_size || key_size > FSB_KEY_MAX) goto fail;
-
- memcpy(io_data.key, key, key_size);
- io_data.key_size = key_size;
- io_data.is_alt = is_alt;
-
- /* setup subfile */
- new_streamFile = open_wrap_streamfile(streamFile);
- if (!new_streamFile) goto fail;
- temp_streamFile = new_streamFile;
-
- new_streamFile = open_io_streamfile(temp_streamFile, &io_data,io_data_size, fsb_decryption_read,NULL);
- if (!new_streamFile) goto fail;
- temp_streamFile = new_streamFile;
-
- new_streamFile = open_fakename_streamfile(temp_streamFile, NULL,"fsb");
- if (!new_streamFile) goto fail;
- temp_streamFile = new_streamFile;
-
- return temp_streamFile;
-
-fail:
- close_streamfile(temp_streamFile);
- return NULL;
-}
+#include "meta.h"
+#include "fsb_keys.h"
+#include "fsb_encrypted_streamfile.h"
+
+
+/* fully encrypted FSBs */
+VGMSTREAM* init_vgmstream_fsb_encrypted(STREAMFILE* sf) {
+ VGMSTREAM* vgmstream = NULL;
+
+ /* checks */
+ /* .fsb: standard
+ * .fsb.xen: various Guitar Hero (X360/PC) */
+ if (!check_extensions(sf, "fsb,xen"))
+ goto fail;
+
+ /* ignore non-encrypted FSB */
+ if ((read_u32be(0x00,sf) & 0xFFFFFF00) == 0x46534200) /* "FSB\0" */
+ goto fail;
+
+
+ /* try fsbkey + all combinations of FSB4/5 and decryption algorithms */
+ {
+ STREAMFILE* temp_sf = NULL;
+ uint8_t key[FSB_KEY_MAX];
+ size_t key_size = read_key_file(key, FSB_KEY_MAX, sf);
+
+ if (key_size) {
+ {
+ temp_sf = setup_fsb_streamfile(sf, key,key_size, 0);
+ if (!temp_sf) goto fail;
+
+ if (!vgmstream) vgmstream = init_vgmstream_fsb(temp_sf);
+ if (!vgmstream) vgmstream = init_vgmstream_fsb5(temp_sf);
+
+ close_streamfile(temp_sf);
+ }
+
+ if (!vgmstream) {
+ temp_sf = setup_fsb_streamfile(sf, key,key_size, 1);
+ if (!temp_sf) goto fail;
+
+ if (!vgmstream) vgmstream = init_vgmstream_fsb(temp_sf);
+ if (!vgmstream) vgmstream = init_vgmstream_fsb5(temp_sf);
+
+ close_streamfile(temp_sf);
+ }
+ }
+ }
+
+
+ /* try all keys until one works */
+ if (!vgmstream) {
+ int i;
+ STREAMFILE* temp_sf = NULL;
+
+ for (i = 0; i < fsbkey_list_count; i++) {
+ fsbkey_info entry = fsbkey_list[i];
+ //;VGM_LOG("fsbkey: size=%i, is_fsb5=%i, is_alt=%i\n", entry.fsbkey_size,entry.is_fsb5, entry.is_alt);
+
+ temp_sf = setup_fsb_streamfile(sf, entry.fsbkey, entry.fsbkey_size, entry.is_alt);
+ if (!temp_sf) goto fail;
+
+ if (fsbkey_list[i].is_fsb5) {
+ vgmstream = init_vgmstream_fsb5(temp_sf);
+ } else {
+ vgmstream = init_vgmstream_fsb(temp_sf);
+ }
+
+ if (vgmstream)
+ dump_streamfile(temp_sf, 0);
+
+ close_streamfile(temp_sf);
+ if (vgmstream) break;
+ }
+ }
+
+ if (!vgmstream)
+ goto fail;
+
+ return vgmstream;
+
+fail:
+ close_vgmstream(vgmstream);
+ return NULL;
+}
diff --git a/src/meta/fsb_encrypted_streamfile.h b/src/meta/fsb_encrypted_streamfile.h
new file mode 100644
index 00000000..eae35d46
--- /dev/null
+++ b/src/meta/fsb_encrypted_streamfile.h
@@ -0,0 +1,73 @@
+#ifndef _FSB_ENCRYPTED_STREAMFILE_H_
+#define _FSB_ENCRYPTED_H_
+
+#define FSB_KEY_MAX 128 /* probably 32 */
+
+
+typedef struct {
+ uint8_t key[FSB_KEY_MAX];
+ size_t key_size;
+ int is_alt;
+} fsb_decryption_data;
+
+/* Encrypted FSB info from guessfsb and fsbext */
+static size_t fsb_decryption_read(STREAMFILE* sf, uint8_t *dest, off_t offset, size_t length, fsb_decryption_data* data) {
+ static const unsigned char reverse_bits_table[] = { /* LUT to simplify, could use some bitswap function */
+ 0x00,0x80,0x40,0xC0,0x20,0xA0,0x60,0xE0,0x10,0x90,0x50,0xD0,0x30,0xB0,0x70,0xF0,
+ 0x08,0x88,0x48,0xC8,0x28,0xA8,0x68,0xE8,0x18,0x98,0x58,0xD8,0x38,0xB8,0x78,0xF8,
+ 0x04,0x84,0x44,0xC4,0x24,0xA4,0x64,0xE4,0x14,0x94,0x54,0xD4,0x34,0xB4,0x74,0xF4,
+ 0x0C,0x8C,0x4C,0xCC,0x2C,0xAC,0x6C,0xEC,0x1C,0x9C,0x5C,0xDC,0x3C,0xBC,0x7C,0xFC,
+ 0x02,0x82,0x42,0xC2,0x22,0xA2,0x62,0xE2,0x12,0x92,0x52,0xD2,0x32,0xB2,0x72,0xF2,
+ 0x0A,0x8A,0x4A,0xCA,0x2A,0xAA,0x6A,0xEA,0x1A,0x9A,0x5A,0xDA,0x3A,0xBA,0x7A,0xFA,
+ 0x06,0x86,0x46,0xC6,0x26,0xA6,0x66,0xE6,0x16,0x96,0x56,0xD6,0x36,0xB6,0x76,0xF6,
+ 0x0E,0x8E,0x4E,0xCE,0x2E,0xAE,0x6E,0xEE,0x1E,0x9E,0x5E,0xDE,0x3E,0xBE,0x7E,0xFE,
+ 0x01,0x81,0x41,0xC1,0x21,0xA1,0x61,0xE1,0x11,0x91,0x51,0xD1,0x31,0xB1,0x71,0xF1,
+ 0x09,0x89,0x49,0xC9,0x29,0xA9,0x69,0xE9,0x19,0x99,0x59,0xD9,0x39,0xB9,0x79,0xF9,
+ 0x05,0x85,0x45,0xC5,0x25,0xA5,0x65,0xE5,0x15,0x95,0x55,0xD5,0x35,0xB5,0x75,0xF5,
+ 0x0D,0x8D,0x4D,0xCD,0x2D,0xAD,0x6D,0xED,0x1D,0x9D,0x5D,0xDD,0x3D,0xBD,0x7D,0xFD,
+ 0x03,0x83,0x43,0xC3,0x23,0xA3,0x63,0xE3,0x13,0x93,0x53,0xD3,0x33,0xB3,0x73,0xF3,
+ 0x0B,0x8B,0x4B,0xCB,0x2B,0xAB,0x6B,0xEB,0x1B,0x9B,0x5B,0xDB,0x3B,0xBB,0x7B,0xFB,
+ 0x07,0x87,0x47,0xC7,0x27,0xA7,0x67,0xE7,0x17,0x97,0x57,0xD7,0x37,0xB7,0x77,0xF7,
+ 0x0F,0x8F,0x4F,0xCF,0x2F,0xAF,0x6F,0xEF,0x1F,0x9F,0x5F,0xDF,0x3F,0xBF,0x7F,0xFF
+ };
+ size_t bytes_read;
+ int i;
+
+ bytes_read = read_streamfile(dest, offset, length, sf);
+
+ /* decrypt data (inverted bits and xor) */
+ for (i = 0; i < bytes_read; i++) {
+ uint8_t xor = data->key[(offset + i) % data->key_size];
+ uint8_t val = dest[i];
+ if (data->is_alt) {
+ dest[i] = reverse_bits_table[val ^ xor];
+ }
+ else {
+ dest[i] = reverse_bits_table[val] ^ xor;
+ }
+ }
+
+ return bytes_read;
+}
+
+static STREAMFILE* setup_fsb_streamfile(STREAMFILE* sf, const uint8_t* key, size_t key_size, int is_alt) {
+ STREAMFILE* new_sf = NULL;
+ fsb_decryption_data io_data = {0};
+ size_t io_data_size = sizeof(fsb_decryption_data);
+
+ /* setup decryption with key (external) */
+ if (!key_size || key_size > FSB_KEY_MAX)
+ return NULL;
+
+ memcpy(io_data.key, key, key_size);
+ io_data.key_size = key_size;
+ io_data.is_alt = is_alt;
+
+ /* setup subfile */
+ new_sf = open_wrap_streamfile(sf);
+ new_sf = open_io_streamfile_f(new_sf, &io_data,io_data_size, fsb_decryption_read,NULL);
+ new_sf = open_fakename_streamfile(new_sf, NULL,"fsb");
+ return new_sf;
+}
+
+#endif /* _FSB5_STREAMFILE_H_ */
diff --git a/src/meta/meta.h b/src/meta/meta.h
index 714afa22..bfd26910 100644
--- a/src/meta/meta.h
+++ b/src/meta/meta.h
@@ -137,7 +137,6 @@ VGMSTREAM * init_vgmstream_hca_subkey(STREAMFILE *streamFile, uint16_t subkey);
#ifdef VGM_USE_FFMPEG
VGMSTREAM * init_vgmstream_ffmpeg(STREAMFILE *streamFile);
-VGMSTREAM * init_vgmstream_ffmpeg_offset(STREAMFILE *streamFile, uint64_t start, uint64_t size);
VGMSTREAM * init_vgmstream_mp4_aac_ffmpeg(STREAMFILE * streamFile);
#endif
diff --git a/src/meta/mups.c b/src/meta/mups.c
index e787a645..fbfa5c65 100644
--- a/src/meta/mups.c
+++ b/src/meta/mups.c
@@ -21,12 +21,15 @@ VGMSTREAM* init_vgmstream_mups(STREAMFILE* sf) {
goto fail;
/* just an Ogg with changed OggS/vorbis words (see streamfile) */
-
temp_sf = setup_mups_streamfile(sf, 0x08);
if (!temp_sf) goto fail;
+#ifdef VGM_USE_VORBIS
vgmstream = init_vgmstream_ogg_vorbis(temp_sf);
if (!vgmstream) goto fail;
+#else
+ goto fail;
+#endif
close_streamfile(temp_sf);
diff --git a/src/meta/ubi_sb.c b/src/meta/ubi_sb.c
index 6c78cb5d..3ce00658 100644
--- a/src/meta/ubi_sb.c
+++ b/src/meta/ubi_sb.c
@@ -3792,6 +3792,19 @@ static int config_sb_version(ubi_sb_header* sb, STREAMFILE* sf) {
return 1;
}
+ /* Tom Clancy's Ghost Recon Advanced Warfighter 2 (2007)(X360)-bank */
+ if (sb->version == 0x0018000b && sb->platform == UBI_X360) {
+ config_sb_entry(sb, 0x68, 0x70);
+
+ config_sb_audio_fs(sb, 0x28, 0x2c, 0x30);
+ config_sb_audio_he(sb, 0x3c, 0x40, 0x48, 0x50, 0x58, 0x5c);
+ sb->cfg.audio_xma_offset = 0x68;
+
+ config_sb_sequence(sb, 0x2c, 0x14);
+
+ return 1;
+ }
+
/* TMNT (2007)(PSP)-map 0x00190001 */
/* Surf's Up (2007)(PSP)-map 0x00190005 */
if ((sb->version == 0x00190001 && sb->platform == UBI_PSP) ||
@@ -3888,6 +3901,18 @@ static int config_sb_version(ubi_sb_header* sb, STREAMFILE* sf) {
return 1;
}
+ /* Tom Clancy's Ghost Recon Advanced Warfighter 2 (2007)(PS3)-bank */
+ if (sb->version == 0x001A0003 && sb->platform == UBI_PS3) {
+ config_sb_entry(sb, 0x6c, 0x78);
+
+ config_sb_audio_fs(sb, 0x30, 0x34, 0x38);
+ config_sb_audio_he(sb, 0x40, 0x44, 0x4c, 0x54, 0x5c, 0x60);
+
+ config_sb_sequence(sb, 0x2c, 0x14);
+
+ return 1;
+ }
+
/* Cranium Kabookii (2007)(Wii)-bank */
if (sb->version == 0x001A0003 && sb->platform == UBI_WII) {
config_sb_entry(sb, 0x6c, 0x78);
diff --git a/src/util.c b/src/util.c
index 95b58a4f..3c7ccc3e 100644
--- a/src/util.c
+++ b/src/util.c
@@ -61,8 +61,6 @@ void interleave_stereo(sample_t * buffer, int32_t sample_count) {
else
belongs = (tomove-sample_count)*2+1;
- printf("move %d to %d\n",tomove,belongs);
-
temp = buffer[belongs];
buffer[belongs] = moving;
moving = temp;
@@ -100,6 +98,14 @@ void put_32bitBE(uint8_t * buf, int32_t i) {
buf[3] = (uint8_t)(i & 0xFF);
}
+int round10(int val) {
+ int round_val = val % 10;
+ if (round_val < 5) /* half-down rounding */
+ return val - round_val;
+ else
+ return val + (10 - round_val);
+}
+
void swap_samples_le(sample_t *buf, int count) {
/* Windows can't be BE... I think */
#if !defined(_WIN32)
diff --git a/src/util.h b/src/util.h
index 98c97100..21a98d89 100644
--- a/src/util.h
+++ b/src/util.h
@@ -7,6 +7,8 @@
#ifndef _UTIL_H
#define _UTIL_H
+/* very common functions, so static inline in .h is useful to avoid some call overhead */
+
/* host endian independent multi-byte integer reading */
static inline int16_t get_16bitBE(uint8_t * p) {
@@ -91,13 +93,9 @@ static inline int clamp16(int32_t val) {
else return val;
}
-static inline int round10(int val) {
- int round_val = val % 10;
- if (round_val < 5) /* half-down rounding */
- return val - round_val;
- else
- return val + (10 - round_val);
-}
+/* less common functions, no need to inline */
+
+int round10(int val);
/* 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 */
diff --git a/src/vgmstream.c b/src/vgmstream.c
index c5e41fd3..4bc02404 100644
--- a/src/vgmstream.c
+++ b/src/vgmstream.c
@@ -498,9 +498,7 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = {
init_vgmstream_diva,
init_vgmstream_imuse,
init_vgmstream_ktsr,
-#ifdef VGM_USE_VORBIS
init_vgmstream_mups,
-#endif
init_vgmstream_kat,
/* lowest priority metas (should go after all metas, and TXTH should go before raw formats) */