mirror of
https://github.com/vgmstream/vgmstream.git
synced 2024-12-11 06:15:59 +01:00
commit
ddb9eaf8f6
@ -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
|
||||||
|
@ -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) */
|
||||||
|
@ -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 */
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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"
|
||||||
>
|
>
|
||||||
|
@ -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" />
|
||||||
|
@ -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>
|
||||||
|
@ -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
|
||||||
|
@ -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;
|
||||||
|
@ -1,51 +1,48 @@
|
|||||||
#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 */
|
/* fully encrypted FSBs */
|
||||||
VGMSTREAM * init_vgmstream_fsb_encrypted(STREAMFILE * streamFile) {
|
VGMSTREAM* init_vgmstream_fsb_encrypted(STREAMFILE* sf) {
|
||||||
VGMSTREAM * vgmstream = NULL;
|
VGMSTREAM* vgmstream = NULL;
|
||||||
|
|
||||||
/* checks */
|
/* checks */
|
||||||
/* .fsb: standard
|
/* .fsb: standard
|
||||||
* .fsb.xen: various Guitar Hero (X360) */
|
* .fsb.xen: various Guitar Hero (X360/PC) */
|
||||||
if ( !check_extensions(streamFile, "fsb,xen") )
|
if (!check_extensions(sf, "fsb,xen"))
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
||||||
/* ignore non-encrypted FSB */
|
/* ignore non-encrypted FSB */
|
||||||
if ((read_32bitBE(0x00,streamFile) & 0xFFFFFF00) == 0x46534200) /* "FSB\0" */
|
if ((read_u32be(0x00,sf) & 0xFFFFFF00) == 0x46534200) /* "FSB\0" */
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
||||||
|
|
||||||
/* try fsbkey + all combinations of FSB4/5 and decryption algorithms */
|
/* try fsbkey + all combinations of FSB4/5 and decryption algorithms */
|
||||||
{
|
{
|
||||||
STREAMFILE *temp_streamFile = NULL;
|
STREAMFILE* temp_sf = NULL;
|
||||||
uint8_t key[FSB_KEY_MAX];
|
uint8_t key[FSB_KEY_MAX];
|
||||||
size_t key_size = read_key_file(key, FSB_KEY_MAX, streamFile);
|
size_t key_size = read_key_file(key, FSB_KEY_MAX, sf);
|
||||||
|
|
||||||
if (key_size) {
|
if (key_size) {
|
||||||
{
|
{
|
||||||
temp_streamFile = setup_fsb_streamfile(streamFile, key,key_size, 0);
|
temp_sf = setup_fsb_streamfile(sf, key,key_size, 0);
|
||||||
if (!temp_streamFile) goto fail;
|
if (!temp_sf) goto fail;
|
||||||
|
|
||||||
if (!vgmstream) vgmstream = init_vgmstream_fsb(temp_streamFile);
|
if (!vgmstream) vgmstream = init_vgmstream_fsb(temp_sf);
|
||||||
if (!vgmstream) vgmstream = init_vgmstream_fsb5(temp_streamFile);
|
if (!vgmstream) vgmstream = init_vgmstream_fsb5(temp_sf);
|
||||||
|
|
||||||
close_streamfile(temp_streamFile);
|
close_streamfile(temp_sf);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!vgmstream) {
|
if (!vgmstream) {
|
||||||
temp_streamFile = setup_fsb_streamfile(streamFile, key,key_size, 1);
|
temp_sf = setup_fsb_streamfile(sf, key,key_size, 1);
|
||||||
if (!temp_streamFile) goto fail;
|
if (!temp_sf) goto fail;
|
||||||
|
|
||||||
if (!vgmstream) vgmstream = init_vgmstream_fsb(temp_streamFile);
|
if (!vgmstream) vgmstream = init_vgmstream_fsb(temp_sf);
|
||||||
if (!vgmstream) vgmstream = init_vgmstream_fsb5(temp_streamFile);
|
if (!vgmstream) vgmstream = init_vgmstream_fsb5(temp_sf);
|
||||||
|
|
||||||
close_streamfile(temp_streamFile);
|
close_streamfile(temp_sf);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -54,22 +51,25 @@ VGMSTREAM * init_vgmstream_fsb_encrypted(STREAMFILE * streamFile) {
|
|||||||
/* try all keys until one works */
|
/* try all keys until one works */
|
||||||
if (!vgmstream) {
|
if (!vgmstream) {
|
||||||
int i;
|
int i;
|
||||||
STREAMFILE *temp_streamFile = NULL;
|
STREAMFILE* temp_sf = NULL;
|
||||||
|
|
||||||
for (i = 0; i < fsbkey_list_count; i++) {
|
for (i = 0; i < fsbkey_list_count; i++) {
|
||||||
fsbkey_info entry = fsbkey_list[i];
|
fsbkey_info entry = fsbkey_list[i];
|
||||||
//;VGM_LOG("fsbkey: size=%i, is_fsb5=%i, is_alt=%i\n", entry.fsbkey_size,entry.is_fsb5, entry.is_alt);
|
//;VGM_LOG("fsbkey: size=%i, is_fsb5=%i, is_alt=%i\n", entry.fsbkey_size,entry.is_fsb5, entry.is_alt);
|
||||||
|
|
||||||
temp_streamFile = setup_fsb_streamfile(streamFile, entry.fsbkey, entry.fsbkey_size, entry.is_alt);
|
temp_sf = setup_fsb_streamfile(sf, entry.fsbkey, entry.fsbkey_size, entry.is_alt);
|
||||||
if (!temp_streamFile) goto fail;
|
if (!temp_sf) goto fail;
|
||||||
|
|
||||||
if (fsbkey_list[i].is_fsb5) {
|
if (fsbkey_list[i].is_fsb5) {
|
||||||
vgmstream = init_vgmstream_fsb5(temp_streamFile);
|
vgmstream = init_vgmstream_fsb5(temp_sf);
|
||||||
} else {
|
} else {
|
||||||
vgmstream = init_vgmstream_fsb(temp_streamFile);
|
vgmstream = init_vgmstream_fsb(temp_sf);
|
||||||
}
|
}
|
||||||
|
|
||||||
close_streamfile(temp_streamFile);
|
if (vgmstream)
|
||||||
|
dump_streamfile(temp_sf, 0);
|
||||||
|
|
||||||
|
close_streamfile(temp_sf);
|
||||||
if (vgmstream) break;
|
if (vgmstream) break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -83,82 +83,3 @@ 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;
|
|
||||||
}
|
|
||||||
|
73
src/meta/fsb_encrypted_streamfile.h
Normal file
73
src/meta/fsb_encrypted_streamfile.h
Normal 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_ */
|
@ -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
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
10
src/util.c
10
src/util.c
@ -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)
|
||||||
|
12
src/util.h
12
src/util.h
@ -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 */
|
||||||
|
@ -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) */
|
||||||
|
Loading…
Reference in New Issue
Block a user