Fix some FSB3 [Rise of the Argonauts (PC)]

This commit is contained in:
bnnm 2025-02-02 12:38:36 +01:00
parent fc6d72a712
commit b436e8b03a
2 changed files with 50 additions and 21 deletions

View File

@ -144,8 +144,13 @@ int mpeg_custom_parse_frame_default(VGMSTREAMCHANNEL* stream, mpeg_codec_data* d
current_interleave_pre = current_interleave*num_stream;
current_interleave_post = current_interleave*(data->streams_size-1) - current_interleave_pre;
if (stream->offset >= data->config.data_size) {
VGM_LOG_ONCE("MPEG: fsb overread\n");
return false;
}
if (!mpeg_get_frame_info(stream->streamfile, stream->offset + current_interleave_pre, &info))
goto fail;
return false;
current_data_size = info.frame_size;
/* get FSB padding for Layer III or multichannel Layer II (Layer I isn't supported by FMOD).
@ -207,7 +212,7 @@ int mpeg_custom_parse_frame_default(VGMSTREAMCHANNEL* stream, mpeg_codec_data* d
else if (ms->current_size_count == data->config.max_chunks)
current_interleave = data->config.interleave_last;
else
goto fail;
return false;
current_interleave_pre = current_interleave*num_stream;
current_interleave_post = current_interleave*(data->streams_size-1) - current_interleave_pre;
@ -218,14 +223,14 @@ int mpeg_custom_parse_frame_default(VGMSTREAMCHANNEL* stream, mpeg_codec_data* d
break;
default: /* standard frames (CBR or VBR) */
if ( !mpeg_get_frame_info(stream->streamfile, stream->offset, &info) )
goto fail;
if (!mpeg_get_frame_info(stream->streamfile, stream->offset, &info))
return false;
current_data_size = info.frame_size;
break;
}
if (!current_data_size || current_data_size > ms->buffer_size) {
VGM_LOG("MPEG: incorrect data_size 0x%x vs buffer 0x%x\n", current_data_size, ms->buffer_size);
goto fail;
return false;
}
/* This assumes all streams' offsets start in the first stream, and advances
@ -248,9 +253,7 @@ int mpeg_custom_parse_frame_default(VGMSTREAMCHANNEL* stream, mpeg_codec_data* d
}
return 1;
fail:
return 0;
return true;
}
#endif
@ -359,7 +362,6 @@ uint32_t mpeg_get_tag_size(STREAMFILE* sf, uint32_t offset, uint32_t header) {
frame_size += 0x0a;
return frame_size;
}
/* skip ID3v1 */

View File

@ -4,7 +4,7 @@
#include "fsb_interleave_streamfile.h"
typedef enum { MPEG, XBOX_IMA, FSB_IMA, PSX, XMA1, XMA2, DSP, CELT, PCM8, PCM8U, PCM16LE, PCM16BE } fsb_codec_t;
typedef enum { MPEG, XBOX_IMA, FSB_IMA, PSX, XMA1, XMA2, DSP, CELT, PCM8, PCM8U, PCM16LE, PCM16BE, SILENCE } fsb_codec_t;
typedef struct {
/* main header */
uint32_t id;
@ -36,7 +36,7 @@ typedef struct {
bool loop_flag;
off_t stream_offset;
uint32_t stream_offset;
fsb_codec_t codec;
} fsb_header_t;
@ -82,10 +82,13 @@ VGMSTREAM* init_vgmstream_fsb(STREAMFILE* sf) {
read_string(vgmstream->stream_name, fsb.name_size + 1, fsb.name_offset, sf);
switch(fsb.codec) {
#ifdef VGM_USE_MPEG
case MPEG: { /* FSB4: Shatter (PS3), Way of the Samurai 3/4 (PS3) */
case MPEG: { /* FSB3: Spider-man 3 (PC/PS3), Rise of the Argonauts (PC), FSB4: Shatter (PS3), Way of the Samurai 3/4 (PS3) */
mpeg_custom_config cfg = {0};
cfg.fsb_padding = fsb.mpeg_padding; /* frames are sometimes padded for alignment */
cfg.fsb_padding = fsb.mpeg_padding; // frames are sometimes padded for alignment
cfg.data_size = fsb.stream_offset + fsb.stream_size;
vgmstream->codec_data = init_mpeg_custom(sf, fsb.stream_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_FSB, &cfg);
if (!vgmstream->codec_data) goto fail;
@ -188,20 +191,25 @@ VGMSTREAM* init_vgmstream_fsb(STREAMFILE* sf) {
}
#endif
case PCM8: /* no games known */
case PCM8U: /* FSB4: Crash Time 4: The Syndicate (X360) */
case PCM8: /* no known games */
case PCM8U: /* FSB4: Crash Time 4: The Syndicate (X360), Zoombinis (PC) */
vgmstream->coding_type = (fsb.codec == PCM8U) ? coding_PCM8_U : coding_PCM8;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = 0x1;
break;
case PCM16LE: /* FSB4: Rocket Knight (PC), Another Century's Episode R (PS3), Toy Story 3 (Wii) */
case PCM16BE: /* FSB4: SpongeBob's Truth or Square (X360) */
case PCM16LE: /* FSB2: Hot Wheels World Race (PC)-bigfile-sfx, FSB3: Bee Movie (Wii), FSB4: Rocket Knight (PC), Another Century's Episode R (PS3), Toy Story 3 (Wii) */
case PCM16BE: /* FSB4: SpongeBob's Truth or Square (X360), Crash Time 4: The Syndicate (X360) */
vgmstream->coding_type = (fsb.codec == PCM16BE) ? coding_PCM16BE : coding_PCM16LE;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = 0x2;
break;
case SILENCE: /* special case for broken MPEG */
vgmstream->coding_type = coding_SILENCE;
vgmstream->layout_type = layout_none;
break;
default:
goto fail;
}
@ -220,7 +228,7 @@ fail:
static layered_layout_data* build_layered_fsb_celt(STREAMFILE* sf, fsb_header_t* fsb, bool is_new_lib) {
layered_layout_data* data = NULL;
STREAMFILE* temp_sf = NULL;
int i, layers = (fsb->channels+1) / 2;
int layers = (fsb->channels+1) / 2;
/* init layout */
@ -228,7 +236,7 @@ static layered_layout_data* build_layered_fsb_celt(STREAMFILE* sf, fsb_header_t*
if (!data) goto fail;
/* open each layer subfile (1/2ch CELT streams: 2ch+2ch..+1ch or 2ch+2ch..+2ch) */
for (i = 0; i < layers; i++) {
for (int i = 0; i < layers; i++) {
int layer_channels = (i+1 == layers && fsb->channels % 2 == 1)
? 1 : 2; /* last layer can be 1/2ch */
@ -464,7 +472,7 @@ static bool parse_fsb(fsb_header_t* fsb, STREAMFILE* sf) {
if (fsb->id == get_id32be("FSB2")) {
fsb->meta_type = meta_FSB2;
fsb->base_header_size = 0x10;
fsb->sample_header_min = 0x40; /* guessed */
fsb->sample_header_min = 0x40;
}
else if (fsb->id == get_id32be("FSB3")) {
fsb->meta_type = meta_FSB3;
@ -528,7 +536,6 @@ static bool parse_fsb(fsb_header_t* fsb, STREAMFILE* sf) {
/* XMA basic headers have extra data [Forza Motorsport 3 (X360)] */
if (fsb->mode & FSOUND_XMA) {
VGM_LOG("h=%x\n", (uint32_t)header_offset);
// 0x08: flags? (0x00=none?, 0x20=standard)
// 0x0c: sample related? (may be 0 with no seek table)
// 0x10: low number (may be 0 with no seek table)
@ -613,6 +620,26 @@ static bool parse_fsb(fsb_header_t* fsb, STREAMFILE* sf) {
/* XOR encryption for some FSB4, though the flag is only seen after decrypting */
//;VGM_ASSERT(fsb->flags & FMOD_FSB_SOURCE_ENCRYPTED, "FSB ENCRYPTED found\n");
// rare FSB3 have odd cases [Rise of the Argonauts (PC)]
if (fsb->codec == MPEG && fsb->version == FMOD_FSB_VERSION_3_1) {
uint32_t mpeg_id = read_u32be(fsb->stream_offset, sf);
if ((mpeg_id & 0xFFFFFF00) == get_id32be("ID3\0")) {
// starts with ID3, probably legal but otherwise not seen (stripped?): Lykas_Atalanta_Join_DLG.fsb, Support_Of_The_Gods*.fsb
uint32_t tag_size = mpeg_get_tag_size(sf, fsb->stream_offset, mpeg_id); // always 0x1000, has 'PeakLevel' info
fsb->stream_offset += tag_size;
fsb->stream_size -= tag_size;
}
// completely empty MPEG, probably works by chance with OG decoder ignoring bad data: DLG_Lycomedes_Statue_*.fsb
if (mpeg_id == 0) {
fsb->codec = SILENCE;
}
// rarely sets more samples than data, must clamp reads to avoid spilling into next subsong: Player_Death_DLG.fsb, Lykas_Atalanta_Join_DLG.fsb
// probably a bug as samples don't seem to match MPEG's 'Info' headers and can be both bigger and smaller than loop_end
}
return true;
fail:
return false;