Merge pull request #642 from bnnm/fsb-ffmpeg

fsb ffmpeg
This commit is contained in:
bnnm 2020-06-04 23:32:01 +02:00 committed by GitHub
commit ddb9eaf8f6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 334 additions and 279 deletions

View File

@ -845,7 +845,7 @@ JIN003.XAG: 0x180
``` ```
**Grandia (PS1) bgm.txth** #### Grandia (PS1) bgm.txth
``` ```
header_file = GM1.IDX header_file = GM1.IDX
body_file = GM1.STZ body_file = GM1.STZ

View File

@ -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); void ffmpeg_set_channel_remapping(ffmpeg_codec_data * data, int *channels_remap);
const char* ffmpeg_get_codec_name(ffmpeg_codec_data * data); const char* ffmpeg_get_codec_name(ffmpeg_codec_data * data);
void ffmpeg_set_force_seek(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) */ /* ffmpeg_decoder_utils.c (helper-things) */

View File

@ -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); 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); memcpy(buf+riff_size-4-4, "data", 4);
put_32bitLE(buf+riff_size-4, data_size); /* data size */ put_32bitLE(buf+riff_size-4, data_size); /* data size */

View File

@ -941,4 +941,22 @@ void ffmpeg_set_force_seek(ffmpeg_codec_data * data) {
//stream = data->formatCtx->streams[data->streamIndex]; //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 #endif

View File

@ -276,6 +276,10 @@
RelativePath=".\meta\ea_schl_streamfile.h" RelativePath=".\meta\ea_schl_streamfile.h"
> >
</File> </File>
<File
RelativePath=".\meta\fsb_encrypted_streamfile.h"
>
</File>
<File <File
RelativePath=".\meta\fsb_interleave_streamfile.h" RelativePath=".\meta\fsb_interleave_streamfile.h"
> >

View File

@ -111,6 +111,7 @@
<ClInclude Include="meta\ea_eaac_streamfile.h" /> <ClInclude Include="meta\ea_eaac_streamfile.h" />
<ClInclude Include="meta\ea_eaac_opus_streamfile.h" /> <ClInclude Include="meta\ea_eaac_opus_streamfile.h" />
<ClInclude Include="meta\ea_schl_streamfile.h" /> <ClInclude Include="meta\ea_schl_streamfile.h" />
<ClInclude Include="meta\fsb_encrypted_streamfile.h" />
<ClInclude Include="meta\fsb_interleave_streamfile.h" /> <ClInclude Include="meta\fsb_interleave_streamfile.h" />
<ClInclude Include="meta\fsb5_streamfile.h" /> <ClInclude Include="meta\fsb5_streamfile.h" />
<ClInclude Include="meta\jstm_streamfile.h" /> <ClInclude Include="meta\jstm_streamfile.h" />

View File

@ -104,6 +104,9 @@
<ClInclude Include="meta\ea_schl_streamfile.h"> <ClInclude Include="meta\ea_schl_streamfile.h">
<Filter>meta\Header Files</Filter> <Filter>meta\Header Files</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="meta\fsb_encrypted_streamfile.h">
<Filter>meta\Header Files</Filter>
</ClInclude>
<ClInclude Include="meta\fsb_interleave_streamfile.h"> <ClInclude Include="meta\fsb_interleave_streamfile.h">
<Filter>meta\Header Files</Filter> <Filter>meta\Header Files</Filter>
</ClInclude> </ClInclude>

View File

@ -3,34 +3,28 @@
#ifdef VGM_USE_FFMPEG #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);
/** /* parses any file supported by FFmpeg and not handled elsewhere (mainly: MP4/AAC, MP3, MPC, FLAC) */
* Generic init FFmpeg and vgmstream for any file supported by FFmpeg. VGMSTREAM* init_vgmstream_ffmpeg(STREAMFILE* sf) {
* Called by vgmstream when trying to identify the file type (if the player allows it). VGMSTREAM* vgmstream = NULL;
*/ ffmpeg_codec_data* data = NULL;
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;
int loop_flag = 0; int loop_flag = 0;
int32_t loop_start = -1, loop_end = -1, num_samples = 0; int32_t loop_start = 0, loop_end = 0, num_samples = 0;
int total_subsongs, target_subsong = streamFile->stream_index; int total_subsongs, target_subsong = sf->stream_index;
/* no checks */ /* no checks */
//if (!check_extensions(streamFile, "...")) //if (!check_extensions(sf, "..."))
// goto fail; // goto fail;
/* don't try to open headers and other mini files */ /* don't try to open headers and other mini files */
if (get_streamfile_size(streamFile) <= 0x1000) if (get_streamfile_size(sf) <= 0x1000)
goto fail; goto fail;
/* init ffmpeg */ /* init ffmpeg */
data = init_ffmpeg_offset(streamFile, start, size); data = init_ffmpeg_offset(sf, 0, get_streamfile_size(sf));
if (!data) return NULL; if (!data) return NULL;
total_subsongs = data->streamCount; total_subsongs = data->streamCount;
@ -41,80 +35,43 @@ VGMSTREAM * init_vgmstream_ffmpeg_offset(STREAMFILE *streamFile, uint64_t start,
{ {
uint8_t posbuf[4+4+4]; uint8_t posbuf[4+4+4];
if ( read_pos_file(posbuf, 4+4+4, streamFile) ) { if (read_pos_file(posbuf, 4+4+4, sf)) {
loop_start = get_32bitLE(posbuf+0); loop_start = get_s32le(posbuf+0);
loop_end = get_32bitLE(posbuf+4); loop_end = get_s32le(posbuf+4);
loop_flag = 1; /* incorrect looping will be validated outside */ loop_flag = 1; /* incorrect looping will be validated outside */
/* FFmpeg can't always determine totalSamples correctly so optionally load it (can be 0/NULL) /* 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 */ * won't crash and will output silence if no loop points and bigger than actual stream's samples */
num_samples = get_32bitLE(posbuf+8); num_samples = get_s32le(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;
}
} }
} }
/* 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) */ /* hack for AAC files (will return 0 samples if not an actual file) */
if (!num_samples && check_extensions(streamFile, "aac,laac")) { if (!num_samples && check_extensions(sf, "aac,laac")) {
num_samples = aac_get_samples(streamFile, 0x00, get_streamfile_size(streamFile)); num_samples = aac_get_samples(sf, 0x00, get_streamfile_size(sf));
} }
#ifdef VGM_USE_MPEG #ifdef VGM_USE_MPEG
/* hack for MP3 files (will return 0 samples if not an actual file) /* hack for MP3 files (will return 0 samples if not an actual file)
* .mus: Marc Ecko's Getting Up (PC) */ * .mus: Marc Ecko's Getting Up (PC) */
if (!num_samples && check_extensions(streamFile, "mp3,lmp3,mus")) { if (!num_samples && check_extensions(sf, "mp3,lmp3,mus")) {
num_samples = mpeg_get_samples(streamFile, 0x00, get_streamfile_size(streamFile)); num_samples = mpeg_get_samples(sf, 0x00, get_streamfile_size(sf));
} }
#endif #endif
/* hack for MPC, that seeks/resets incorrectly due to seek table shenanigans */ /* hack for MPC, that seeks/resets incorrectly due to seek table shenanigans */
if (read_32bitBE(0x00, streamFile) == 0x4D502B07 || /* "MP+\7" (Musepack V7) */ if (read_u32be(0x00, sf) == 0x4D502B07 || /* "MP+\7" (Musepack V7) */
read_32bitBE(0x00, streamFile) == 0x4D50434B) { /* "MPCK" (Musepack V8) */ read_u32be(0x00, sf) == 0x4D50434B) { /* "MPCK" (Musepack V8) */
ffmpeg_set_force_seek(data); ffmpeg_set_force_seek(data);
} }
/* default but often inaccurate when calculated using bitrate (wrong for VBR) */ /* default but often inaccurate when calculated using bitrate (wrong for VBR) */
if (!num_samples) { 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->layout_type = layout_none;
vgmstream->num_samples = num_samples; vgmstream->num_samples = num_samples;
if (loop_flag) { vgmstream->loop_start_sample = loop_start;
vgmstream->loop_start_sample = loop_start; vgmstream->loop_end_sample = loop_end;
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->channel_layout = ffmpeg_get_channel_layout(vgmstream->codec_data); vgmstream->channel_layout = ffmpeg_get_channel_layout(vgmstream->codec_data);
return vgmstream; return vgmstream;
@ -153,46 +103,94 @@ fail:
} }
/** /* open file containing looping data and copy to buffer, returns true if found and copied */
* open file containing looping data and copy to buffer int read_pos_file(uint8_t* buf, size_t bufsize, STREAMFILE* sf) {
*
* returns true if found and copied
*/
int read_pos_file(uint8_t * buf, size_t bufsize, STREAMFILE *streamFile) {
char posname[PATH_LIMIT]; char posname[PATH_LIMIT];
char filename[PATH_LIMIT]; char filename[PATH_LIMIT];
/*size_t bytes_read;*/ /*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" */ /* try to open a posfile using variations: "(name.ext).pos" */
{ {
strcpy(posname, filename); strcpy(posname, filename);
strcat(posname, ".pos"); strcat(posname, ".pos");
streamFilePos = streamFile->open(streamFile,posname,STREAMFILE_DEFAULT_BUFFER_SIZE); sf_pos = open_streamfile(sf, posname);;
if (streamFilePos) goto found; if (sf_pos) goto found;
goto fail; goto fail;
} }
found: 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 */ /* allow pos files to be of different sizes in case of new features, just fill all we can */
memset(buf, 0, bufsize); 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; return 1;
fail: fail:
if (streamFilePos) close_streamfile(streamFilePos); close_streamfile(sf_pos);
return 0; 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 #endif

View File

@ -67,7 +67,7 @@
/* simplified struct based on the original definitions */ /* 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 { typedef struct {
/* main header */ /* main header */
uint32_t id; uint32_t id;
@ -269,7 +269,7 @@ VGMSTREAM* init_vgmstream_fsb(STREAMFILE* sf) {
if (fsb.mode & FSOUND_MPEG) fsb.codec = MPEG; if (fsb.mode & FSOUND_MPEG) fsb.codec = MPEG;
else if (fsb.mode & FSOUND_IMAADPCM) fsb.codec = IMA; else if (fsb.mode & FSOUND_IMAADPCM) fsb.codec = IMA;
else if (fsb.mode & FSOUND_VAG) fsb.codec = PSX; 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_GCADPCM) fsb.codec = DSP;
else if (fsb.mode & FSOUND_CELT) fsb.codec = CELT; else if (fsb.mode & FSOUND_CELT) fsb.codec = CELT;
else if (fsb.mode & FSOUND_8BITS) fsb.codec = PCM8; else if (fsb.mode & FSOUND_8BITS) fsb.codec = PCM8;
@ -382,14 +382,19 @@ VGMSTREAM* init_vgmstream_fsb(STREAMFILE* sf) {
break; break;
#ifdef VGM_USE_FFMPEG #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]; uint8_t buf[0x100];
size_t bytes, block_size, block_count; size_t bytes, block_size, block_count;
block_size = 0x8000; /* FSB default */ if (fsb.version != FMOD_FSB_VERSION_4_0) { /* 3.x, though no actual output changes [ex. Guitar Hero III (X360)] */
block_count = fsb.stream_size / block_size; /* not accurate but not needed (custom_data_offset+0x14 -1?) */ 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); vgmstream->codec_data = init_ffmpeg_header_offset(sf, buf,bytes, fsb.stream_offset,fsb.stream_size);
if (!vgmstream->codec_data) goto fail; if (!vgmstream->codec_data) goto fail;
vgmstream->coding_type = coding_FFmpeg; vgmstream->coding_type = coding_FFmpeg;

View File

@ -1,164 +1,85 @@
#include "meta.h" #include "meta.h"
#include "fsb_keys.h" #include "fsb_keys.h"
#include "fsb_encrypted_streamfile.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* sf) {
VGMSTREAM* vgmstream = NULL;
/* fully encrypted FSBs */
VGMSTREAM * init_vgmstream_fsb_encrypted(STREAMFILE * streamFile) { /* checks */
VGMSTREAM * vgmstream = NULL; /* .fsb: standard
* .fsb.xen: various Guitar Hero (X360/PC) */
/* checks */ if (!check_extensions(sf, "fsb,xen"))
/* .fsb: standard goto fail;
* .fsb.xen: various Guitar Hero (X360) */
if ( !check_extensions(streamFile, "fsb,xen") ) /* ignore non-encrypted FSB */
goto fail; if ((read_u32be(0x00,sf) & 0xFFFFFF00) == 0x46534200) /* "FSB\0" */
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_sf = NULL;
/* try fsbkey + all combinations of FSB4/5 and decryption algorithms */ uint8_t key[FSB_KEY_MAX];
{ size_t key_size = read_key_file(key, FSB_KEY_MAX, sf);
STREAMFILE *temp_streamFile = NULL;
uint8_t key[FSB_KEY_MAX]; if (key_size) {
size_t key_size = read_key_file(key, FSB_KEY_MAX, streamFile); {
temp_sf = setup_fsb_streamfile(sf, key,key_size, 0);
if (key_size) { if (!temp_sf) goto fail;
{
temp_streamFile = setup_fsb_streamfile(streamFile, key,key_size, 0); if (!vgmstream) vgmstream = init_vgmstream_fsb(temp_sf);
if (!temp_streamFile) goto fail; if (!vgmstream) vgmstream = init_vgmstream_fsb5(temp_sf);
if (!vgmstream) vgmstream = init_vgmstream_fsb(temp_streamFile); close_streamfile(temp_sf);
if (!vgmstream) vgmstream = init_vgmstream_fsb5(temp_streamFile); }
close_streamfile(temp_streamFile); if (!vgmstream) {
} temp_sf = setup_fsb_streamfile(sf, key,key_size, 1);
if (!temp_sf) goto fail;
if (!vgmstream) {
temp_streamFile = setup_fsb_streamfile(streamFile, key,key_size, 1); if (!vgmstream) vgmstream = init_vgmstream_fsb(temp_sf);
if (!temp_streamFile) goto fail; if (!vgmstream) vgmstream = init_vgmstream_fsb5(temp_sf);
if (!vgmstream) vgmstream = init_vgmstream_fsb(temp_streamFile); close_streamfile(temp_sf);
if (!vgmstream) vgmstream = init_vgmstream_fsb5(temp_streamFile); }
}
close_streamfile(temp_streamFile); }
}
}
} /* try all keys until one works */
if (!vgmstream) {
int i;
/* try all keys until one works */ STREAMFILE* temp_sf = NULL;
if (!vgmstream) {
int i; for (i = 0; i < fsbkey_list_count; i++) {
STREAMFILE *temp_streamFile = NULL; 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);
for (i = 0; i < fsbkey_list_count; i++) {
fsbkey_info entry = fsbkey_list[i]; temp_sf = setup_fsb_streamfile(sf, entry.fsbkey, entry.fsbkey_size, entry.is_alt);
//;VGM_LOG("fsbkey: size=%i, is_fsb5=%i, is_alt=%i\n", entry.fsbkey_size,entry.is_fsb5, entry.is_alt); if (!temp_sf) goto fail;
temp_streamFile = setup_fsb_streamfile(streamFile, entry.fsbkey, entry.fsbkey_size, entry.is_alt); if (fsbkey_list[i].is_fsb5) {
if (!temp_streamFile) goto fail; vgmstream = init_vgmstream_fsb5(temp_sf);
} else {
if (fsbkey_list[i].is_fsb5) { vgmstream = init_vgmstream_fsb(temp_sf);
vgmstream = init_vgmstream_fsb5(temp_streamFile); }
} else {
vgmstream = init_vgmstream_fsb(temp_streamFile); if (vgmstream)
} dump_streamfile(temp_sf, 0);
close_streamfile(temp_streamFile); close_streamfile(temp_sf);
if (vgmstream) break; if (vgmstream) break;
} }
} }
if (!vgmstream) if (!vgmstream)
goto fail; goto fail;
return vgmstream; return vgmstream;
fail: fail:
close_vgmstream(vgmstream); close_vgmstream(vgmstream);
return NULL; 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;
}

View File

@ -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_ */

View File

@ -137,7 +137,6 @@ VGMSTREAM * init_vgmstream_hca_subkey(STREAMFILE *streamFile, uint16_t subkey);
#ifdef VGM_USE_FFMPEG #ifdef VGM_USE_FFMPEG
VGMSTREAM * init_vgmstream_ffmpeg(STREAMFILE *streamFile); 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); VGMSTREAM * init_vgmstream_mp4_aac_ffmpeg(STREAMFILE * streamFile);
#endif #endif

View File

@ -21,12 +21,15 @@ VGMSTREAM* init_vgmstream_mups(STREAMFILE* sf) {
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

@ -3792,6 +3792,19 @@ static int config_sb_version(ubi_sb_header* sb, STREAMFILE* sf) {
return 1; 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 */ /* TMNT (2007)(PSP)-map 0x00190001 */
/* Surf's Up (2007)(PSP)-map 0x00190005 */ /* Surf's Up (2007)(PSP)-map 0x00190005 */
if ((sb->version == 0x00190001 && sb->platform == UBI_PSP) || 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; 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 */ /* Cranium Kabookii (2007)(Wii)-bank */
if (sb->version == 0x001A0003 && sb->platform == UBI_WII) { if (sb->version == 0x001A0003 && sb->platform == UBI_WII) {
config_sb_entry(sb, 0x6c, 0x78); config_sb_entry(sb, 0x6c, 0x78);

View File

@ -61,8 +61,6 @@ void interleave_stereo(sample_t * buffer, int32_t sample_count) {
else else
belongs = (tomove-sample_count)*2+1; belongs = (tomove-sample_count)*2+1;
printf("move %d to %d\n",tomove,belongs);
temp = buffer[belongs]; temp = buffer[belongs];
buffer[belongs] = moving; buffer[belongs] = moving;
moving = temp; moving = temp;
@ -100,6 +98,14 @@ void put_32bitBE(uint8_t * buf, int32_t i) {
buf[3] = (uint8_t)(i & 0xFF); 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) { void swap_samples_le(sample_t *buf, int count) {
/* Windows can't be BE... I think */ /* Windows can't be BE... I think */
#if !defined(_WIN32) #if !defined(_WIN32)

View File

@ -7,6 +7,8 @@
#ifndef _UTIL_H #ifndef _UTIL_H
#define _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 */ /* host endian independent multi-byte integer reading */
static inline int16_t get_16bitBE(uint8_t * p) { static inline int16_t get_16bitBE(uint8_t * p) {
@ -91,13 +93,9 @@ static inline int clamp16(int32_t val) {
else return val; else return val;
} }
static inline int round10(int val) { /* less common functions, no need to inline */
int round_val = val % 10;
if (round_val < 5) /* half-down rounding */ int round10(int val);
return val - round_val;
else
return val + (10 - round_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 */

View File

@ -498,9 +498,7 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = {
init_vgmstream_diva, init_vgmstream_diva,
init_vgmstream_imuse, init_vgmstream_imuse,
init_vgmstream_ktsr, init_vgmstream_ktsr,
#ifdef VGM_USE_VORBIS
init_vgmstream_mups, init_vgmstream_mups,
#endif
init_vgmstream_kat, init_vgmstream_kat,
/* lowest priority metas (should go after all metas, and TXTH should go before raw formats) */ /* lowest priority metas (should go after all metas, and TXTH should go before raw formats) */